WPF expander validation error not shown when expanded - wpf

Using MVVM. I have a DataTemplate which I am using to display an expander with some controls in per object.
<DataTemplate>
<Expander ExpandDirection="Down" IsExpanded="False">
<Expander.Header>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="Platform Group {0} {1}">
<Binding Path="PlatformGroupCode"/>
<Binding Path="PlatformGroupName"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Expander.Header>
<vw:PlatformGroup HorizontalAlignment="Left"/>
</Expander>
</DataTemplate>
Inside that view is 2 textboxes bound to those 2 properties. I'm using IDataErrorInfo in my VM to do validation and I've a style in my main application resources to display error messages as tooltips:
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self},Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
When a new group is added the 2 properties have default values, which is invalid so I want the textboxes to be red to prompt the user to enter data. This works if the Expander's IsExpanded is set to true. But if it is false I have to expand AND change the value in one of the textboxes in order to get the red border and tooltip to show.
I don't want to set the expander to be expanded because there will eventually be quite a few controls. How can I get the red border to show as soon as the expander is expanded? Even better, is there a way to make the newly added expander expand (when user adds a new group I'm adding a PlatformGroupviewModel to an observablecollection of PlatformGroupviewModels)?
EDIT more detail:
the top level view:
<StackPanel Orientation="Vertical">
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="630">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="5*" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical" Grid.ColumnSpan="2" Grid.Row="1" HorizontalAlignment="Stretch">
<Expander ExpandDirection="Down" IsExpanded="True" Header="Header" HorizontalAlignment="Stretch" Name="expHeader" VerticalAlignment="Top">
<vw:Header DataContext="{Binding HeaderVM}"/>
</Expander>
<Expander ExpandDirection="Down" IsExpanded="True" Header="Platform Groups" HorizontalAlignment="Stretch" Name="expPlatformGroups" VerticalAlignment="Top">
<AdornerDecorator>
<vw:PlatformGroups DataContext="{Binding PlatformGroupsVM}"/>
</AdornerDecorator>
</Expander>
</StackPanel>
</Grid>
</ScrollViewer>
</StackPanel>
the PlatformGroups view:
<StackPanel Orientation="Vertical" HorizontalAlignment="Stretch" Margin="10,10,10,10">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch" Margin="0,10">
<Label Content="Number of platform groups" VerticalAlignment="Center"/>
<vw:IntegerInput MinValue="0" MaxValue="50" MaxLength="2" Text="{Binding Path=NumPlatformGroups, Mode=TwoWay,ValidatesOnDataErrors=True}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
</StackPanel>
<ItemsControl IsTabStop="False" ItemsSource="{Binding PlatformGroups}" Margin="20,10" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander ExpandDirection="Down" IsExpanded="False">
<Expander.Header>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="Platform Group {0} {1}">
<Binding Path="PlatformGroupCode"/>
<Binding Path="PlatformGroupName"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Expander.Header>
<AdornerDecorator>
<vw:PlatformGroup HorizontalAlignment="Left"/>
</AdornerDecorator>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate>
<Border BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="{TemplateBinding Control.Padding}"
BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" SnapsToDevicePixels="True">
<ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="400" CanContentScroll="True" Padding="{TemplateBinding Control.Padding}" Focusable="False">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</StackPanel>

As per this post, wrapping your Expander content inside an AdornerDecorator should solve this problem -
<DataTemplate>
<Expander ExpandDirection="Down" IsExpanded="False">
<Expander.Header>
...
</Expander.Header>
<AdornerDecorator>
<vw:PlatformGroup HorizontalAlignment="Left"/>
</AdornerDecorator>
</Expander>
</DataTemplate>
Another SO thread which confirms this - Issue with WPF validation(IDataErrorInfo) and tab focusing
Some other WorkArounds are also mentioned on this connect bug -
TabControl doesn't display Validation error information correctly when switching tabs back and forth

Related

Border extending too far

