listView的ItemSelectionChanged事件中'Is Selected'属性没有按预期工作?

3
我在处理 ListView ItemSelectionChanged 事件时遇到了问题,会被调用两次。经过搜索,我发现这是因为当一个项目被选中和取消选中时,该事件会被调用两次。我只希望在选择项目时触发,而不是取消选择时。解决办法似乎是在事件开头加上 if(e.IsSelected) ,这样只有在选择项目时才会调用事件,但在我的情况下这种方法并没有奏效。下面是我的代码基本结构:
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;
    }
}

多选功能已禁用,所选项索引将始终为0。我的问题是,如果DialogResult为“否”,我希望所选项目被取消选择,但是当单击“否”时,SelectionChanged事件再次触发,并且e.IsSelected显然仍然为真,因此对话框会再次弹出。我想这可能与第一个事件在取消所选项目时尚未完全执行完有关,但我不确定如何解决这个问题并使对话框只显示一次。 有什么建议吗?
编辑:我现在尝试的另一种方法是,不是取消选择列表框中的项目,而是清除列表框中的所有项目,然后重新创建它们。如果所有项目都被清除,则对话框不会再次弹出。 但是,如果立即重新创建项目,则同一项目将再次被选择,并且对话框仍然会第二次弹出。
3个回答

2
如果您想要的行为是:
  1. 用户单击一个项目
  2. 对话框弹出
  3. 如果他们在弹出窗口上单击“否”,则不会发生选择
  4. 如果他们单击“是”,则会发生选择
那么一种方法是在将Selected属性设置为false之前取消挂钩事件处理程序,然后重新挂钩,如下所示:
if (GetDialogResult == DialogResult.No)
{
    listView1.ItemSelectionChanged -= SelectionChanged;
    e.Item.Selected = false;
    listView1.ItemSelectionChanged += SelectionChanged;
}

这将阻止事件处理程序再次触发,直到您的操作完成。

你可以尝试使用事件参数对象的Item属性,而不是自己使用listView1.SelectedItems[0]来获取它吗?请参见上面编辑过的示例。 - Baldrick
即使进行了更改,仍然会出现两个对话框。 - jason_the2nd

2

ItemSelectionChanged 事件(就像其他控件上的一些事件一样)在程序中改变值时也会触发。因此,当您运行此代码行时,它将被视为“选择已更改”...

listView1.SelectedItems[0].Selected = false;

处理此问题的一种方法是设置一个标志:

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;
        }
    }
}

虽然个人认为添加/删除处理程序的方法可能更加优雅。


这样做确实可以使对话框第二次不弹出,但现在该项实际上并没有被取消选择。这个问题让我更加困惑。 - jason_the2nd
如果您直接复制了Jeff的代码,则需要相应地放置大括号:if (GetDialogResult == DialogResult.No) { _programChangingSelection = true; listView1.SelectedItems[0].Selected = false; } - Dev Leader
嗯,我想我只是复制了额外的代码,所以在测试时括号没问题。它没有弹出第二个对话框,但项目没有被取消选择。 - jason_the2nd
if (e.IsSelected) 需要改成 if (!e.IsSelected) 吗?也就是说,用户取消选择了它,因此它不再被选中了吗? - Jeff B
不,我希望当选择某些内容时弹出对话框,但是在取消选择时不要弹出,这是正确的。问题似乎是第二个事件发生得太快了,程序仍然认为在触发时有内容被选中。当我按照你建议的使用断点来减慢速度时,它就能像我想要的那样工作,但是没有这些断点,它又会出现双重对话框。 - jason_the2nd
显示剩余3条评论

1

我找到了一个可行的解决方案。在单独的线程中异步地调用取消选择项的函数似乎已经为我解决了这个问题。如果不这样做,第二个SelectionChanged事件会被调用并且必须执行完成,然后第一个才能执行完成,这显然会引起我看到的问题。以下是我想出的代码,它已经起作用:

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();
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接