WPF: LineBreak enable/disable dynamically - wpf

i would like to make the LineBreak element inside of that TextBlock controllable by the user in preferences to Enable/Disable it being there
<TextBlock Style="{StaticResource TextBlockStyle}" Width="130">
<TextBlock.Inlines>
<Run Text="{Binding Path=Name}" FontWeight="Bold" Foreground="#FF2A4D9E" />
<Run Text="{Binding Path=Price}" FontWeight="Bold" />
<LineBreak />
<Run Text="{Binding Path=Quantity}" Foreground="#99000000" />
</TextBlock.Inlines>
</TextBlock>

I don't believe there is any way in a FlowDocument to make a LineBreak not really break except to take it out. Your choices are to switch to using WPF layout or to use an attached property to switch between a LineBreak and an empty Run.
Using WPF layout
You may consider using WPF layout instead. Something like this:
<DataTemplate x:Key="Layout1">
<DockPanel>
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="#FF2A4D9E" />
<TextBlock Text="{Binding Price}" FontWeight="Bold" />
<TextBlock Text="{Binding Quantity}" Foreground="#99000000" />
</DockPanel>
</DataTemplate>
<DataTemplate x:Key="Layout2">
<DockPanel>
<DockPanel DockPanel.Dock="Top">
<TextBlock Text="{Binding Name}" FontWeight="Bold" Foreground="#FF2A4D9E" />
<TextBlock Text="{Binding Price}" FontWeight="Bold" />
</DockPanel>
<TextBlock Text="{Binding Quantity}" Foreground="#99000000" />
</DockPanel>
</DataTemplate>
Now you can easily switch between layouts just by switching DataTemplates.
Automatically removing LineBreaks using bindings
If you want to "hide" the LineBreak via a binding you can do it with an attached "BecomeLineBreak" property that, when applied to an empty Run and set true, removes it and replaces it with a LineBreak.
Like magic you now have the ability to write:
<Run my:LineBreakSwitcher.BecomeLineBreak="{Binding SomeCondition}" />
And your Run will turn into a LineBreak any time the SomeCondition property is true.
Here is the code:
public class LineBreakSwitcher : DependencyObject
{
public static bool GetBecomeLineBreak(DependencyObject obj) { return (bool)obj.GetValue(BecomeLineBreakProperty); }
public static void SetBecomeLineBreak(DependencyObject obj, bool value) { obj.SetValue(BecomeLineBreakProperty, value); }
public static readonly DependencyProperty BecomeLineBreakProperty = DependencyProperty.RegisterAttached("BecomeLineBreak", typeof(bool), typeof(LineBreakSwitcher), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
var oldElement = (Inline)obj;
var newElement = (bool)e.NewValue ? (Inline)new LineBreak() : new Run();
newElement.SetBinding(BecomeLineBreakProperty, oldElement.GetBindingExpression(BecomeLineBreakProperty).ParentBindingBase);
var parent = (Paragraph)oldElement.Parent;
parent.Inlines.InsertBefore(oldElement, newElement);
parent.Inlines.Remove(oldElement);
}
});
How it works: When BecomeLineBreak becomes true on a Run, a new LineBreak is created, the BecomeLineBreak binding is copied across, the LineBreak is inserted before the Run, then the Run is removed. When BecomeLineBreak become false, a new Run is created and the whole process happens in reverse.

This is what you want (100% XAML):
<TextBlock Text="{Binding Text, ElementName=MyContainer}"
FontWeight="Bold" FontSize="14" Name="TextBlockA" />
<TextBlock Name="TextBlockB">
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text.Length,
ElementName=TextBlockC}" Value="0">
<Setter Property="TextBlock.Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
<LineBreak />
</TextBlock>
<TextBlock Text="{Binding SubText, ElementName=MyContainer}"
FontWeight="Normal" FontSize="12" Name="TextBlockC" />

Related

PropertyChanged value is null for child ViewModel property