I got this xaml code:
<UserControl x:Class="CharacterMap.CharacterMapControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CharacterMap"
xmlns:cxml="clr-namespace:CharacterMap.Xml">
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="Flat">
<Setter Property="Control.Background" Value="White" />
<Setter Property="Control.FontWeight" Value="DemiBold" />
<Setter Property="Control.FontFamily" Value="Arial" />
</Style>
<DataTemplate x:Key="ItemsTemplate">
<Border BorderThickness="0,0,1,1" BorderBrush="Black">
<Button Width="26" Height="23" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Content="{Binding TheChar}"
Style="{StaticResource Flat}" BorderThickness="0"
ToolTipService.InitialShowDelay="800" ToolTipService.ShowDuration="10000" ToolTipService.BetweenShowDelay="300"
ToolTipService.ShowOnDisabled="True" ToolTip="{Binding AggregatedTooltip}" ToolTipService.IsEnabled="True" />
</Border>
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Border BorderBrush="{x:Null}" Height="25" Margin="10,0,20,0">
<TextBlock Text="Filtern nach Buchstabe: " TextWrapping="Wrap" VerticalAlignment="Center"/>
</Border>
<TextBox Text="{Binding FilterString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="125" Height="25" VerticalContentAlignment="Center" />
</StackPanel>
<ScrollViewer x:Name="ScrollViewer" Margin="10,0,10,10" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Row="1" >
<Border BorderBrush="Black" BorderThickness="1,1,0,0" SnapsToDevicePixels="True" >
<ItemsControl ItemsSource="{Binding CharDescriptions}" ItemTemplate="{StaticResource ItemsTemplate}" Name="CharItemsControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Border>
</ScrollViewer>
</Grid>
</UserControl>
It looks something like this:
Before i had the BorderThickness from the button just on 1 but then the borders were overlaying and it looked not so nice.
Now i tried to set the buttons borders thickness to "0,0,1,1" and put a border around the itemscontrol which is "1,1,0,0". The problem is that when i resize the window or in generall the border from the itemscontrol just extends too far and that doesn't look nice either.
How to achieve something like this: ?
You can do something like below. Essentially, you use negative margins to meld all the borders together. On a parent border set a Padding="1" so that the negative margins along the edges can be seen.
The Border around your Button in your DataTemplate would look like this:
<DataTemplate x:Key="ItemsTemplate">
<Border
Margin="-1,-1,0,0"
BorderBrush="Black"
BorderThickness="1">
<Button
Width="26"
Height="23"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
BorderThickness="0"
Content="{Binding TheChar}"
Style="{StaticResource Flat}"
ToolTip="{Binding AggregatedTooltip}"
ToolTipService.BetweenShowDelay="300"
ToolTipService.InitialShowDelay="800"
ToolTipService.IsEnabled="True"
ToolTipService.ShowDuration="10000"
ToolTipService.ShowOnDisabled="True" />
</Border>
</DataTemplate>
The Border around your ItemsControl in your ScrollViewer would have a Padding="1" and look like this:
<ScrollViewer x:Name="ScrollViewer"
Grid.Row="1"
Margin="10,0,10,10"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Border
Padding="1"
SnapsToDevicePixels="True">
<ItemsControl
Name="CharItemsControl"
ItemTemplate="{StaticResource ItemsTemplate}"
ItemsSource="{Binding CharDescriptions}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Border>
</ScrollViewer>

VirtualizingPanel not Virtualizing using WPF XAML [duplicate]

