Using the Treeview HierarchicalDataTemplate.Triggers to change folder icon - wpf

I'm pretty fresh to WPF and this is the closest I have come to achieving what I set out to do after reviewing many of the previously asked questions posted here.
The XAML code:
<TreeView x:Name="folderView" Grid.Column="0" Grid.Row="1" BorderThickness="0">
<TreeViewItem Header="Folders" ItemsSource="{Binding SubFolders, Source={StaticResource RootFolderDataProvider}}" Margin="5"/>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type my:FolderView}" ItemsSource="{Binding SubFolders}">
<StackPanel Orientation="Horizontal" Name="myPanel">
<Image x:Name="img" Width="16" Height="16" Source="Images/FolderClosed.png" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding IsExpanded}" Value="True">
<Setter TargetName="img" Property="Source" Value="Images/FolderOpen.png"/>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
This displays the FolderClosed image on all my subfolders except the very top root folder.
The HierachicalDataTemplate trigger also fails to fire when expanded.
Any help would be appreciated.

If you are binding to the TreeViewItem IsExpanded property, then you will have to update your binding like:
<DataTrigger Binding="{Binding IsExpanded, RelativeSource={RelativeSource AncestorType={x:Type TreeViewItem}}}" Value="True">
<Setter TargetName="img" Property="Source" Value="Images/FolderOpen.png"/>
</DataTrigger>

I cannot pin point the issues. But as a first step you should check if the binding is working or not.
you can add debugging for binding as mentioned in here
eg :
<Window
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
xmlns:local="clr-namespace:DebugDataBindings"
x:Class="DebugDataBindings.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="layoutRoot">
<Grid.Resources>
<local:DebugConverter x:Key="debugConverter" />
</Grid.Resources>
<TextBox
Text="{Binding Path=Customer.FirstName, diag:PresentationTraceSources.TraceLevel=High}"
Height="23" HorizontalAlignment="Left" Margin="90,18,0,0" VerticalAlignment="Top" Width="120"/>
</Grid>
</Window>

Related

WPF Treeview Icon Plus Minus

I'm trying to add icons in my treeview but it's not showing up. The HierarchicalDataTemplate is in the windows resources and treeview is in the Grid.
Can any body tell me what is the mistake i'm making?
Here is the XML:
<Window x:Class="Treeview.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Support" Height="700" Width="1024" SizeToContent="WidthAndHeight"
mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="229" Icon="/Test;component/Images/Treeview.jpg">
<Window.Resources>
<XmlDataProvider x:Key="questions" XPath="Questions/Question" />
<HierarchicalDataTemplate x:Key="rootTemplate">
<HierarchicalDataTemplate.ItemsSource>
<Binding XPath="child::*" />
</HierarchicalDataTemplate.ItemsSource>
<StackPanel Orientation="Horizontal">
<CheckBox Name="checkBoxTree" Checked="TreeView_Checked" Unchecked="checkBoxTree_Unchecked" />
<Image Style="{StaticResource ExpandingImageStyle}">
<Image.Resources>
<BitmapImage x:Key="Icon_Closed" UriSource="Images/Plus.ico"/>
<BitmapImage x:Key="Icon_Open" UriSource="Images/Minus.ico"/>
</Image.Resources>
</Image>
<TextBlock Text="{Binding XPath=#Name, Mode=TwoWay}" />
<TextBlock>
<Hyperlink NavigateUri="{Binding XPath=#WebSite}" RequestNavigate="Hyperlink_RequestNavigate">
<TextBlock Text="{Binding XPath=#WebSite}" />
</Hyperlink>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
</Window.Resources>
<Grid>
<TreeView Name="TreeViewer" HorizontalAlignment="Left" Height="220" Margin="10,116,0,0" Grid.Row="1"
VerticalAlignment="Top" Width="700" Visibility="Collapsed" FontSize="15"
ItemsSource="{Binding Source={StaticResource questions}}" ItemTemplate="{StaticResource rootTemplate}" >
<TreeView.Resources>
<Style x:Key="ExpandingImageStyle" TargetType="{x:Type Image}">
<Setter Property="Source" Value="{DynamicResource Icon_Closed}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, Path=IsExpanded}" Value="True">
<Setter Property="Source" Value="{DynamicResource Icon_Open}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.Resources>
</TreeView>
</Grid>
</Window>
Surely with that XAML you have a Warning shown in the Visual Studio Error List like the following?:
The resource "ExpandingImageStyle" could not be resolved.
This is telling you that your HierarchicalDataTemplate can't find the Resource. If you move that Style to the top of your Window.Resources, the first problem should disappear. However, when you do that, you'll then get a warning that the Style can't find the two BitmapImage Resources. So you'd better move those two Resources to the top of your Window.Resources:
<BitmapImage x:Key="Icon_Closed" UriSource="Images/Plus.ico"/>
<BitmapImage x:Key="Icon_Open" UriSource="Images/minus.ico"/>
<Style x:Key="ExpandingImageStyle" TargetType="{x:Type Image}">
...
</Style>
<XmlDataProvider x:Key="questions" XPath="Questions/Question" />
<HierarchicalDataTemplate x:Key="rootTemplate">
...
<Image Style="{StaticResource ExpandingImageStyle}" />
...
</HierarchicalDataTemplate>
If you find that that still doesn't work, please take a look at any errors or warnings that you may get in either the Error List or Output Window in Visual Studio and let us know.

