WPF - ToolTip in TextBlock that use the Text Property from the TextBlock - wpf

We have many TextBlocks in our application and many of them must have ToolTips.
Often the ToolTip should display the same as the TextBlock.Text property.
Also the ToolTip should not be displayed if the TextBlock.Text = "" or null.
So we have this solution in many many places:
<TextBlock Text="{Binding SomeTextProperty}">
<TextBlock.ToolTip>
<ToolTip Visibility="{Binding SomeTextProperty}, Converter={StaticResource StringToVisibilityConverter}">
<TextBlock Text="{Binding SomeTextProperty}" />
</TextBlock.ToolTip>
</TextBlock>
Notice:
I have to specify the SomeTextProperty three times on each TextBlock that need a TooLTip with this functionality.This seems very redundant!
The ToolTip.Content is a TextBlock itself.This is because I need to have a Style on the TextBlock.I have omitted that style to keep this post as simple as possible.
So I have tried to invent a Style for TextBlocks that use Bindings with RelativeSource to get the TextBlock.Text property for the ToolTip. I came up with this solution:
MainWindow (Just copy-paste more or less)
<Window x:Class="Main.Views.ToolTips"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Main.Views"
xmlns:converters="clr-namespace:Main.Converters"
mc:Ignorable="d"
Title="ToolTips" Height="450" Width="800">
<Window.Resources>
<Style TargetType="TextBlock" x:Key="TextBlockWithToolTipStyle">
<Style.Resources>
<converters:StringToVisibilityConverter x:Key="StringToVisibilityConverter" />
</Style.Resources>
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip Visibility="{Binding Text, RelativeSource={RelativeSource AncestorType={x:Type TextBlock}}, Converter={StaticResource StringToVisibilityConverter}}">
<ToolTip.Content>
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TextBlock}}}" />
</ToolTip.Content>
</ToolTip>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="This should display a tooltip" />
<TextBlock Text="asdf"
Background="Gray"
Style="{StaticResource TextBlockWithToolTipStyle}" />
<Separator Height="50" Visibility="Hidden" />
<TextBlock Text="This should not display a tooltip" />
<TextBlock Text=""
Background="LightGray"
Style="{StaticResource TextBlockWithToolTipStyle}" />
</StackPanel>
</Window>
StringToVisibilityConverter
public class StringToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var input = value is null ? string.Empty : value.ToString();
return string.IsNullOrWhiteSpace(input) ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
This, of course, don't work!
I put a breakpoint in the StringToVisibilityConverter and that breakpoint is not hit when I hover both TextBlocks in the Window.
In VS's XAML Binding Failures view I see two binding errors that regards ToolTip.Visibility and TextBlock.Text:
ToolTip.Visibility: Cannot find source: RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TextBlock', AncestorLevel='1'
TextBlock.Text: Cannot find source: RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TextBlock', AncestorLevel='1'
Is this possible to solve?
If so, how?
/BR,
Steffe

The ToolTip is not part of the visual tree. That's why your Binding.RelativeSource does not resolve.
To reference the element that is decorated by the ToolTip you must reference the ToolTip.PlacementTarget property:
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=ToolTip}, Path=PlacementTarget.Text}" />
Furthermore, you should not try to toggle the ToolTip.Visibility. It won't work.
Instead use a Trigger to set the ToolTip.
The correct solution would be as followed:
<Style TargetType="TextBlock"
x:Key="TextBlockWithToolTipStyle">
<Style.Resources>
<ToolTip x:Key="ToolTip">
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=ToolTip}, Path=PlacementTarget.Text}" />
</ToolTip>
</Style.Resources>
<Setter Property="ToolTip"
Value="{StaticResource ToolTip}" />
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="ToolTip" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>

Related

Get control properties from Grid.Resources in code behind

