WPF ListView select/deselect under some conditions

173 views Asked by At

I'm using a WPF ListView to show and select/deselect some items but there are some conditions that user cannot select/deselect an item and I should handle it in the code behind. I tried to use ListView.SelectionChanged event to handle it but the problem is that when I change the selected item in the code behind (select it again if it was deselected or the other way), the event triggers again and I don't want that.

What is the best way to set conditions on select/deselect ListView item?

I tried to solve it using ListView.PreviewMouseLeftButtonDown event instead of ListView.SelectionChanged. But I wanted to know if there is a better way?

1

There are 1 answers

0
AceGambit On

There might be other WPF controls (especially third party ones) that can handle this a little more gracefully. Additionally you can hook into the style of the ListView to alter the mouse-over behavior.

As a crude approach; however, I was able to accomplish what you're looking for by using the SelectionChanged event to effectively "undo" any selections we deem invalid.

First a simple ListView in xaml:

<ListView 
    x:Name="ItemsLv" 
    SelectionChanged="ItemsLv_SelectionChanged" 
    SelectionMode="Single" />

The SelectionMode being Single is important here as the approach for undoing a selection when multiple items are selected is more complicated.

Then, an object to represent our list items with the ToString() overload implemented so we don't have to fiddle with data templates and binding.

public class MyListViewItem
{
    public string Text { get; set; }
    public bool IsSelectable { get; set; }

    public override string ToString()
    {
        return Text;
    }
}

This object just represents a string (our data) paired with a boolean of whether or not we want this item to be selectable.

Then, a quick little setup to populate the list with a few item for testing:

public MainWindow()
{
    InitializeComponent();

    var items = new List<MyListViewItem>
    {
        new() { Text = "Item One", IsSelectable = true },
        new() { Text = "Item Two", IsSelectable = true },
        new() { Text = "Item Three", IsSelectable = false },
        new() { Text = "Item Four", IsSelectable = true }
    };
    
    ItemsLv.ItemsSource = items;
}

And finally, the "magic". The SelectionChanged event handler:

private void ItemsLv_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    // if this event wasn't caused by a new item that wasn't already
    // selected being selected, don't do anything extra
    if (e.AddedItems.Count <= 0)
    {
        return;
    }

    // get the newly selected item and cast it to a MyListViewItem so we can inspect the IsSelectable property
    var selectedItem = (MyListViewItem)ItemsLv.SelectedItem;
    
    // if it's a selectable item, we don't need to intervene
    if (selectedItem.IsSelectable)
    {
        return;
    }

    // we made it this far that means we tried to select an item that should NOT be selectable

    // if the new selected item caused us to UNselect an old item, put the selection back
    if (e.RemovedItems.Count > 0)
    {
        ItemsLv.SelectedItem = e.RemovedItems[0];
    }
    // otherwise (the first selection ever?) just set selection back to null
    else
    {
        ItemsLv.SelectedItem = null;
    }
}

Hopefully the code comments in there make it clear what's going on.