How to expand bound & styled TreeViewItem nodes - wpf

I have a TreeView in WPF which I have bound to a collection of categories that itself contains a collection of categories and son on and so forth. Currently, I'm binding the collection successfully and having all of my category names appearing. However, I also have an IsExpanded property on my collection objects that when all set to true only expand the first TreeViewItem but no subsequent ones.
Does anyone know what may be wrong with my TreeView styles?
<HierarchicalDataTemplate x:Key="menuHierarchicalTemplate" ItemsSource="{Binding Path=SubCategories, Mode=TwoWay}" >
<Border x:Name="treeViewItemBorder"
CornerRadius="3"
MinWidth="125"
BorderBrush="Silver" Background="Transparent">
<StackPanel Orientation="Horizontal" >
<TextBlock Height="24" Padding="5" Text="{Binding Path=Name}" />
</StackPanel>
</Border>
</HierarchicalDataTemplate>
<Style x:Key="treeViewItemStyle" TargetType="TreeViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<TreeViewItem IsExpanded="{Binding Path=IsExpanded, Mode=TwoWay}"
ItemsSource="{Binding Path=SubCategories}"
ItemTemplate="{StaticResource menuHierarchicalTemplate}" >
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal" >
<TextBlock Height="24" FontSize="12" Padding="5" Text="{Binding Path=Name}" />
</StackPanel>
</TreeViewItem.Header>
</TreeViewItem>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<TreeView Name="menuTreeView"
Background="Transparent"
BorderBrush="Transparent"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Margin="10"
ItemContainerStyle="{StaticResource treeViewItemStyle}">
</TreeView>
Thanks,
Xam

You are applying the "treeViewItemStyle" only to the first level of items in the TreeView. You have to set it as well as the ItemContainerStyle of the TreeViewItems. So, in your ControlTemplate, you'll need something like:
<ControlTemplate TargetType="TreeViewItem">
<TreeViewItem ItemContainerStyle="{StaticResource treeViewItemStyle}".../>
</ControlTemplate>
Better yet, you can just remove the "x:Key" attribute in your Style declaration. Doing so will automatically apply the style to all TreeViewItems.
SIDE NOTE:
I would suggest that instead of overriding the ControlTemplate like what you did above, just use property Setters in the Style instead. Something like:
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded, Mode=TwoWay}"/>
<Setter Property="ItemsSource" Value="{Binding Path=SubCategories}"/>
<Setter Property="ItemTemplate" Value="{StaticResource menuHierarchicalTemplate}"/>
<Setter Property="Header">
<Setter.Value>
<StackPanel Orientation="Horizontal" >
<TextBlock Height="24" FontSize="12" Padding="5" Text="{Binding Path=Name}" />
</StackPanel>
</Setter.Value>
</Setter>
</Style>
It's quite unusual to use the same Control as part of the ControlTemplate of itself (i.e. using TreeViewItem in the ControlTemplate of a TreeViewItem).

Related

Create a Style for TabItem in WPF

I want to show an image and Text on each and every tabItem of my TabControl.
So, I created a Style as follows:
<TabControl.Resources>
<Style TargetType="TabItem">
<Style.Setters>
<Setter Property="Header">
<Setter.Value>
<StackPanel Orientation="Horizontal" Background="#FF2A2A2A" Margin="-7,-2" Cursor="Hand">
<Image Source="{Binding Tag}" Margin="10"/>
<TextBlock Text="{Binding Content}" FontSize="32" Foreground="White" Margin="0,10,10,10"/>
</StackPanel>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</TabControl.Resources>
Now in the TabItem:
<TabItem Tag="Images/Plus.png" Content="Create New" />
But I don't see any header on any tabItems??????
You need to use RelativeSource to travel up Visual tree to get your property bindings to work. By default it will search for property in DataContext of Image and not on TabItem.
<Image Source="{Binding Tag, RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=TabItem}}" Margin="10"/>
and similarly for TextBlock:
<TextBlock Text="{Binding Content, RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=TabItem}}"
FontSize="32" Foreground="White" Margin="0,10,10,10"/>
specify the name of the style
<TabControl.Resources>
<Style x:Key="TabItemStyle" TargetType="{x:Type TabItem}">
<Style.Setters>
and indicate style in TabItem
<TabItem Tag="Images/Plus.png" Content="Create New" Style="{StaticResource TabItemStyle}" />

