Hello,
I've been trying to find a solution to this problem for a while but I can't seem to be able to find one.
My application contains a XamTilesControl, and each tile contains a ViewModel (I'm using MVVM, and the ItemsSource is bound to a collection of ViewModels which are in turn each bound to a Xaml usercontrol).
Some of those ViewModels contain graphs, others contain lists in the form of XamDatagrids.
The problem is that everytime I try to maximize one of the tiles, its content is re-created.
Some of these tiles take a while to load (pretty big lists or graphs), and maximizing causes the tab to reload everything again.
I managed to change the way my Viewmodel loads to avoid most of that, but it also causes the tile to lose all the changes that the user made in the controls contained by the tile (grouping or sorting in the XamDatagrid, or zooming in a graph, etc..).
Is there a way to avoid this?
Thanks,
Sébastien
I finally found a solution to my problems, thanks to your code sample.
I used it as a basis and modified it to have only 1 ContentPresenter and setting a ScaleTransform property to it, bound to a width property with a converter.
It allows me to avoid the Viewbox completely, and thus removes the need to have 2 Content areas.
I just needed to add 2 Converters to handle the MaxWidth and Scale of the Content depending on the resolution.
Unfortunately, the MaxWidth and MaxHeight in the TileConstraints didn't work for some reason (I haven't tried on the demo sample though, so it might be the graphs or the huge datagrids that cause some sort of problem), so I added a setter to change the Border's width.
The triggers that were used to change the Content Area depending on the Tile state now modify the Scale and width of the minimized tiles.
Here is the modified TileControl from my application :
<igTiles:XamTilesControl Name="xamTiles" ItemsSource="{Binding Path=Workspaces, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Margin="10,0,0,0" MaximizedTileLimit="1" Theme="Generic" TileCloseAction="RemoveItem" HeaderPath="DisplayName" ToolTip="{Binding Path=DisplayToolTip, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, RelativeSource={RelativeSource Self}}" MaxWidth="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type igDock:ContentPane}}, Path=ActualWidth}" MaxHeight="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type igDock:ContentPane}}, Path=ActualHeight}" ItemContainerGenerationMode="PreLoad">
<igTiles:XamTilesControl.Resources> <!-- ================================ --> <!-- Tile Style --> <!-- ================================ --> <Style TargetType="{x:Type igTiles:Tile}"> <Setter Property="Background" Value="{DynamicResource {x:Static igTiles:TileBrushKeys.TileContentBackgroundFillKey}}"/> <Setter Property="BorderBrush" Value="{DynamicResource {x:Static igTiles:TileBrushKeys.TileBorderBrushFillKey}}"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type igTiles:Tile}"> <AdornerDecorator> <Border Name="Border" Tag="0.5" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="2,2,1,1"> <igWindows:CardPanel> <DockPanel Margin="{TemplateBinding Padding}"> <!-- Header area--> <igTiles:TileHeaderPresenter x:Name="TileHeader" AutomationProperties.AutomationId="TileHeader" DockPanel.Dock="Top" Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}" ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}" Tile="{Binding RelativeSource={x:Static RelativeSource.TemplatedParent}}" /> <!-- Content area--> <Grid> <ContentPresenter x:Name="ContentArea" MinWidth="300" MinHeight="300" Tag="{Binding ElementName=xamTiles, Path=ActualWidth, Converter={StaticResource SizeConverter}}" MaxWidth="{Binding ElementName=xamTiles, Path=ActualWidth, Converter={StaticResource SizeConverter}}" MaxHeight="{Binding ElementName=xamTiles, Path=ActualHeight, Converter={StaticResource SizeConverter}}"> <ContentPresenter.LayoutTransform> <ScaleTransform ScaleX="{Binding ElementName=ContentArea, Path=Tag, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource ScaleTransformConverter}}" ScaleY="{Binding ElementName=ContentArea, Path=Tag, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource ScaleTransformConverter}}"/> </ContentPresenter.LayoutTransform> </ContentPresenter> </Grid> </DockPanel> <Rectangle x:Name="Overlay"/> </igWindows:CardPanel> </Border> </AdornerDecorator> <ControlTemplate.Triggers> <Trigger Property="State" Value="Minimized"> <Setter TargetName="ContentArea" Property="Tag" Value="{Binding ElementName=ContentArea, Path=ActualWidth, Mode=OneTime, Converter={StaticResource TileMinimizedDimensionsConverter}}"/> <Setter TargetName="Border" Property="MaxWidth" Value="300" /> <Setter TargetName="Border" Property="MaxHeight" Value="300" /> </Trigger> <Trigger Property="State" Value="MinimizedExpanded"> <Setter TargetName="ContentArea" Property="Tag" Value="{Binding ElementName=ContentArea, Path=ActualWidth, Mode=OneTime, Converter={StaticResource TileMinimizedDimensionsConverter}}"/> </Trigger> <Trigger Property="IsDragging" Value="True"> <Setter TargetName="Border" Property="Opacity" Value="0.75"/> </Trigger> <Trigger Property="IsSwapTarget" Value="True"> <Setter TargetName="Overlay" Property="Fill" Value="{DynamicResource {x:Static igTiles:TileBrushKeys.TileSwapTargetBgBrushFillKey}}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </igTiles:XamTilesControl.Resources> <igTiles:XamTilesControl.NormalModeSettings> <igTiles:NormalModeSettings HorizontalTileAreaAlignment="Left" VerticalTileAreaAlignment="Top" AllowTileDragging="Slide" ShowAllTiles="False" ShouldAnimate="True" AllowTileSizing="Individual" TileLayoutOrder="{Binding Source={StaticResource DATA_MainWindowViewModel}, Path=TileLayout}" /> </igTiles:XamTilesControl.NormalModeSettings> <igTiles:XamTilesControl.MaximizedModeSettings> <igTiles:MaximizedModeSettings HorizontalTileAreaAlignment="Stretch" VerticalTileAreaAlignment="Stretch" AllowTileDragging="Slide" ShowTileAreaSplitter="True" ShowAllMinimizedTiles="True" MinimizedTileExpansionMode="AllowOne" ShouldAnimate="True" MaximizedTileLayoutOrder="{Binding Source={StaticResource DATA_MainWindowViewModel}, Path=MaximizedTileLayout}" /> </igTiles:XamTilesControl.MaximizedModeSettings> </igTiles:XamTilesControl>
Thanks again for the help, it would probably have taken me a lot longer to find a suitable solution without your sample.
Thanks, that's a good idea.
I will test it out and post the results later.
Okay, I modified your MainWindow.xaml file.
Instead of using a DataTemplate I created a style for the Tile with a trigger based on State.
This still doesn't solve your issue where modifications made while in maximized mode get reflected in the other modes and vice versa. It should adrees the perfornace hit when switching modes.
Thanks for the fast answer, I'm sorry I couldn't reply earlier, I had to work on something else until a few hours ago.
I've been trying to implement something like you said but with only one binding, to avoid having 2 instances of the ViewModel (some of those are pretty long to load), but it's not working.
Even with 2 instances of the viewmodel like you said, I'm not sure I'm implementing this correctly (I'm also having trouble with how the ViewBox displays the normal itemtemplate (not maximized and not minimized).
Could you please give me a small example of how you would implement it with a ContentPresenter and a ViewBox (if that doesn't take too much time)?
Sébastien De Prins
Now I see what is going on.
Since the template is swapped when the tile's state is changed a new tree of elements will get created based on the default DataTemplate you defined for AllCustomersViewModel. This is normal behavior.
If you want to avoid this what you could try is to provide a single template for the tile (not based on state). This template could contain both of the following:
Note: the min/max width or height settings can be specified for each state via ''...TileConstraint' properties off the NormalModeSettings and MaximizedModeSettings objects instead on being set inside the template. Also you should probably set the ItemContainerGenerationMode to 'LazyLoad' or 'PreLoad' so once a tile's element tree is hydrated the tile won't be recycled if you scroll and attached to a different AllCustomersViewModel.
This will result in up to 2 AllCustomersView elements being created for each tile but once they are both hydrated for a tile, i.e. once the tile is maximized the first time, you won't encounter any extra overhead for that tile again.