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
Hi Sebastien,
I'm not sure what is going on here. When the Tile is maximized we don't touch the content.
It sounds like something to do with your user control. If you can post a stripped down sample that duplicates the problem I will take a look at it.
Otherwise you could submit a support request at http://ko.infragistics.com/support/submitrequest.aspx.
Hi Joe,
When trying to recreate an sample on an existing MVVM demo application, I noticed that the problem only arises when I specify the different ItemTemplates (Maximized, minimized, etc..).
In the following sampnle, Workspaces is a collection of ViewModels (each ViewModel is bound as the datacontext of a WPF UserControl).
So the XamTilesControl is bound to the collection of Workspaces and each item template is bound to a single item from this collection.
That means that when I add a ViewModel to the collection, a tile is created with the UserControl corresponding to the added ViewModel.
When no Template is specified (default), the UserControl displays and can be maximized or minimized correctly without affecting the content, but once I set an ItemTemplate (for example for the maximized state), it reloads the content when the tile goes to that state.
Here is the strippped down sample (I've also attached the modified demo application to reproduce the problem) :
<igTiles:XamTilesControl ItemsSource="{Binding Path=Workspaces, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" TileCloseAction="RemoveItem" ItemContainerGenerationMode="PreLoad" > <igTiles:XamTilesControl.ItemTemplateMinimizedExpanded> <DataTemplate> <Viewbox Stretch="Uniform" Width="{Binding Width}" Height="{Binding Height}" MaxWidth="300" MaxHeight="400" MinHeight="50" MinWidth="50"> <ContentControl Content="{Binding}" MinHeight="400" MinWidth="400" /> </Viewbox> </DataTemplate> </igTiles:XamTilesControl.ItemTemplateMinimizedExpanded> <igTiles:XamTilesControl.ItemTemplateMinimized> <DataTemplate> <Viewbox Stretch="Uniform" Width="{Binding Width}" Height="{Binding Height}" MaxWidth="300" MaxHeight="200" MinHeight="50" MinWidth="50"> <ContentControl Content="{Binding}" MinHeight="400" MinWidth="400" /> </Viewbox> </DataTemplate> </igTiles:XamTilesControl.ItemTemplateMinimized> <igTiles:XamTilesControl.ItemTemplateMaximized> <DataTemplate> <ContentControl Content="{Binding}" Width="{Binding Width}" Height="{Binding Height}" MinWidth="300" MinHeight="300"/> </DataTemplate> </igTiles:XamTilesControl.ItemTemplateMaximized> <igTiles:XamTilesControl.ItemTemplate> <DataTemplate> <ContentControl Content="{Binding}" Width="{Binding Width}" Height="{Binding Height}" MinWidth="300" MinHeight="300" /> </DataTemplate> </igTiles:XamTilesControl.ItemTemplate> </igTiles:XamTilesControl>
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.
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
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, that's a good idea.
I will test it out and post the results later.
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.