ItemContainerStyle reuse silverlight 5

I have control template with a checkbox as this:
<Style x:Key="GeneralChkbxItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid x:Name="RootElement">
<CheckBox ClickMode="Press" Content="{Binding Path=Name}"
IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter
</Style>
I use this in a combobox and a listbox mentioning like this ItemContainerStyle="{StaticResource GeneralChkbxItemStyle .It works perfect when the datasource has a property named Name .
But I am wondering how can I use this template with other data source having class which does not have a name property . It might have a property name2, or name 3 etc depending on the datasource.
Any suggestions? Thanks.
In your template:
<CheckBox ClickMode="Press"
Content="{TemplateBinding Content}"
ContentTemplate="{templateBinding ContentTemplate}"
IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" />
Then in your view:
<ListBox DisplayMemberPath="Name"..../>
<!--OR-->
<ListBox DisplayMemberPath="Name2"..../>
<!--OR-->
......
You may use implicit data templates.
<DataTemplate DataType="MyApp:Person">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
<DataTemplate DataType="MyApp:Booking">
<TextBlock Text="{Binding Id}" />
</DataTemplate>
<Style x:Key="GeneralChkbxItemStyle"
TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid x:Name="RootElement">
<CheckBox ClickMode="Press"
Content="{Binding .}"
IsChecked="{Binding Path=IsSelected, Mode=TwoWay}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Bind image in templated ListBox to ViewModel property

TL;DR - I had a binding error. Tired eyes miss things.
I have implemented a multi-select CheckBox list using a ListBox as the container. Now, beside each checkbox in the list I want to display an image whose visbility is bound to a ViewModel property, but I'm having difficulty doing this.
My styles are:
<Grid.Resources>
<Style x:Key="ListBoxCheckStyle" TargetType="ListBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<ItemsPresenter HorizontalAlignment="Left" VerticalAlignment="Top"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ItemsPanelTemplate x:Key="ListBoxCheckStyleItemsPanelTemplate">
<StackPanel />
</ItemsPanelTemplate>
<Style x:Key="ListBoxItemCheckStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<StackPanel Orientation="Horizontal">
<ChimeControls:CheckBox
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="0,0,10,0"
IsChecked="{Binding IsSelected, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"/>
<Image
Width="16"
Height="16"
VerticalAlignment="Center"
Source="{StaticResource OccurredStatusTypeImageSource}"
Visibility="{Binding HasConsentCondition, Converter={StaticResource BoolToVisibilityConverter}, FallbackValue=Collapsed}"
HorizontalAlignment="Right"
Margin="10,0,10,0" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
My Listbox is defined as:
<ListBox
x:Name="objectivesListBox"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Style="{StaticResource ListBoxCheckStyle}"
ItemsPanel="{StaticResource ListBoxCheckStyleItemsPanelTemplate}"
ItemContainerStyle="{StaticResource ListBoxItemCheckStyle}"
ItemsSource="{Binding ObjectivesList}"
DisplayMemberPath="mgt_plan_obj_name"
AttachedProperties:ListBoxSelectedItems.Items="{Binding SelectedObjectives, Mode=TwoWay}"
SelectionMode="Multiple"/>
My image never displays though, and the getter of the property the Visibility is bound to is never called. What am I missing?
As suggested by nemesv, I checked my output window again and there was my binding error.

WPF TreeViewItem Control Template Partially applied?