I have an ObservableCollection of ViewModels in my main ViewModel. The binding seems to work fine since I can switch views. However, raising the ViewModelBase OnPropertyChanged method (which work for other stuff) in an element of the ObservableCollection result in a null PropertyChanged value in ViewModelBase.
Here's my main code snippets:
In my main ViewModel Constructor:
public EditorViewModel()
{
base.DisplayName = Strings.EditorName;
_availableEditors = new ObservableCollection<ViewModelBase>();
AvailableEditors.Add(new GBARomViewModel(646, 384));
AvailableEditors.Add(new MonsterViewModel(800, 500));
CurrentEditor = _availableEditors[0];
}
At GBA ROM loading, ViewModel and Model properties are updated:
void RequestOpenRom()
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.DefaultExt = ".gba";
dlg.Filter = "GBA ROM (.gba)|*.gba|All files (*.*)|*.*";
dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Nullable<bool> result = dlg.ShowDialog();
if (result == true)
{
if(CurrentEditor is GBARomViewModel)
{
(CurrentEditor as GBARomViewModel).ReadRom(dlg.FileName);
}
}
}
In my main View: Variation of TabControl (to have view switching and view states preservation).
<controls:TabControlEx ItemsSource="{Binding AvailableEditors}"
SelectedItem="{Binding CurrentEditor}"
Style="{StaticResource BlankTabControlTemplate}"
MinWidth="{Binding CurrentEditorWidth}"
MinHeight="{Binding CurrentEditorHeight}"
MaxWidth="{Binding CurrentEditorWidth}"
MaxHeight="{Binding CurrentEditorHeight}"
Width="{Binding CurrentEditorWidth}"
Height="{Binding CurrentEditorHeight}"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<controls:TabControlEx.Resources>
<DataTemplate DataType="{x:Type vm:GBARomViewModel}">
<vw:GBARomView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MonsterViewModel}">
<vw:MonsterView />
</DataTemplate>
</controls:TabControlEx.Resources>
</controls:TabControlEx>
In GBARomViewModel (child ViewModel, element of AvailableEditors)
public String CRC32
{
get
{
return _rom.CRC32;
}
set
{
if (value.Equals(_rom.CRC32))
{
return;
}
_rom.CRC32 = value;
OnPropertyChanged("CRC32");
}
}
Property binding in child View
Now this is a UserControl so I'll put its code as well after. Other properties at startup work such as LabelWidth and the LabelValue. Giving a default value to TextBoxValue in XAML also work.
<StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="10, 0, 0, 10" Width="300">
<dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomTitle}" TextBoxValue="{Binding Title}" />
<dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomGameCode}" TextBoxValue="{Binding GameCode}" />
<dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomRomSize}" TextBoxValue="{Binding RomSize}" />
<dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomCRC32}" TextBoxValue="{Binding CRC32}" />
<dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="200" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomMD5Checksum}" TextBoxValue="{Binding MD5Checksum}"/>
</StackPanel>
DefaultLabelBox.cs
<UserControl x:Name="uc">
<StackPanel>
<TextBlock Text="{Binding Path=LabelValue, ElementName=uc}"
Width="{Binding Path=LabelWidth, ElementName=uc}"/>
<Label Content="{Binding Path=TextBoxValue, Mode=OneWay, ElementName=uc}"
Width="{Binding Path=TextBoxWidth, ElementName=uc}"/>
</StackPanel>
</UserControl>
DefaultLabelBox.xaml.cs
public string TextBoxValue
{
get {
return (string)GetValue(TextBoxValueProperty);
}
set {
SetValue(TextBoxValueProperty, value);
}
}
public static readonly DependencyProperty TextBoxValueProperty =
DependencyProperty.Register("TextBoxValue", typeof(string), typeof(DefaultLabelBox), new PropertyMetadata(default(string)));
Control Template
<Style TargetType="dlb:DefaultLabelBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="dlb:DefaultLabelBox">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding LabelValue, RelativeSource={RelativeSource TemplatedParent}}"
MinWidth="20"
Width="{Binding LabelWidth, RelativeSource={RelativeSource TemplatedParent}}"
VerticalAlignment="Center"
FontFamily="Mangal"
Height="20"
FontSize="13"/>
<Label Content="{Binding TextBoxValue, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
BorderBrush="{StaticResource DefaultLabelBoxBorderBrush}"
BorderThickness="1"
Padding="1,1,1,1"
Background="{StaticResource DefaultLabelBoxBackgroundBrush}"
Foreground="{StaticResource DefaultLabelBoxForeground}"
MinWidth="60"
Height="20"
VerticalAlignment="Center"
FontFamily="Mangal"
Width="{Binding TextBoxWidth, RelativeSource={RelativeSource TemplatedParent}}"
FontSize="13"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I tried a few things but being new to MVVM I don't know if I have a DataContext issue of a binding one. Any help would be appreciated.
Edit: I made changes to some code to illustrate the working solution for me as well as adding the ControlTemplate I had forgot. I'm not sure if Mode=OneWay is mandatory in UserControl and ControlTemplate but it's working now so I'm leaving it as it is.
In order to make a binding like
<dlb:DefaultLabelBox ... TextBoxValue="{Binding CRC32, Mode=TwoWay}" />
work, the DefaultLabelBox needs to inherit its DataContext from its parent control (this is btw. the reason why a UserControl should never explicitly set its DataContext).
However, the "internal" bindings in the UserControl's XAML then need an explicitly specified Source or RelativeSource or ElementName.
So they should (for example) look like this:
<UserControl ... x:Name="uc">
<StackPanel>
<TextBlock
Text="{Binding Path=LabelValue, ElementName=uc}"
Width="{Binding Path=LabelWidth, ElementName=uc}"/>
<TextBox
Text="{Binding Path=TextBoxValue, Mode=TwoWay, ElementName=uc}"
Width="{Binding Path=TextBoxWidth, ElementName=uc}"/>
</StackPanel>
</UserControl>

