I managed to solve this:
- Include a converter on the resources node of the
ItemsPanel
which wraps eachListBoxItem
(or somewhere else within the repeated part of the tree), so there is one converter instance per item. - Add a property (
Host
in my example) to the converter for the item each converter attaches to and initialise it somehow* as eachListBoxItem
is created. - Bind
IsChecked
to theViewModel
property you need to have populated using the converter.
*I tried the Host
property as a DP with binding to the DataContext
(which should be one member of the collection the ListBox
is bound to at the point at which the converter is located), but I simply could not get this to work for my nested ListBoxes
. I resorted to the Initialized
event of the containing element and some code behind (which did allow Host
to revert to an ordinary property; the DP was overkill but had been necessary for binding).
The DataTemplate
:
<StackPanel Initialized="StackPanel_Initialized">
<StackPanel.Resources>
<!-- ExlcusionRadioConverter.Host is initialised in code behind -->
<local:ExclusionRadioConverter x:Key="ExclusionRadio" />
</StackPanel.Resources>
<RadioButton
GroupName="Exclusion"
IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}, Mode=FindAncestor}, Converter={StaticResource ExclusionRadio}, Path=DataContext.ExclusionCriterion}" />
<Label Content="{Binding Description}" />
</StackPanel>
The Panel
Initialized
event (NB this answer originally had this code in the Loaded
event handler, but this was causing some problems):
private void StackPanel_Initialized(object sender, EventArgs e)
{
StackPanel panel = (StackPanel)sender;
ExclusionRadioConverter converter = (ExclusionRadioConverter)panel.FindResource("ExclusionRadio");
converter.Host = panel.DataContext as OptionListMember;
}
The Converter:
[ValueConversion(typeof(object), typeof(bool))]
public class ExclusionRadioConverter : IValueConverter
{
public OptionListMember Host { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ReferenceEquals(value, Host);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)value) ? Host : Binding.DoNothing;
}
}