This question already has answers here:
Virtualizing WPF Wrap Panel Issue
(3 answers)
Is there a virtualizing WrapPanel for WPF available? Commercial or free ones
(2 answers)
Virtualizing WrapPanel as ListView's ItemsTemplate
(3 answers)
Closed 4 years ago.
So no matter how hard I try I can't get virtualization in my view to work.
When I scroll through my Listbox, the items always keep populating from the top and carry on right to the bottom, rather than rendering what is on screen and just a bit above and bellow.
Any help would be very much appreciated.
Bellow is the code for the Viewbox. A point of note:
I am using a Viewbox inside a ListBox and a grid inside the ViewBox. This is because my items populate in a similar way to text with word warping turned on - it populates horizontally until the width, then moves on to the next line.
My items in the ListBox are scalable, and there position in the view changes dynamically depending on the scale size, whilst also, the TextBlocks text dose not scale.
Not sure if that effects virtualization in a way I am unaware.
If that is not clear, I apologise and am happy to answer any questions. Thanks for your time:
<ListBox x:Name="LibraryBox"
Grid.Column="1"
Grid.Row="0"
Grid.RowSpan="4"
Margin="0,-5,0,-10"
BorderBrush="Transparent"
Background="#1e1e1e"
ItemsSource="{Binding}"
ItemContainerStyle="{StaticResource ListBoxItemStyle}"
IsSynchronizedWithCurrentItem="True"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True"
VirtualizingPanel.IsContainerVirtualizable="True"
VirtualizingPanel.VirtualizationMode="Standard"
ScrollViewer.IsDeferredScrollingEnabled="False"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Visible"
SelectionMode="Extended"
SelectionChanged="LibraryBox_SelectionChanged"
MouseDoubleClick="LibraryBox_MouseDoubleClick">
<ListBox.Resources>
<!--#region scrollbar style-->
<Style TargetType="{x:Type ScrollBar}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="#990000"/>
<Setter Property="Width" Value="25"/>
</Style>
<!--#endregion-->
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Red"/>
</ListBox.Resources>
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit" Click="ContextMenuItemEdit_Click"/>
<MenuItem Header="ComicVine Scraper" Click="MenuItemScraper_Click"/>
<Separator/>
<MenuItem Header="Delete" Click="ContextMenuItemDelete_Click"/>
</ContextMenu>
</ListBox.ContextMenu>
<!--#region Group Style-->
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"
FontWeight="Bold"
Foreground="#dbdbdb"
FontSize="16"
FontFamily="Cordia New"
VerticalAlignment="Bottom" />
<TextBlock Text="{Binding ItemCount}"
FontSize="16"
Foreground="#dbdbdb"
FontStyle="Italic"
Margin="10,0,0,0"
FontFamily="Cordia New"
VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
<!--#endregion-->
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Viewbox Height="100" Width="70" Margin="2">
<Viewbox.LayoutTransform>
<ScaleTransform ScaleX="{Binding Value, ElementName=ZoomSlider}"
ScaleY="{Binding Value, ElementName=ZoomSlider}"/>
</Viewbox.LayoutTransform>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300"/>
<ColumnDefinition Width="32"/>
</Grid.ColumnDefinitions>
<!--This is for thumbnails-->
<Image x:Name="coverImage"
Grid.Column="0"
Grid.Row="0"
Source="{Binding CoverPath, Converter={StaticResource UriToBitmapConverter}, IsAsync=True}"/>
<Rectangle x:Name="ReadMarkerBottom"
Grid.Column="1"
Grid.Row="1"
Margin="-16,0,0,0"
Fill="#fff"
Width="32"
Height="32"
Loaded="CoverImage_Loaded"/>
<Rectangle x:Name="ReadMarkerTop"
Grid.Column="1"
Grid.Row="1"
Margin="-16,0,0,0"
Fill="#000"
Width="30"
Height="30"
Loaded="CoverImage_Loaded"/>
</Grid>
</Viewbox>
<TextBlock TextTrimming="CharacterEllipsis"
TextAlignment="Center"
Width="120"
Foreground="#dbdbdb"
Background="Transparent"
Margin="0,0,0,5"
Loaded="Text_Loaded"
FontFamily="Cordia New"
FontWeight="Bold">
</TextBlock>
<TextBlock TextTrimming="CharacterEllipsis"
TextAlignment="Center"
Width="120"
Foreground="#dbdbdb"
Background="Transparent"
Margin="0,0,0,5"
Loaded="IssueNumer_Loaded"
FontFamily="Cordia New"/>
<TextBlock TextTrimming="CharacterEllipsis"
TextAlignment="Center"
Width="120"
Foreground="#dbdbdb"
Background="Transparent"
Margin="0,0,0,5"
Loaded="CountStack_Loaded"
FontFamily="Cordia New"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
The answer is, as Clemens pointed out, "The ItemsPanel must support virtualization. WrapPanel does not."

Rendering templates preformances