WPF Radcalender customization day template format

I want to cretae wpf radcalender in my wpf mvvm application. I can't able to customize the calender day template. I want to this format.
I am using this code. But the text has been overwrite the date.
xmal:
<telerik:RadCalendar Margin="30" SelectableDateStart="2015-01-01" SelectableDateEnd="2015-01-31" >
<telerik:RadCalendar.DayTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="Test"/>
</StackPanel>
</DataTemplate>
</telerik:RadCalendar.DayTemplate>
</telerik:RadCalendar>
I've worked with the RadCalendar within a Winrt app and here how i fixed that (with some help from the official Docs)
First add a new converter that will return the Date Label like that:
public class CellModelConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var cellModel = value as CalendarCellModel;
return cellModel.Label;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
then in you Xaml add a textBlock to display the Cell Date using the previous converter :
<telerik:RadCalendar Margin="30" SelectableDateStart="2015-01-01" SelectableDateEnd="2015-01-31" >
<telerik:RadCalendar.DayTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="Test"/>
<TextBlock Text="{Binding Converter={StaticResource CellModelConverter}}" FontSize="13.333" VerticalAlignment="Bottom" Margin="6,0,0,4" />
</StackPanel>
</DataTemplate>
</telerik:RadCalendar.DayTemplate>
</telerik:RadCalendar>
and don't forget to add the Converter to the Static Resources:
<converters:CellModelConverter x:Key="CellModelConverter" />
--EDIT 1
the code above work fine in a Winrt application, for a WPF project you don't need a converter at all :
<telerik:RadCalendar Margin="30" SelectableDateStart="2015-01-01" SelectableDateEnd="2015-01-31" >
<telerik:RadCalendar.DayTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="Test"/>
<TextBlock Text="{Binding}" FontSize="13.333" VerticalAlignment="Bottom" Margin="6,0,0,4" />
</StackPanel>
</DataTemplate>
</telerik:RadCalendar.DayTemplate>
</telerik:RadCalendar>
--EDIT 2
the above code shows the "test" text in all the cells including WeekName, DayName ..., to fix that you can use either a Converter or a DataTrigger, here how to do it using a DataTrigger :
<telerik:RadCalendar Margin="30" SelectableDateStart="2015-01-01" SelectableDateEnd="2015-01-31" >
<telerik:RadCalendar.DayTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="Test">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Visibility" Value="Collapsed"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding DataContext.ButtonType,ElementName=tb}"
Value="Date">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBlock x:Name="tb" Text="{Binding Converter={StaticResource CellModelConverter}}" FontSize="13.333" VerticalAlignment="Bottom" Margin="6,0,0,4" />
</StackPanel>
</DataTemplate>
</telerik:RadCalendar.DayTemplate>
</telerik:RadCalendar>
Output:

Double Values in listView cell