WPF TextBlock StringFormat Binding To Parent

I have been trying to bind the StringFormat of the Text property of a TextBlock to a templated parent.
Here is where I'm trying to set the StringFormat:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataFlowControls">
<Style TargetType="{x:Type local:DfcEditTextBox}">
<Setter Property="Margin" Value="-6, 0, -6, 0" />
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:DfcEditTextBox}">
<TextBlock x:Name="PART_TextBlock"
Padding="2, 0, 0, 0"
Text="{Binding Path=Value, StringFormat=ThisStringFormat, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Here is the parent:
<Window x:Class="DataFlowControls.Show.DfcListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dfc="clr-namespace:DataFlowControls;assembly=DataFlowControls"
xmlns:local="clr-namespace:DataFlowControls.Show"
Title="DfcListView" Height="400" Width="500">
<Grid>
<StackPanel>
<dfc:DfcListView Name="lvTradesCollection" ItemsSource="{Binding Path=TradesCollection}" KeyboardNavigation.DirectionalNavigation="Continue" >
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.Resources>
<DataTemplate x:Key="Date_DataTemplate">
<dfc:DfcEditTextBox Value="{Binding Path=Date, Mode=Twoway, UpdateSourceTrigger=PropertyChanged}" ThisStringFormat='{}{0:dd/MM/yyyy}' HorizontalContentAlignment="Left" IsEditable="true" />
</DataTemplate>
<DataTemplate x:Key="Asset_DataTemplate">
<dfc:DfcEditTextBox Value="{Binding Path=Asset, Mode=Twoway, UpdateSourceTrigger=PropertyChanged}" HorizontalContentAlignment="Left" IsEditable="true" />
</DataTemplate>
<DataTemplate x:Key="Lots_DataTemplate">
<dfc:DfcEditTextBox Value="{Binding Path=Lots, Mode=Twoway, UpdateSourceTrigger=PropertyChanged}" ThisStringFormat='N2' HorizontalContentAlignment="Center" IsEditable="true" />
</DataTemplate>
<DataTemplate x:Key="Price_DataTemplate">
<dfc:DfcEditTextBox Value="{Binding Path=Price, Mode=Twoway, UpdateSourceTrigger=PropertyChanged}" HorizontalContentAlignment="Center" IsEditable="true" />
</DataTemplate>
<DataTemplate x:Key="IsCheap_DataTemplate">
<dfc:DfcEditCheckBox Value="{Binding Path=IsCheap, Mode=Twoway, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" IsEditable="true" />
</DataTemplate>
<DataTemplate x:Key="NextTrade_DataTemplate">
<dfc:DfcEditComboBox Value="{Binding Path=NextTrade, Mode=Twoway, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{x:Static local:DfcListView.NextTradeTypes}" IsEditable="true" />
</DataTemplate>
</ListView.Resources>
<ListView.View>
<dfc:DfcGridView>
<dfc:DfcGridViewColumn Header="Date" Width="140" CellTemplate="{StaticResource Date_DataTemplate}" />
<dfc:DfcGridViewColumn Header="Asset" Width="40" CellTemplate="{StaticResource Asset_DataTemplate}" />
<dfc:DfcGridViewColumn Header="Lots" Width="40" CellTemplate="{StaticResource Lots_DataTemplate}" />
<dfc:DfcGridViewColumn Header="Price" Width="50" CellTemplate="{StaticResource Price_DataTemplate}" />
<dfc:DfcGridViewColumn Header="IsCheap" Width="60" CellTemplate="{StaticResource IsCheap_DataTemplate}" />
<dfc:DfcGridViewColumn Header="NextTrade" Width="80" CellTemplate="{StaticResource NextTrade_DataTemplate}" />
</dfc:DfcGridView>
</ListView.View>
</dfc:DfcListView>
<Button Content="Add Row" HorizontalAlignment="Left" Margin="5,5,5,5" Click="AddRow_Click"/>
<Button Content="Update Row" HorizontalAlignment="Left" Margin="5,5,5,5" Click="UpdateRow_Click"/>
</StackPanel>
</Grid>
</Window>
Everything works fine until I include the StringFormat=ThisStringFormat, which mucks it up. But I somehow need to connect the StringFormat to the ThisStringFormat property expressed in the parent. I've experimented with changing StringFormat=ThisStringFormat to try to get to the templated parent, but to no avail.
Any ideas on how to solve this one?
The StringFormat property is just a regular property on BindingBase and regular properties cannot be binding targets only dependency properties can. So the answer is: You cannot not do it that way.
Some possible approaches:
Subclass TextBox and add a string format dependency property to which you can bind providing the required functionality
Augment your view model (if you have one) with a FormattedValue property (probably a bit ugly)
Use a MultiBinding for the Text property. One binding goes to Value and one to the ThisStringFormat of the templated parent. Then write a converter implementing IMultiValueConverter to return the formatted value.