I created a propertyGrid like.
I used an Item control which uses a viewmodel object.
The VM object has the property to be display and its relevant DataTemplates.
Everything wosrk fine. My Property grid actually act like the VS property grid
My problem is that every time the use changes the selected object (which lead to changes in the property grid) its took to much time (depends on the amount of the selected object properties). I figured out that the reason for the bad performances is the templates loading and rendering.
so I thought that I may solve this issues by creating real controls for each object, and not just uses templates. (Create buttons and text box for each property) this will solve the loading time, I hope..
1- is there any way to creates real controls (in code) using data templates?
2- is there any other way to improve the performances of my Property grid?
The main code for the propGrid is attached to this post
Thanks, Leon
CODE:
<DataTemplate x:Key="gridItemsControl" >
<Grid Visibility="{Binding Visibility}">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="60" MaxWidth="350" Width="{Binding Source={StaticResource firstCulWidth},Path=Width,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,Converter={StaticResource widthToGridLenConverter}}"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderBrush="#ff333333" BorderThickness="0.5" Grid.ColumnSpan="2"
Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter},ConverterParameter=Category}">
<StackPanel Orientation="Horizontal" Background="#FFA9BFD4" Height="25">
<Expander Template="{StaticResource SimpleExpanderTemp}" ExpandDirection="Left" IsExpanded="{Binding IsExpanded, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Margin="3,0,5,0"/>
<Label Content="{Binding Path=CategoryName,Converter={StaticResource propertyNameToDiplayNameConverter}}" FontSize="12" Foreground="White" VerticalAlignment="Top" />
</StackPanel>
</Border>
<Border Grid.Column="0" BorderBrush="#ff333333" BorderThickness="0.5"
Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter}}">
<Label Margin="5,0,0,0" Content="{Binding Path=DisplayName,Converter={StaticResource propertyNameToDiplayNameConverter}}"/>
</Border>
<GridSplitter Grid.Row="0" Grid.Column="0" Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter}}" HorizontalAlignment="Right" Width="4" Background="Transparent"/>
<Border Grid.Column="1" BorderBrush="#ff333333" BorderThickness="0.5"
Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter}}">
<ContentPresenter Grid.Column="1" Margin="10,1,10,1" HorizontalAlignment="Left"
ContentTemplate="{Binding Path=InlineTemplate}" />
</Border>
</Grid>
</DataTemplate>
Update: Here is the container
<ItemsControl Name="propsDataGrid" ItemsSource="{Binding Properties}" ItemTemplate ="{StaticResource gridItemsControl}">
<ItemsControl.ContextMenu>
<ContextMenu>
<MenuItem Header="Close all expanders" Click="MenuItem_Click" Visibility="{Binding IsAlphaBeticSort,Converter={StaticResource boolToVisConverter},ConverterParameter=VisForFalse}"></MenuItem>
<MenuItem Header="Expand all" Name="mnExpandeAll" Click="mnExpandeAll_Click" Visibility="{Binding IsAlphaBeticSort,Converter={StaticResource boolToVisConverter},ConverterParameter=VisForFalse}"></MenuItem>
</ContextMenu>
</ItemsControl.ContextMenu>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!--<ItemsControl.ItemTemplate>
<DataTemplate >
<Grid Visibility="{Binding Visibility}">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="60" MaxWidth="350" Width="{Binding Source={StaticResource firstCulWidth},Path=Width,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,Converter={StaticResource widthToGridLenConverter}}"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderBrush="#ff333333" BorderThickness="0.5" Grid.ColumnSpan="2"
Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter},ConverterParameter=Category}">
<StackPanel Orientation="Horizontal" Background="#FFA9BFD4" Height="25">
<Expander Template="{StaticResource SimpleExpanderTemp}" ExpandDirection="Left" IsExpanded="{Binding IsExpanded, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Margin="3,0,5,0"/>
<Label Content="{Binding Path=CategoryName,Converter={StaticResource propertyNameToDiplayNameConverter}}" FontSize="12" Foreground="White" VerticalAlignment="Top" />
</StackPanel>
</Border>
<Border Grid.Column="0" BorderBrush="#ff333333" BorderThickness="0.5"
Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter}}">
<Label Margin="5,0,0,0" Content="{Binding Path=DisplayName,Converter={StaticResource propertyNameToDiplayNameConverter}}"/>
</Border>
<GridSplitter Grid.Row="0" Grid.Column="0" Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter}}" HorizontalAlignment="Right" Width="4" Background="Transparent"/>
<Border Grid.Column="1" BorderBrush="#ff333333" BorderThickness="0.5"
Visibility ="{Binding IsCategoryItem,Converter={StaticResource isCategoryItemToVisibilityConverter}}">
<ContentPresenter Grid.Column="1" Margin="10,1,10,1" HorizontalAlignment="Left"
ContentTemplate="{Binding Path=InlineTemplate}" />
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>-->
</ItemsControl>
DataTemplates create "real controls" there is no gain in doing something manually which is done automatically using templates, the only thing you should worry about is virtualization (so that the controls for properties not in view are not created) and how your templates are wired up so that their creation is not triggered needlessly.
(By the way, the way you create the grid, setting its width, looks like it might be redundant, you probably can just share the size.)
To make an ItemsControl virtualize the items you need to:
Set the ItemsPanel to a VirtualizingStackPanel
Have a control template which contains a ScrollViewer around the ItemsPresenter where the VirtualizingStackPanel will be.
Set ScrollViewer.CanContentScroll to true to enable scrolling by item which allows for virtualization.
e.g.
<ItemsControl ItemsSource="{Binding Items}"
ScrollViewer.CanContentScroll="True">
<ItemsControl.Template>
<ControlTemplate>
<Border Name="Bd" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true"
Padding="1">
<ScrollViewer Padding="{TemplateBinding Padding}" Focusable="false">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- ... -->
</ItemsControl>
If this is still too slow you could make the whole ItemsControl part of a template which applies per type as presumably the contents of this grid are dependent on the type of the item, thus a different grid should only be created if the item-type changes, also you might be able to cache the resulting templates as well (i never tried doing that though).