I know a ControlTemplate is suppose to replace the default one entirely and this seems to work when not using a HierarchicalDataTemplate. However using a HierarchicalDataTemplate my control template seems to be partially used - the TreeViewItem is the control I specified containing an image etc. BUT still appears as a node with the default expander to show its children - not specified in my Template (I want my children to always be shown, but thats beside the point). It looks like this:
<TreeView>
<TreeView.Resources>
<Style x:Key="MyNodeStyle" TargetType="TreeViewItem">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="IsExpanded" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeViewItem">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center">
<Border CornerRadius="8" BorderThickness="1" Padding="2" Margin="4, 6" BorderBrush="{StaticResource itemBorderBrush}" Background="{StaticResource itemBackgroundBrush}" x:Name="border">
<DockPanel LastChildFill="False">
<StackPanel Orientation="Vertical" DockPanel.Dock="Top" Height="80">
<TextBlock Text="{Binding Path=DisplayValue}" HorizontalAlignment="Center" FontWeight="Bold"/>
<Image Source="MyNode.png" Stretch="None"/>
<TextBlock Text="{Binding Path=Notes}" TextWrapping="Wrap" Width="150"/>
</StackPanel>
</DockPanel>
</Border>
<ItemsPresenter />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" IsItemsHost="True" HorizontalAlignment="Center" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<HierarchicalDataTemplate DataType="{x:Type src:MyNode}" ItemsSource="{Binding Path=Children}" >
<TreeViewItem Style="{StaticResource MyNodeStyle}"/>
</HierarchicalDataTemplate>
</TreeView.Resources>
<!-- Arrange the root items horizontally. -->
<TreeView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True" Orientation="Horizontal" HorizontalAlignment="Center" />
</ItemsPanelTemplate>
</TreeView.ItemsPanel>
</TreeView>
For some reason only when using a HierarchicalDataTemplate the ItemsPanel and Setter does not seem to be applied and the children are presented in the default expander. How did that expander get there when I am using my own ControlTemplate!?##$
I don't think you should put the TreeViewItem inside your HierarchicalDataTemplate.
Try this:
<HierarchicalDataTemplate DataType="{x:Type src:MyNode}" ItemsSource="{Binding Path=Children}" >
<StackPanel Orientation="Vertical" DockPanel.Dock="Top" Height="80">
<TextBlock Text="{Binding Path=DisplayValue}" HorizontalAlignment="Center" FontWeight="Bold"/>
<Image Source="MyNode.png" Stretch="None"/>
<TextBlock Text="{Binding Path=Notes}" TextWrapping="Wrap" Width="150"/>
</StackPanel>
</HierarchicalDataTemplate>
Now, your template becomes:
<ControlTemplate TargetType="TreeViewItem">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center">
<Border CornerRadius="8" BorderThickness="1" Padding="2" Margin="4, 6" x:Name="border">
<DockPanel LastChildFill="False">
<ContentPresenter ContentSource="Header" />
</DockPanel>
</Border>
<ItemsPresenter />
</StackPanel>
</ControlTemplate>
Is that how you intended it to look?
Edit: the original expanders are probably there because you only use your style for child items - make your style the ItemContainerStyle for the treeview:
<Window.Resources>
<Style x:Key="MyNodeStyle" TargetType="TreeViewItem">
....
<TreeView ItemContainerStyle="{StaticResource MyNodeStyle}">

ListBoxItem IsSelected style

I still didn't get it. Could you please show me exactly how to override ListBox's default behavior.
Everytime when ListBoxItem is selected the Border's background should be changed. Not the background of the whole row but only background of the border which's specified.
<ListBox ItemsSource="{Binding Source={StaticResource AssetsViewSource}}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="2" BorderBrush="Black">
<StackPanel>
<TextBlock Text="Name: " />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Use the DataTemplate's Triggers collection, with a RelativeSource to get you to the containing ListBoxItem:
<DataTemplate>
<Border BorderThickness="2" BorderBrush="Black" Name="Bd">
<StackPanel>
<TextBlock Text="Name: " />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Value="True"
Binding="{Binding
IsSelected,
RelativeSource={RelativeSource
AncestorType={x:Type ListBoxItem}}}">
<!-- everybody loves HotPink -->
<Setter TargetName="Bd" Property="Background" Value="HotPink"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Simply add the following into the ListBox Item tag
<ListBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
</ListBox.Resources>
That should do the trick..

Resources