Change listbox template on lost focus event in WPF

I want to present a listbox with TextBlocks as items. When the user clicks/selects an item it changes into a TextBox for editing. As soon as the controls loses focus the item would turn back to a TextBlock.
The following XAML is almost working in that the TextBlock does turn into a TextBox when it's selected. It also turns back to a TextBlock if I select another item on the list. The problem is that if I move out of the listbox (in this case to the Add New text box) the list item stays as a TextBox.
The question ( WPF ListViewItem lost focus event - How to get at the event? ) looked promising but I can't make it work. I tried using the IsFocused property but then I wasn't able to edit the textbox because when I got into the textbox the listboxitem would go out of focus and thus turning back to a TextBlock before I had a chance to edit the text.
How can I make the TextBox turn back to TextBlock if the listbox/item loses the focus?
(Any other implementation that accomplishes the goal are also welcomed)
<Window x:Class="MyView.MainWindow1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Title="MainWindow1" Height="300" Width="200">
<Window.Resources>
<DataTemplate x:Key="ItemTemplate">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
<DataTemplate x:Key="SelectedTemplate">
<TextBox Text="{Binding Name, Mode=TwoWay}" />
</DataTemplate>
<Style TargetType="{x:Type ListBoxItem}" x:Key="ContainerStyle">
<Setter Property="ContentTemplate" Value="{StaticResource ItemTemplate}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource SelectedTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel >
<ListBox ItemsSource="{Binding Departments}" HorizontalContentAlignment="Stretch"
ItemContainerStyle="{StaticResource ContainerStyle}" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Text="{Binding NewDepartmentName, Mode=TwoWay}" />
<Button Grid.Column="1" Width="50" Content="Add" Command="{Binding Path=AddNewDepartmentCommand}" />
</Grid>
</StackPanel>
</Window>
This doesn't answer your question, but gives an alternative solution by making a textbox look like a textblock when the listboxitem isn't selected:
<Page.Resources>
<ResourceDictionary>
<Style x:Key="ListBoxSelectableTextBox" TargetType="{x:Type TextBox}">
<Setter Property="IsHitTestVisible" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}, AncestorLevel=1}}" Value="True">
<Setter Property="IsHitTestVisible" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</Page.Resources>
<Grid>
<ListBox ItemsSource="{Binding Departments}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Margin="5" Style="{StaticResource ListBoxSelectableTextBox}" Text="{Binding Name}" BorderBrush="{x:Null}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
There are many ways you can do this:
Above answer
Create a TextBox style based on TextBoxBase such that when disabled, it makes the TextBox look like a TextBlock by setting the BorderThickness="0", Background="transparent"
Have both TextBlock and TextBox on each ListViewItem and using the above answer technique hide and show the TextBlock/TextBox accrodingly

WPF- Validation -The validation error message goes behind the other controls because of AdornerDecorator