At the begining, theres xaml code:
<Grid.Resources>
<DataTemplate x:Name="dataTemp" x:Key="dtKey">
<WrapPanel Orientation="Horizontal" Name="mainWP">
<TextBlock Name="codeTB" FontSize="18" Width="200" Text="{Binding barcode}"></TextBlock>
(...)
</WrapPanel>
</DataTemplate>
</Grid.Resources>
and listview with datatemplate:
<ListView Name="testLV" Grid.Row="0" ItemTemplate="{StaticResource ResourceKey=dtKey}" >
</ListView>
So in code behind i'd like to change TextBlock width as this.width/5 (becouse width could be different in another PC), but becouse it's DataTemplate i don't have access to this control.
I also tried Width="{Binding Path=ActualWidth, ElementName=grid0}", but as actual width i need something like ActualWidth/5, which doesnt work
Thanks
Using a Grid with 5 columns and each with Width="0.2*" will work fine when all the child element's DesiredWidth have been satisfied(In other words when the size of the Grid is large enough to fit all columns with equal space). If it cannot do this, The layout works in a way to trim elements it can and give the extra space to other columns that need it more thereby overriding the Width="0.2*" in the process
For your requirement where you want the 5 columns to be split equally, just use a UniformGrid. That pretty much doesn't care abt any of the above things.
So say something like:
<ListView Name="paragonLV" HorizontalContentAlignment="Stretch">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Padding"
Value="0" />
<Setter Property="BorderThickness"
Value="0" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<UniformGrid MaxWidth="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ScrollViewer}},
Path=ActualWidth}"
Columns="5">
<UniformGrid.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextTrimming"
Value="CharacterEllipsis" />
<Setter Property="FontSize"
Value="18" />
<Setter Property="HorizontalAlignment"
Value="Stretch" />
</Style>
</UniformGrid.Resources>
<TextBlock Text="{Binding barCode}" />
<TextBlock Text="{Binding nazwa}" />
<TextBlock Text="{Binding jm}" />
<TextBlock Text="{Binding ilosc}" />
<TextBlock Text="{Binding cena}" />
</UniformGrid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Use Grid.Columndefination to format your grid or otherwise use Ivaluconverter class let's see value converter development
convert parameter will your calculating parameter,
you know how to construct value converter class
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
//value is grid actual width
// parameter = 5 is your calculated value
return value / parameter;
}

Pass listbox selected item information through to usercontrol (WPF)

I have a listbox, and each listbox item is a custom usercontrol I made. I have used styles to remove all of the default highlighting for a listbox item (ie. removing the blue background highlight for a selected item).
What I want is to be able to do something special to my user control to denote that the listbox item is highlighted. Such as make the border on the user control more bold, something like that.
If I could get a boolean into the user control, I think from there I'd be able to figure out how to make the necessary changes to the user control... through a converter or something most likely.
What I'm not sure of, is how do I pass into the usercontrol the information that shows whether the listbox item which the user control is in is highlighted.
The code in question is like this:
<ListBox.ItemTemplate>
<DataTemplate>
<hei:OrangeUserCtrl DataContext="{Binding}" Height="40" Width="40" />
</DataTemplate>
</ListBox.ItemTemplate>
How can I pass in to the user control (preferably as a true/false) if the listbox item it is in is highlighted?
Thanks
You can use Tag property and RelativeSource binding.
In my example when item is highlighted I changed Border properties (BorderBrush=Red and BorderThickness=3).
Source code:
Simple class to hold data:
class Person
{
public string Name { get; set; }
public string Surname { get; set; }
}
ListBox:
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<local:MyCustomPresenter DataContext="{Binding}"
Tag="{Binding Path=IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}, UpdateSourceTrigger=PropertyChanged}"
Height="60" Width="120" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
UserControl to display custom data:
<UserControl x:Class="WpfTextWrapping.MyCustomPresenter"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Border Margin="10">
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="Green" />
<Setter Property="BorderThickness" Value="1" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Tag, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}, UpdateSourceTrigger=PropertyChanged}" Value="True">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="3" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Surname}" />
</StackPanel>
</Border>
</UserControl>
If I understand you well, you need to add a property to your custom UserControl bound to the nested ComboBox something like :
public object MySelectedItem
{
get { return myNestedCombox.SelectedItem; }
set { myNestedCombox.SelectedItem = value; }
}
You need to NotifyPropertyChanged as well.

StackPanel Dynamic Binding of Visibility and IsEnabled Properties via Converters

