I want to display some custom content (using datatemplate) on button click:
<ContentControl x:Name="content" />
<Button Content="Test" Click="button_Click" />
Button shows/hides content like this
VM _vm = new VM();
void button_Click(object sender, RoutedEventArgs e) =>
content.Content = content.Content == null ? _vm : null;
Here is datatemplate:
<DataTemplate DataType="{x:Type local:VM}">
<ListBox ItemsSource="{Binding Items}" SelectionChanged="listBox_SelectionChanged">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</DataTemplate>
Event handler:
void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e) =>
Title = "Items: " + ((ListBox)sender).Items.Count;
Viewmodel:
public class VM
{
public List<Item> Items { get; } = new List<Item> { new Item(), new Item(), new Item() };
}
public class Item
{
public bool IsSelected { get; set; }
}
The problem: when datatemplate is unloaded then SelectionChanged
for ListBox
event is rised with no items.
I do not want this event. I don't want to see "Items: 0" after selecting something and unloading datatemplate.
Question: what is happening and how can I prevent this from happening?
Note: this is very short and simplified MCVE, i.e. not everything is pretty, though there are key points: datatemplate with ListBox
inside, which uses IsSelected
binding and I need to get rid from that SelectionChanged
event at unloading.
Call stack:
This is working exactly as designed. You made a selection by clicking an item in the list box. When the template is unloaded, the ItemsSource
binding is disconnected, and the items source becomes empty. At that point, the current selection is no longer valid (the item doesn't exist in the items source), so the selection is cleared. That's a selection change: the selection went from something to nothing. The event is expected to be raised under these circumstances.
It's rarely necessary to subscribe to SelectionChanged
. It's usually better to bind the SelectedItem
to a property on your view model. Whenever the selection changes, that property will be updated. Instead of responding to the SelectionChanged
event, you can respond to that property changing.
This approach nicely avoids the issue you're seeing. Once the template is unloaded, the SelectedItem
binding will be disconnected, so your view model won't be updated anymore. Consequently, you won't see that final change when the selection is cleared.
If your ListBox
supports multiple selections, you can continue subscribing to SelectionChanged
. However, don't query listBoxItems
; instead, scan through _vm.Items
and see which items have IsSelected
set to true
. That should tell you the actual selection, and the results should not be affected by the template being unloaded.
You can also determine that the template was unloaded by checking whether (sender as ListBox)?.ItemsSource
is null in your handler. However, this should not be necessary.