David David - 25 days ago 23
C# Question

How to force a WPF multi-binding to update when inside a ComboBox TemplateSelector?

As a preface, this question comes from extending this answer on how to make the selected item look different from the dropdown items in a ComboBox.

I'm trying to make my custom selected item use information from the ComboBox's Tag attribute. Since I need to use a converter, I' using a mutli-converter in order to be able to send "myself" to the conversion class. Thus, I have these two templates:

<DataTemplate x:Key="SelectedItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource SelectedConverter}">
<Binding RelativeSource="{RelativeSource Self}" />
<Binding StringFormat="This is here so it's called every time" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>

<DataTemplate x:Key="DropDownItemsTemplate">
<TextBlock Grid.Column="0" Text="{Binding}" Margin="0,0,10,0" VerticalAlignment="Center" />
</DataTemplate>


And my converter:

class SelectedConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var retVal = "";

// The value passed in should be the TextBox in the ComboBoxItem
var element = values[0] as DependencyObject;

// Try to find it's parent RoleComboBox
while (element != null && !(element is ComboBox))
{
element = VisualTreeHelper.GetParent(element);
}

// If we didn't find anything, return an empty string
if (element == null) return retVal;

// Otherwise, get the role from the ComboBox
var tag = (element as ComboBox).Tag;

if (tag?.ToString().Contains("Custom") ?? false)
{
retVal = "Custom Stuff ";
}

return retVal;
}

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}


XAML for the ComboBox

<ComboBox Name="myComboBox" Grid.Column="0" Tag="My Custom Tag" ItemTemplateSelector="{StaticResource CbTemplateSelector}">
<ComboBox.Items>
<ComboBoxItem Content="A" />
<ComboBoxItem Content="B" />
<ComboBoxItem Content="C" />
<ComboBoxItem Content="D" />
<ComboBoxItem Content="E" />
</ComboBox.Items>
</ComboBox>


And all of this nicely produces this:

Screen Snip

Now, what I need to do is have the "Custom Stuff C" change to something else when I change the Tag property. So far, it will only change when I change the selected item, but I need it to change without the user having to change it to D, and then back to C.

From reserach, I've tried forcing the updating with the below code, but it does not work.

myComboBox.GetBindingExpression(ComboBox.ItemTemplateProperty)?.UpdateTarget();
myComboBox.GetBindingExpression(ComboBox.ItemTemplateSelectorProperty)?.UpdateTarget();
myComboBox.GetBindingExpression(ComboBox.SelectionBoxItemTemplateProperty)?.UpdateTarget();
myComboBox.GetBindingExpression(ComboBox.TemplateProperty)?.UpdateTarget();
myComboBox.UpdateLayout();
myComboBox.OnSelectionChanged(new SelectionChangedEventArgs(SelectionChangedEvent, new List<bool> { }, new List<bool> { }));


Note that I do actually have a custom control that inherits from
ComboBox
, so I do have access to protected methods (which is how the last line of code above works). I just extracted that part out in order to give a small subset of reproducable code.

At this point, I'm not sure what else to try. So how can I force my binding to update, based on how I'm doing it?

Evk Evk
Answer

Instead of binding to self and then searching for parent combobox in code, just use built-in methods to do that:

<TextBlock.Text>
    <MultiBinding Converter="{StaticResource SelectedConverter}">
        <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}" Path="Tag" />
        <Binding StringFormat="This is here so it's called every time" />
    </MultiBinding>
</TextBlock.Text>

As a bonus, if you bind directly to ComboBox.Tag - whole multibinding will be refreshed when that Tag changes.

Comments