My small objective is to implement a dynamic on-the-go generation of controls and associate their respective properties, such as Visibility and IsEnabled.
The controls must be inserted into the StackPanel. And based on some conditions, the children nodes properties would change. I've used the following nasted StackPanel structure:
The Parent StackPanel with Vertical orientation of Children (collection of Labels ad input Fields to simulate a form)
The Child node StackPanel with Horizontal orientation of Children (Label and an input field)
The main idea is that there is only one input field per each label: Date Picker, Combo, Text or any other. As result, I created a Label and a Grid with multiple Controls. I manipulate Visibility and IsEnabled properties of the input controls via Converters.
Here is the question: is it possible to implement all these by other means (more efficient/aesthetic)? Constructive criticism and suggestions are more than welcome :)
Thank you in advance.
XAML:
<StackPanel Grid.Row="1" Orientation="Vertical">
<ItemsControl ItemsSource="{Binding DataClass}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0, 5, 0, 0" >
<Label Content="{Binding KeyName}" Width="150"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="190"/>
</Grid.ColumnDefinitions>
<DatePicker Text="{Binding Value}"
Visibility="{Binding Type, Converter={StaticResource TypeVisiblity}, ConverterParameter='DateTime'}"
IsEnabled="{Binding RelativeSource={x:Static RelativeSource.Self}, Converter={StaticResource VisibilityEnabled}}"/>
<ComboBox Text="{Binding Value}"
Visibility="{Binding Type, Converter={StaticResource TypeVisiblity}, ConverterParameter='Lookup'}"
IsEnabled="{Binding RelativeSource={x:Static RelativeSource.Self}, Converter={StaticResource VisibilityEnabled}}"/>
<TextBox Text="{Binding Value}"
Visibility="{Binding Type, Converter={StaticResource TypeVisiblity}, ConverterParameter='Number'}"
IsEnabled="{Binding RelativeSource={x:Static RelativeSource.Self}, Converter={StaticResource VisibilityEnabled}}"/>
<TextBox Text="{Binding Value}"
Visibility="{Binding Type, Converter={StaticResource TypeVisiblity}, ConverterParameter='Text'}"
IsEnabled="{Binding RelativeSource={x:Static RelativeSource.Self}, Converter={StaticResource VisibilityEnabled}}"/>
</Grid>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
Visibility Converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool isMatched;
string type,
controlType;
// Safe Convert.
type = System.Convert.ToString(value);
controlType = System.Convert.ToString(parameter);
if (string.IsNullOrEmpty(type) || string.IsNullOrEmpty(controlType))
{
return Visibility.Hidden;
}
// Check matching.
isMatched = string.Equals(type, controlType, StringComparison.CurrentCultureIgnoreCase);
return isMatched ? Visibility.Visible : Visibility.Hidden;
}
IsEnabled Converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Control control;
control = value as Control;
if (control == null)
{
return false;
}
return control.Visibility == Visibility.Visible;
}
I would use a ContentControl and set the ContentTemplate in a style
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<!-- Default Template -->
<Setter Property="ContentTemplate"
Value="{DynamicResource TextBoxTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Type}" Value="DateTime">
<Setter Property="ContentTemplate"
Value="{DynamicResource DateTimeTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding Type}" Value="Lookup">
<Setter Property="ContentTemplate"
Value="{DynamicResource ComboBoxTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
Create a user control that has your DatePicker, ComboBox, TextBox etc as user interface and implement all necessary logic in code behind of this user control. You can add dependency properties to implement your logic without converters.
You can add your user control into StackPanel easily by adding it into its Children property.

How can I base a style on a Silverlight toolkit theme?