I need to show old and new values in my ListView, when the record is changed.
I mean each cell should show new value and old value.
For now I'm doing so:
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding MyValue}"/>
<TextBlock Margin="7,0,0,0" Text="{Binding old.MyValue}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
for next column it will be:
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding MySecondValue}"/>
<TextBlock Margin="7,0,0,0" Text="{Binding old.MySecondValue}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
But I have 10 columns and this is not so interesting to make a lot of copy-paste for all 10 columns.
Any ideas how this can be done more compact and more better?
The ideal variant I want is something like this:
<GridViewColumn.CellTemplate>
<DataTemplate>
<MySpecialWhatever NewValueText="{Binding MyValue}" OldValueText="{Binding old.MyValue}" >
</MySpecialWhatever>
</DataTemplate>
</GridViewColumn.CellTemplate>
You can achieve your goal with custom UserControl like this.
DoubleValuesCell.xaml
<UserControl x:Class="WpfApplication1.DoubleValuesCell"
x:Name="root"
...>
<StackPanel>
<TextBlock Text="{Binding NewValue, ElementName=root}"/>
<TextBlock Margin="7,0,0,0" Text="{Binding OldValue, ElementName=root}"/>
</StackPanel>
</UserControl>
DoubleValuesCell.xaml.cs
public partial class DoubleValuesCell : UserControl
{
public static readonly DependencyProperty NewValueProperty = DependencyProperty.Register("NewValue", typeof(object), typeof(DoubleValuesCell));
public static readonly DependencyProperty OldValueProperty = DependencyProperty.Register("OldValue", typeof(object), typeof(DoubleValuesCell));
public object NewValue
{
get { return GetValue(NewValueProperty); }
set { SetValue(NewValueProperty, value); }
}
public object OldValue
{
get { return GetValue(OldValueProperty); }
set { SetValue(OldValueProperty, value); }
}
public DoubleValuesCell()
{
InitializeComponent();
}
}
XXXWindow.xaml
<Window x:Class="WpfApplication1.XXXWindow"
xmlns:local="clr-namespace:WpfApplication1"
...>
...
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<local:DoubleValuesCell NewValue="{Binding MyValue}" OldValue="{Binding old.MyValue}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
...
</Window>
UPDATE
You can collapse second control with DataTrigger.
<StackPanel>
<TextBlock Text="{Binding NewValue, ElementName=root}"/>
<TextBlock Margin="7,0,0,0" Text="{Binding OldValue, ElementName=root}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding OldValue, ElementName=root}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
The above code means the second TextBlock is collapsed if the binding source value (OldValue property) is null.

wpf listbox checkbox combination, really stuck

I am trying to link the checkbox and listview together, and then use the binding method to set the path to the object in order to set the check-box IsChecked status in the listbox.
List<Test1> datas = new List<Test1>();
var data = new Test1 {Key = 1, Value = "Hello", IsSelected= true};
datas.Add(data);
data = new Test1 {Key = 2, Value = "Hello2", IsSelected= false};
datas.Add(data);
What I need to happen is that if the checkbox is checked ( IsSelected is true ) then I need to populate with those values and then when I click and unclick the checkbox in the GUI I need it to also select the proper listeview item so I can get to the tag property.
This code below does not set the checkbox IsChecked.
<ListBox ItemsSource="{Binding}" Name="lstSwimLane" Width="225" Height="125" SelectionChanged="lstSwimLane_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected, Mode=TwoWay}"
Checked="CheckBox_Checked"
Unchecked="CheckBox_Unchecked" />
<TextBlock Text="{Binding Path=Value}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
What do I need to change in order to set the check box to the value in the object? I even tried INotifyChange etc...
Here is the object as well.
public class Test1 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _isChecked;
public int Key { get; set; }
public string Value { get; set; }
public bool IsSelected
{
get { return _isChecked; }
set
{
if(_isChecked != value)
{
_isChecked = value;
OnPropertyChanged("IsSelected");
}
}
}
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
I am new to all this wpf, and I am stumped.
Thanks.
Do you need this to work "three-way"? So setting any of the three properties
ListBoxItem.IsSelected
CheckBox.IsChecked
Item1.IsSelected
will affect both of the other properties? Unfortunately, there is no such Binding in WPF so you're gonna have to do a little extra work.
Update
As pointed out by Robert Rossney, a much better solution for this is to bind
ListBoxItem.IsSelected to Item1.IsSelected
CheckBox.IsChecked to ListBoxItem.IsSelected
Updated Xaml
<ListBox ItemsSource="{Binding}"
Name="lstSwimLane"
SelectionMode="Multiple"
Width="225" Height="125" SelectionChanged="lstSwimLane_SelectionChanged">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected"
Value="{Binding Path=IsSelected,
Mode=TwoWay}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Checked="CheckBox_Checked"
Unchecked="CheckBox_Unchecked"
IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}},
Path=IsSelected}">
</CheckBox>
<TextBlock Text="{Binding Path=Value}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In case anyone is interested, here is the markup for the listbox / combox. It will display horizontal.
Thank you all again for all you help as I greatly appreciated it.
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding}"
Name="lstSwimLane"
SelectionMode="Multiple"
Width="auto"
Height="auto"
Background="Transparent"
BorderThickness="0"
SelectionChanged="lstSwimLane_SelectionChanged" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Path=IsChecked, Mode=TwoWay}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="3,3,3,3">
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, Path=IsSelected}"
Checked="CheckBox_Checked"
Unchecked="CheckBox_Unchecked"
VerticalAlignment="Center"
Margin="0,0,4,0" />
<TextBlock Text="{Binding Value}" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Refreshing a binding that uses a value converter

