Trying to find an answer to this question (listbox items showing either a checkbox or a radio button depending on a viewmodel property), I came across a very weird behavior.
If I use DataTrigger
to set the content of a ContentControl
in the DataTemplate
of an item in the list box, only the last item seems to work properly.
Why only the last item gets a checkbox/radio button?
Here is the XAML
code, where the window view model property AllowMultiItem
defines if I want checkboxes or radio buttons:
<Window x:Class="TestSpace.MyWindow"
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:TestSpace"
mc:Ignorable="d"
Title="MyWindow" Height="450" Width="800">
<Grid>
<!-- the list box -->
<ListBox x:Name="MyList" ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:MyListItem}">
<StackPanel Orientation="Horizontal">
<!-- this content control selects between check and radio-->
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<!-- trigger that creates checkbox -->
<DataTrigger Value="True"
Binding="{Binding Path=DataContext.AllowMultiItem, ElementName=MyList}">
<Setter Property="Content">
<Setter.Value>
<CheckBox IsChecked="{Binding IsChecked}"/>
</Setter.Value>
</Setter>
</DataTrigger>
<!-- trigger that creates radio button -->
<DataTrigger Value="False"
Binding="{Binding Path=DataContext.AllowMultiItem, ElementName=MyList}">
<Setter Property="Content">
<Setter.Value>
<RadioButton IsChecked="{Binding IsChecked}" GroupName="RadioGroup"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
<!-- item text -->
<TextBlock Text="{Binding Text}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
When I do this, either when AllowMultiItem
is true or false, only the last item in the list box receives a checkbox/radio button.
Why does this happen?? How can I solve it?
Other codes – may not be relevant
Not sure it’s useful, but if you want to inspect the window view model and the items viewmodel, or if you want to fully reproduce my test, the codes are below. I made this test the simplest possible so no unrelated bugs affect the result.
Window view model (with static function to open window)
public class MyViewModel
//didn't feel the need for INotifyPropertyChanged since nothing here is supposed to change
{
public List<MyListItem> Items { get; private set; }
public bool AllowMultiItem { get; private set; }
public MyViewModel(List<MyListItem> items, bool allowMultiItem)
{
Items = items;
AllowMultiItem = allowMultiItem;
}
public static void ShowWindow(bool allowMultiItem)
{
//just creating items with names from 1 to 10
List<MyListItem> items = Enumerable.Range(1, 10)
.Select(index=> new MyListItem("Item " + index.ToString()))
.ToList();
//create viewmodel
MyViewModel vm = new MyViewModel(items, allowMultiItem);
//create and show window
MyWindow window = new MyWindow();
window.DataContext = vm;
window.ShowDialog();
}
}
Item view model
public class MyListItem : INotifyPropertyChanged
{
private bool _checked;
private string _text;
public event PropertyChangedEventHandler PropertyChanged;
public string Text { get { return _text; } set { SetText(value); } }
public bool IsChecked { get { return _checked; } set { SetChecked(value); } }
public MyListItem(string text)
{
SetText(text);
SetChecked(false);
}
private void SetChecked(bool value)
{
if (value == _checked) return;
_checked = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsChecked"));
}
private void SetText(string value)
{
if (value == _text) return;
_text = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Text"));
}
}
Test cases simply call MyViewModel.ShowWindow(true)
and MyViewModel.ShowWindow(false)
A few things I tried
- Tried to add
INotifyPropertyChanged
to theMyViewModel
and fire the change toAllowMultiItem
afterOnContentRendered
nothing changed - Tried to change the
DataTrigger
bindings toRelativeSource
targeting the Window: curiously, now the first item gets the box, but only the first