I have implemented IDataErrorInfo in my ViewModel to return a string if the text box has error.
public string this[string columnName]
{
get { return "Error-- This is a long error message - sd"; }
}
But this error message goes behind the other control on the UI as shown below.
Below is the xaml:
<Window x:Class="Test.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="600" Width="600">
<Window.Resources>
<ControlTemplate x:Key="validationTemplateNew">
<DockPanel LastChildFill="True">
<TextBlock Name="ErrorText" DockPanel.Dock="Bottom" Foreground="White" Background="Red"
FontSize="12" Padding="2" FontFamily="Trebuchet MS"
Margin="5,5,0,0"
TextWrapping="Wrap"
Text="{Binding [0].ErrorContent}" ></TextBlock>
<AdornedElementPlaceholder Name="ErrorTextBox" />
</DockPanel>
</ControlTemplate>
<Style x:Key="ValidationStyle" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BitmapEffect">
<Setter.Value>
<BitmapEffectGroup>
<OuterGlowBitmapEffect GlowColor="Red" GlowSize="3" Noise="0.6"></OuterGlowBitmapEffect>
</BitmapEffectGroup>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ItemsControl Name="ItemCtrl">
<AdornerDecorator>
<TextBox
FontSize="11"
Margin="10"
Width="250"
VerticalAlignment="Center"
Text="{Binding Path=StrText, ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged}"
Validation.ErrorTemplate="{StaticResource validationTemplateNew}"
Style="{StaticResource ValidationStyle}"
>
</TextBox>
</AdornerDecorator>
<TextBox Width="250" Text="ASDFASFASDFASDFASDFASDFASDF"/>
<TextBox Width="250" Text="ASDFASFASDFASDFASDFASDFASDF"/>
<TextBox Width="250" Text="ASDFASFASDFASDFASDFASDFASDF"/>
<TextBox Width="250" Text="ASDFASFASDFASDFASDFASDFASDF"/>
<TextBox Width="250" Text="ASDFASFASDFASDFASDFASDFASDF"/>
</ItemsControl>
</Grid>
</Window>
Please let me know how to use AdornerDecorator such that the error message overlaps the other controls and doesn't go behind.
My application is such that if I don't use AdornerDecorator, the error message is not displayed at all.
Adding Grid.ZIndex on the AdornerDecorator should be enough
<Grid>
<ItemsControl Name="ItemCtrl">
<AdornerDecorator Grid.ZIndex="1">

Data-based template selection in WPF

I have this simple XAML example:
<Window x:Class="DynTemplateTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="ItemTemplate">
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Width="30" Height="30" Fill="Red"></Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding Position}"></Setter>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</DataTemplate>
</Window.Resources>
<DockPanel LastChildFill="True">
<ContentPresenter
Content="{Binding Path=Items}"
ContentTemplate="{StaticResource ItemTemplate}"
>
</ContentPresenter>
</DockPanel>
</Window>
It is rendering my items in the observable collection in MVVM style. Each item is having its horizontal position in a property. Each item also has a property IsSpecial which tells if it wants to be rendered in some special way. I want ordinary items (IsSpecial=false) render as red squares (already in the code) and special items as blue circles with "special" text inside.
What I do not know is how to adjust the XAML code to do the template selection for the items. Is there a way to do that without coding my own ItemTemplateSelector? Will it still work with the canvas positioning based on binding. I think that the solution is to extract the item template to a separate template, create one more template for special items and then somehow play with triggers ... but it is not very easy for me as I am a WPF beginner at the moment.
The other thing is that I quite dislike the way how the Position is passed to the items. Is there some other way?
Are there any other recommendations how to improve the code?
I solved it myself :D
<Window x:Class="DynTemplateTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="NormalItem">
<Rectangle Width="30" Height="30" Fill="Red"></Rectangle>
</DataTemplate>
<DataTemplate x:Key="SpecialItem">
<Rectangle Width="30" Height="30" Fill="Red"></Rectangle>
</DataTemplate>
<DataTemplate x:Key="ItemTemplate">
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource NormalItem}" x:Name="ItemsContentControl" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsSpecial}" Value="true">
<Setter TargetName="ItemsContentControl" Property="ContentTemplate" Value="{StaticResource SpecialItem}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding Position}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</DataTemplate>
</Window.Resources>
<DockPanel LastChildFill="True">
<ContentPresenter
Content="{Binding Path=Items}"
ContentTemplate="{StaticResource ItemTemplate}"
>
</ContentPresenter>
</DockPanel>
</Window>
But still, any thoughts on alternatives or improvements?

Resources