is it possible to do this?
In my xaml code, I have some ComboBoxes with a style, defined like this:
<Style x:Key="comboProjectsStyle"
TargetType="ComboBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=Name}"
FontSize="14" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="comboDataSourcesStyle"
TargetType="ComboBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=DescriptiveName}"
FontSize="14" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<ComboBox Width="300"
Style="{StaticResource comboProjectsStyle}" />
<ComboBox Width="300"
Style="{StaticResource comboDataSourcesStyle}" />
The silverlight theme (eg: ExpressionDark) is correctly applied on every controls except on the ones where I have defined a style, like above.
As I understand, in WPF we could use x:Style an base our Style on the silverlight theme using the "BasedOn" property. However, it seems like it is not possible to do so with Silverlight 4.
Any ideas on how to approach this?
Thanks!
Declare your ItemTemplate as a resource rather than in a style then your theme style will apply.
<UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="Silverlight_Spike.MainPage"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<DataTemplate x:Key="DataTemplate1">
<Grid>
<TextBlock Text="{Binding Name}" FontSize="14" />
</Grid>
</DataTemplate>
</UserControl.Resources>
<ComboBox ItemTemplate="{StaticResource DataTemplate1}" />
</UserControl>
Remove the key in the style:
<Style TargetType="ComboBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=Name}" FontSize="14" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="comboProjectsStyle" TargetType="ComboBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=Name}" FontSize="14" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="comboDataSourcesStyle" TargetType="ComboBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=DescriptiveName}" FontSize="14" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
<ComboBox Width="300" Style="{StaticResource comboProjectsStyle}" />
<ComboBox Width="300" Style="{StaticResource comboDataSourcesStyle}" />
<ComboBox Width="300" />
<ComboBox Width="300" />
<ComboBox Width="300" />
Basically this means the style you made above applies to a target type of ComboBox, it is not named therefore every Combobox without a style set to it will inherit this as default.
UPDATE:
As you can see all the 3 styles can coexist in the same resource, whenever you use a named style it will be applied to the said control but, for the last three comboboxes, all 3 will be having the style without the key. This is how it is done in themming, like in the JetPack skin from MS.
hope this helps.
Hi this is not exactly using basedOn but by using a converter we can achieve the goal of basing the custom style to the theme. Here's how we do it.
Expose a Theme and place it where you can bind it to the style.
Create a value converter to convert the style that you are using. This converter will return a style that is based on the theme, so here's a snippet.
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter is Style)
{
Style retStyle = parameter as Style;
Theme themeContainer;
if (value is Theme)
themeContainer = value as Theme; //(App.Current as App).AppTheme;
else
themeContainer = (App.Current as App).AppTheme;
if (themeContainer != null)
{
foreach (DictionaryEntry i in themeContainer.ThemeResources)
{
if (i.Value is Style)
{
Style t = i.Value as Style;
if (t.TargetType == retStyle.TargetType)
{
Style newStyle = new Style();
newStyle.TargetType = retStyle.TargetType;
newStyle.BasedOn = t;
foreach (Setter set in retStyle.Setters)
newStyle.Setters.Add(new Setter() { Property = set.Property, Value = set.Value });
return newStyle;
}
}
}
}
return retStyle;
}
return null;
}
Bind the theme to the style and use the converter on every custom style that you use
Style="{Binding Theme, Converter={StaticResource styleConverter}, ConverterParameter={StaticResource ButtonStyle1}}"
Where theme is a property of type Theme(System.Windows.Controls.Theming).
I uplaoded my sample project here
The sample code is not updated but you can start from there.

WPF Setting style based on datatype?