I have a WPF UI that is bound to an object. I'm using a ValueConverter to convert a property to a specific image by a business rule:
public class ProposalStateImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var proposal = value as Proposal;
var basePath = "pack://application:,,,/ePub.Content;component/Images/General/Flag_{0}.png";
string imagePath;
if(proposal.Invoice != null)
{
imagePath = string.Format(basePath, "Good");
}
else
{
imagePath = string.Format(basePath, "Warning");
}
var uri = new Uri(imagePath);
var src = uri.GetImageSource(); //Extention method
return src;
}
}
The element is a TreeView where the image is on the 2nd level:
<TreeView x:Name="tree"
ItemsSource="{Binding People}"
SelectedItemChanged="OnTreeItemChanged">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type dmn:Person}"
ItemsSource="{Binding Proposals}">
<StackPanel Orientation="Horizontal" ToolTip="{Binding Path=Fullname}" Margin="3">
<Image Margin="5,0,5,0" Width="16" Height="16" Source="pack://application:,,,/ePub.Content;component/Images/General/Person_Active.png" />
<TextBlock Text="{Binding Path=Firstname}" />
<TextBlock Text="{Binding Path=Lastname}" Margin="5,0,0,0" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type dmn:Proposal}">
<StackPanel Orientation="Horizontal" Margin="3">
<Image x:Name="invoiceImage" Width="16" Height="16" Margin="5,0,5,0" Source="{Binding, Converter={StaticResource ProposalStateImageConverter}, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="{Binding DeliveryDate, Converter={StaticResource textCulturedDateConverter}}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
It is working fine, but later, when the object's state changes, I want to refresh the image and make the value converter reevaluate. How is this possible?
It looks like you're only using a single value inside the converter and you're just doing a simple switch between two values so you could instead just do this directly in XAML with a trigger. This method also switches to a Binding against the Invoice property so that any change notifications for that property will cause the Trigger to update.
<HierarchicalDataTemplate >
<StackPanel Orientation="Horizontal" Margin="3">
<Image x:Name="invoiceImage" Width="16" Height="16" Margin="5,0,5,0" Source="good.png"/>
<TextBlock ... />
</StackPanel>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding Invoice}" Value="{x:Null}">
<Setter TargetName="invoiceImage" Property="Source" Value="warning.png"/>
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
Assuming you can't use INotifyPropertyChanged because you're binding to the whole object, you need to call BindingExpression.UpdateTarget.
The slight subtlety is in getting hold of the binding expression. This requires you to have a fairly intimate knowledge of the view: as far as I know, the only way to do this is to call BindingOperations.GetBindingExpression, passing the control and property whose binding you want to update, e.g.:
BindingOperations.GetBindingExpression(myImage, Image.SourceProperty).UpdateTarget();

Resources