jason_the2nd
jason_the2nd

Reputation: 41

listView ItemSelectionChanged 'Is Selected' not working as expected?

I'm having a problem with a ListView ItemSelectionChanged event being called twice. From searching I've found it's because the event is being called when an item is selected and again when an item is deselected. I only want it to fire when an item is selected, not when it's deselected. The solution seems to have an if(e.IsSelected) at the beginning of the event so it's only called when an item is selected, but that's not working for me in this case. Here's the basic structure of my code:

private void SelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
    if (e.IsSelected)
    {
        DialogResult GetDialogResult = 
               MessageBox.Show("Keep this item selected?", 
               "Keep Selected", 
               MessageBoxButtons.YesNo);

        if (GetDialogResult == DialogResult.No)
            listView1.SelectedItems[0].Selected = false;
    }
}

MultiSelect is disabled so the selected item index will always be 0. My problem is I want the selected item to be deselected if the DialogResult is No, but when you click no, the SelectionChanged event is firing again, and e.IsSelected is apparently still true, so the Dialog Box pops up a second time. I imagine it has something to do with the fact that the first event hasn't completely finished executing when the item is being deselected, but I'm not sure how to solve that and make it so the Dialog Box only shows up once. Any suggestions?

Edit: Something else I've tried now is instead of Deselecting the item in the listBox, clearing all items in the listbox and recreating them. If the items are all cleared, the dialog box doesn't come up a second time. However if the items are immediately recreated, the same item becomes selected again and the dialog box still comes up the 2nd time.

Upvotes: 3

Views: 2943

Answers (3)

jason_the2nd
jason_the2nd

Reputation: 41

I've found a solution that works. Calling a function to deselect the item asynchronously in a separate thread seems to have solved the issue for me. Without doing this, the second SelectionChanged event is called and has to finish executing before the first one can finished executing, which apparently causes the problem I was seeing. Here is the code I came up with that has worked:

delegate void DeselectDelegate();
public void DeselectItem()
{
    if (this.listView1.InvokeRequired)
    {
        DeselectDelegate del = new DeselectDelegate(DeselectItem);
        this.Invoke(del);
    }
    else
    {
        listView1.SelectedItems[0].Selected = false;
    }
}
private void SelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
    if (e.IsSelected)
    {
        DialogResult GetDialogResult =
                MessageBox.Show("Keep this item selected?",
                "Keep Selected",
                MessageBoxButtons.YesNo);
        if (GetDialogResult == DialogResult.No)
        {
            Thread thread = new Thread(DeselectItem);
            thread.Start();
        }
    }
}

Upvotes: 1

Jeff B
Jeff B

Reputation: 9002

ItemSelectionChanged (like several other events on other controls) gets fired when you change the value from the program as well. So when you run this line of code, that counts as a "selection changed"...

listView1.SelectedItems[0].Selected = false;

One way to handle this is by setting a flag:

private bool _programChangingSelection;

private void SelectionChanged(object sender, EventArgs e)
{
    if (_programChangingSelection)
    {
       _programChangingSelection = false;
       return;
    }

    if (e.IsSelected)
    {
        DialogResult GetDialogResult = 
               MessageBox.Show("Keep this item selected?", 
               "Keep Selected", 
               MessageBoxButtons.YesNo);

        if (GetDialogResult == DialogResult.No)
        {
            _programChangingSelection = true;
            listView1.SelectedItems[0].Selected = false;
        }
    }
}

Although, personally I think the add/remove handler method is probably a bit more elegant.

Upvotes: 2

Baldrick
Baldrick

Reputation: 11840

If the behaviour you want is:

  1. User clicks an item
  2. Dialog pops up
  3. If they click 'no' on the popup, selection doesn't happen
  4. If they click 'yes', it does

then one approach is to unhook your event handler before you set the Selected property to false, then rehook afterwards, as follows:

if (GetDialogResult == DialogResult.No)
{
    listView1.ItemSelectionChanged -= SelectionChanged;
    e.Item.Selected = false;
    listView1.ItemSelectionChanged += SelectionChanged;
}

This will stop the event handler from triggering again until your operation has completed.

Upvotes: 2

Related Questions