Here's the problem. I'm binding a TreeView with a few different types of objects. Each object is a node, and SOME objects have a property called IsNodeExpanded, and of course, some others don't. Here's my style:
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsNodeExpanded, Mode=TwoWay}" />
</Style>
Now, the problem is when binding the items that DON'T have this property, we get this error in the output:
System.Windows.Data Error: 39 : BindingExpression path error: 'IsNodeExpanded' property not found on 'object' ''CompensationChannel' (HashCode=56992474)'. BindingExpression:Path=IsNodeExpanded; DataItem='CompensationChannel' (HashCode=56992474); target element is 'TreeViewItem' (Name=''); target property is 'IsExpanded' (type 'Boolean')
Of course we get it a ton of times. So I'm trying to come up with a way to switch the style of the TreeViewItem based on the DataType it holds. Any idea on how to do this?
Some info: I can't do it manually for each item because I'm not creating them in XAML, they are created dynamically from a data source.
EDIT: I found this answer but it didn't work for me.
Try using the TreeView.ItemContainerStyleSelector property with a custom StyleSelector class which changes the style depending if the bound object has that property or not.
public class TreeItemStyleSelector : StyleSelector
{
public Style HasExpandedItemStyle { get; set; }
public Style NoExpandedItemStyle { get; set; }
public override Style SelectStyle(object item, DependencyObject container)
{
// Choose your test
bool hasExpandedProperty = item.GetType().GetProperty("IsExpanded") != null;
return hasExpandedProperty
? HasExpandedItemStyle
: NoExpandedItemStyle;
}
}
In the XAML Resources:
<Style x:Key="IsExpandedStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsNodeExpanded, Mode=TwoWay}" />
</Style>
<Style x:Key="NoExpandedStyle" TargetType="{x:Type TreeViewItem}">
</Style>
<x:TreeViewItemStyleSelector x:Key="TreeViewItemStyleSelector"
HasExpandedItemStyle="{StaticResource IsExpandedStyle}"
NoExpandedItemStyle="{StaticResource NoExpandedStyle}" />
In the XAML:
<TreeView ItemsSource="{Binding ...}"
ItemContainerStyleSelector="{StaticResource TreeItemStyleSelector}">
UPDATE
<TreeView.Resources>
... following is only for one type of data
<HierarchicalDataTemplate
DataType="{x:Type local:RegionViewModel}"
ItemsSource="{Binding Children}"
>
... define your style
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}"
... following line is necessary
BasedOn="{StaticResource {x:Type TreeViewItem}}">
..... your binding stuff....
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16"
Margin="3,0" Source="Images\Region.png" />
<TextBlock Text="{Binding RegionName}" />
</StackPanel>
</HierarchicalDataTemplate>
...
</TreeView.Resources>
ALTERNATIVE WAY
Instead of Switching Styles, you should use HierarchicalDataTemplate and DataTemplate in order to style your TreeViewItem, they will work similarly unless you want to change certain inherited framework properties.
You can define different "DataTemplate" and "HeirarchicalDataTemplate" based on different types of object that are bound to for Item Template of TreeView.
And that is why these templates are designed to completely seperate your UI logic and code behind, using Selector etc or any such coding, you will introduce UI dependency more on your code behind, which WPF is not intended for.
Here is the link,
TreeView DataBinding
And see how you can define item templates in resources,
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type local:RegionViewModel}"
ItemsSource="{Binding Children}"
>
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16"
Margin="3,0" Source="Images\Region.png" />
<TextBlock Text="{Binding RegionName}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate
DataType="{x:Type local:StateViewModel}"
ItemsSource="{Binding Children}"
>
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16"
Margin="3,0" Source="Images\State.png" />
<TextBlock Text="{Binding StateName}" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:CityViewModel}">
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16"
Margin="3,0" Source="Images\City.png" />
<TextBlock Text="{Binding CityName}" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
Would using a FallbackValue on the binding work for you? This would apply if the binding fails...
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsNodeExpanded, Mode=TwoWay, FallbackValue=False}" />
</Style>
DataTrigger-based solution:
<UserControl.Resources>
<converters:DataTypeConverter x:Key="DataTypeConverter"/>
</UserControl.Resources>
<!-- .... -->
<Style TargetType="{x:Type TreeViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource DataTypeConverter}}"
Value="{x:Type yourClasses:ClassWithIsNodeExpanded}">
<Setter Property="IsExpanded" Value="{Binding IsNodeExpanded}" />
</DataTrigger>
</Style>
And DataTypeConverter:
namespace Converters
{
/// <summary>
/// Implement an IValueConverter named DataTypeConverter, which accepts an object and returns its Type(as a
/// System.Type):
/// Usage:
/// Change your DataTrigger to use the Converter, and set the value to the Type:
/// <DataTrigger Binding="{Binding SelectedItem,
/// Converter={StaticResource DataTypeConverter}}"
/// Value="{x:Type local:MyType}">
/// ...
/// </DataTrigger>
/// Declare DataTypeConverter in the resources:
/// <UserControl.Resources>
/// <v:DataTypeConverter x:Key="DataTypeConverter"></v:DataTypeConverter>
/// </UserControl.Resources>
/// </summary>
[ValueConversion(typeof(object), typeof(Type))]
public class DataTypeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
return value.GetType();
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

Resources