Using HierarcicalDataTemplates in conjunction with TreeViewItem control templates

I am having some difficulty figuring out how to template the following TreeView item layout:
I have several items, SearchList, which contains a collection of Search, which contains a collection of DataSet (sort of, but that is beside the point). What I am having difficulty with is styling each node level the way I want. I am using MVVM, and the TreeViews ItemsSource property is set to an ObservableCollection of SearchListViewModels which in turn contain my objects all the way down the object tree.
I can successfully style the SearchList HierarchicalDataTemplate to display them correctly. Where I get hung up is on SearchTerm nodes styling. I want the DataSets to be represented in a wrap panel or uniform grid (I haven't decided yet) to the right of the SearchTerm content area. I have modified a TreeViewItem control template to behave this way I think), however if I set it in the ItemContainerStyle property of the Search HierarchicalDataTemplate, it does nothing. All that gets displayed is the content for the Search.
My Altered TreeViewItem Template
<Style TargetType="{x:Type TreeViewItem}" x:Key="AlteredTreeViewItem">
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"
MinWidth="19" />
<ColumnDefinition Width="0.414*" />
<ColumnDefinition Width="0.586*"/>
</Grid.ColumnDefinitions>
<Border x:Name="Bd" HorizontalAlignment="Stretch"
Grid.Column="1" Grid.ColumnSpan="1" Background="#7F058956">
<ContentPresenter x:Name="PART_Header" Margin="10,0" />
</Border>
<WrapPanel x:Name="ItemsHost"
Grid.Column="2" IsItemsHost="True"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My Search Hierarchical Data Template
<HierarchicalDataTemplate DataType="{x:Type local:SearchViewModel}" ItemsSource="{Binding MySearch.Custodians}" ItemContainerStyle="{StaticResource AlteredTreeViewItem}">
<TextBlock Text="{Binding MySearch.SearchName}" Foreground="Black" FontFamily="Arial" FontSize="16"/>
</HierarchicalDataTemplate>
Surely it is possible to both style differently and have child items laid out differently? How can this be achieved?
It seems that you are pretty close to what you're after. I tried to recreate your scenario based on the code you posted and I noted some problems with it (which of course are based on my interpretation of the code you posted)
You are missing the ContentSource="Header" part of the ContentPresenter
I think you are applying the ItemContainerStyle at the wrong HierarchicalDataTemplate level. It should be specified on the parent in order to affect the children (in your case SearchListViewModel).
The default Template for TreeViewItem lays out the ContentPresenter in an Auto sized ColumnDefinition so the WrapPanel won't succesfully wrap unless you modify the ItemContainerStyle for the parent as well. I changed it to a UniformGrid in my sample below
With the changes from above and a few other things I got a result that looks like this which hopefully is pretty close to what you're after
I uploaded the sample solution here: https://www.dropbox.com/s/4v2t8imikkagueb/TreeViewAltered.zip?dl=0
And here is the Xaml code for it (too much code to post it all..)
<Window.Resources>
<!-- DataSet-->
<HierarchicalDataTemplate DataType="{x:Type data:DataSet}">
<Border BorderThickness="3"
BorderBrush="Gray"
Background="Green">
<TextBlock Text="{Binding Path=Tables[0].TableName}"
Margin="5"/>
</Border>
</HierarchicalDataTemplate>
<!-- SearchViewModel -->
<HierarchicalDataTemplate DataType="{x:Type viewModel:SearchViewModel}"
ItemsSource="{Binding DataSets}">
<TextBlock Text="{Binding DisplayName}"
Foreground="Black"
FontFamily="Arial"
FontSize="16"/>
</HierarchicalDataTemplate>
<!-- SearchListViewModel -->
<HierarchicalDataTemplate DataType="{x:Type viewModel:SearchListViewModel}"
ItemsSource="{Binding SearchList}">
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="19" />
<ColumnDefinition Width="0.414*" />
<ColumnDefinition Width="0.586*"/>
</Grid.ColumnDefinitions>
<Border x:Name="Bd"
HorizontalAlignment="Stretch"
Grid.Column="1"
Grid.ColumnSpan="1"
Background="#7F058956">
<ContentPresenter x:Name="PART_Header"
ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</Border>
<UniformGrid x:Name="ItemsHost"
Grid.Column="2"
Columns="3"
IsItemsHost="True"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<TextBlock Text="{Binding DisplayName}"
FontSize="20"/>
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<TreeView ItemsSource="{Binding SearchListViewModels}" />
</Grid>
Something I learnt a long time ago when trying to create a similar interface was that you are better using a ListBox than a TreeView.
Why?
If you only have one level of expansion (as it appears from your sample) you will a lot more control of the layout as you have a single DataTemplate to style.
It is lot easier to customize a ListBox than a TreeView as you do not have be concerned with the GridViewColumnHeader and GridViewColumnPresenters etc.
To get the expansion part (which is why you initially selected a TreeView), simply use a Grid with two rows defined and an Expander in the second row bound to the IsChecked property of a ToggleButton. See the example that I pulled from my Log Viewer.
<DataTemplate>
<Grid Margin="0,0,0,3" Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" SharedSizeGroup="SSG_TimeIcon"/>
<ColumnDefinition Width="120" SharedSizeGroup="SSG_Time"/>
<ColumnDefinition Width="30" SharedSizeGroup="SSG_LevelIcon"/>
<ColumnDefinition Width="70" SharedSizeGroup="SSG_Level"/>
<ColumnDefinition Width="*" SharedSizeGroup="SSG_Message"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- ProgramTime -->
<Rectangle Grid.Column="0" Grid.Row="0" Margin="0,0,0,0" Width="16" Height="16" VerticalAlignme="Top" HorizoalAlignme="Stretch" Fill="{StaticResource Icon_Timer}"/>
<TextBlock Grid.Column="1" Grid.Row="0" Margin="5,0,0,0" VerticalAlignme="Top" HorizoalAlignme="Stretch" Text="{Binding Path=TimeStamp, Converter={StaticResource ObjectToStringConverter}}" ToolTip="{Binding Path=ProgramTime}"/>
<!-- Level -->
<Rectangle Grid.Column="2" Grid.Row="0" Margin="10,0,0,0" Width="16" Height="16" VerticalAlignme="Top" HorizoalAlignme="Stretch" Fill="{Binding Path=Level, Converter={StaticResource MappingConverterNinjaLogLevelEnumToBrushResource}}"/>
<TextBlock Grid.Column="3" Grid.Row="0" Margin="5,0,0,0" Text="{Binding Path=LevelFriendlyName}" VerticalAlignme="Top" HorizoalAlignme="Stretch"/>
<!-- Message -->
<StackPanel Grid.Column="4" Grid.Row="0" Margin="10,0,0,0" Orieation="Horizoal" >
<TextBlock Margin="0,0,0,0" Text="{Binding Path=LogMessage}" TextWrapping="Wrap" VerticalAlignme="Top" HorizoalAlignme="Stretch"/>
<ToggleButton x:Name="ExpandExceptiooggleButton" VerticalAlignme="Top" Margin="5,0,0,0" IsChecked="False"
Coe="Show Details" Tag="Hide Details" Style="{StaticResource TextButtonStyle}"
Foreground="{StaticResource BlueBrush}" Background="{StaticResource RedBrush}"
Visibility="{Binding Path=HasException, Converter={StaticResource BoolToVisibilityConverter}}" />
</StackPanel>
<Expander IsExpanded="{Binding Path=IsChecked, ElemeName=ExpandExceptiooggleButton}" Style="{StaticResource CoeExpanderStyle}"
Margin="10,0,0,0" Grid.Column="4" Grid.Row="1">
<Border BorderBrush="{StaticResource DarkGreyBrush}" BorderThickness="1,0,0,0">
<TextBlock Text="{Binding Path=Exception}" Margin="5,0,0,0"/>
</Border>
</Expander>
</Grid>
</DataTemplate>
Can you see how much easier it is to define a header and expandable body. If you do have a need for nested data, add a Level property your view model (you are using MVVM aren't you?!) and then create a IValueConverter that returns a Margin (i.e. Thickness) to fake the indent.

Refactor DataTemplate (XAML) to reduce duplication

I have the following datatemplates:
First One:
<DataTemplate DataType="{x:Type WIAssistant:DestinationField}">
<ContentControl Margin="5" MinWidth="60" MinHeight="70" >
<Border BorderThickness="2" BorderBrush="Black" CornerRadius="5">
<!--This text block seems un-needed. But it allows the whole control to be dragged. Without it only the border and the
text can be used to drag the control.-->
<TextBlock>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock FontWeight="Bold" TextWrapping="Wrap" HorizontalAlignment="Center" Margin="10"
Text="{Binding DestField.Name}"/>
<TextBlock Grid.Row="1" TextWrapping="Wrap" HorizontalAlignment="Center" Margin="10"
Text="{Binding DestField.FieldType}"/>
</Grid>
</TextBlock>
</Border>
</ContentControl>
</DataTemplate>
Second One:
<DataTemplate DataType="{x:Type WIAssistant:SourceField}">
<ContentControl Margin="5" MinWidth="60" MinHeight="70" >
<Border BorderThickness="2" BorderBrush="Black" CornerRadius="5">
<!--This text block seems un-needed. But it allows the whole control to be dragged. Without it only the border and the
text can be used to drag the control.-->
<TextBlock>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock FontWeight="Bold" TextWrapping="Wrap" HorizontalAlignment="Center" Margin="10"
Text="{Binding SrcField.Name}"/>
<TextBlock Grid.Row="1" TextWrapping="Wrap" HorizontalAlignment="Center" Margin="10"
Text="{Binding SrcField.FieldType}"/>
</Grid>
</TextBlock>
</Border>
</ContentControl>
</DataTemplate>
They are 100% identical except for the stuff in the {}.
Is there a way to reduce the redundancy here? I fear I will make a change in one and forget to change the other.
As for your comment in the code - i think the problem can be solved by setting the Border's Background to Transparent.
And to the main problem. You could probably have a style that describes the ContentControl, which will include the DataTemplate for the type of the SrcField and DestField properties. Then bind in it to Name and FieldType, and use it the main 2 DataTemplates. Something like this:
<Style x:Key="SomeStyle" TargetType="ContentControl">
<Setter Property="Margin" Value="5" />
<Setter Property="MinWidth" Value="60" />
<Setter Property="MinHeight" Value="70" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType=...> <!-- type of the `SrcField` and `DestField` properties -->
<Border Background="Transparent" BorderThickness="2" BorderBrush="Black" CornerRadius="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock FontWeight="Bold" TextWrapping="Wrap" HorizontalAlignment="Center" Margin="10"
Text="{Binding Name}"/>
<TextBlock Grid.Row="1" TextWrapping="Wrap" HorizontalAlignment="Center" Margin="10"
Text="{Binding FieldType}"/>
</Grid>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate DataType="{x:Type WIAssistant:DestinationField}">
<ContentControl
Style="{StaticResource SomeStyle}"
Content="{Binding DestField}"
/>
</DataTemplate>
<DataTemplate DataType="{x:Type WIAssistant:SourceField}">
<ContentControl
Style="{StaticResource SomeStyle}"
Content="{Binding SrcField}"
/>
</DataTemplate>
Yeah, I'd create a user control with public dependency properties called Name and FieldType and then use that control in your DataTemplates.

Resources