【问题标题】:WinForms ListView selection changes randomly when assigning selected index from code从代码中分配选定索引时,WinForms ListView 选择会随机更改
【发布时间】:2017-10-28 09:05:00
【问题描述】:

我遇到了非常奇怪的 WinForms 行为,这似乎是一个框架错误。

情况:

在一个空白表格上,将 2 个ListViews 并排放置。禁用HideSelection 以在控件没有焦点时使选择可见。 View 属性(细节,大图标)似乎无关紧要,但我发现细节更容易点击(在这种情况下,添加一列)。

MultiSelect 是否启用也无关紧要。

(不涉及button1)

在表单构造函数中,将一些项目放入列表中:

this.listView1.Items.Add("item1-1");
this.listView1.Items.Add("item1-2");
this.listView1.Items.Add("item1-3");

this.listView2.Items.Add("item2-1");
this.listView2.Items.Add("item2-2");

现在,当用户在listView1 中选择某些内容时,我们希望在listView2 中选择具有相同列表索引的项目。例如。用户在左侧列表视图中选择item1-1,我们要在右侧列表视图中选择item2-1,依此类推。

private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
    // (*1)
    this.listView2.SelectedIndices.Clear();

    if (this.listView1.SelectedIndices.Count != 0 && this.listView1.SelectedIndices[0] < this.listView2.Items.Count)
    {
        // (*1) can also be put here; it makes no difference with regards to the bug (it makes a functional difference though)
        this.listView2.SelectedIndices.Add(this.listView1.SelectedIndices[0]);
    }
}

错误有时会出现在以下场景中(如果我非常快速地执行这些步骤(例如,总共不到一秒),我只能重现):

  • 单击左侧列表中的第 1 项(从 1 开始计数)
  • 自动选中右侧列表中的第 1 项
  • 单击右侧列表中的第 1 项
  • 单击左侧列表中的第 2 项
  • 右侧列表中的第 2 项自动被选中
  • 单击右侧列表中的第 2 项
  • 单击左侧列表中的第 1 项
  • 自动选中右侧列表中的第 1 项
  • 等一下(大约 300 毫秒)
  • 右侧列表中的第 2 项被自动选中(不正确!)

更笼统地说(这是我的猜想):

  • 点击左侧列表中的项目
  • 单击右侧列表中的项目刚刚被自动选中
  • 快速在左侧列表中选择一个不同的
  • 会在正确的列表中自动选择正确的项目片刻
  • 选择会在短暂延迟后跳回上一项

我还可以使用此事件处理程序观察右侧列表中的无效选择更改,但我无法从断点中获得任何用途 - 我似乎没有任何可疑之处。在错误的情况下,调用堆栈只包含框架内部方法(除了最顶层的框架,当然是事件处理程序),所以虚假的选择变化来自框架本身。

private void listView2_SelectedIndexChanged(object sender, EventArgs e)
{
    if (this.listView1.SelectedIndices.Count == 0 || this.listView2.SelectedIndices.Count == 0)
    {
        return;
    }

    if (this.listView2.SelectedIndices[0] != this.listView1.SelectedIndices[0])
    {
        // Unless the user MANUALLY selects a DIFFERENT item in the right list view, this should never happen, but it does!
        int j = 5; // BREAKPOINT HERE
    }
}

这个错误并不总是发生,而且似乎对时间很敏感,但经过一些“练习”后,我现在可以在大约 50% 的时间内重现它。

我的猜测是那里有一些非常愚蠢的故障保护机制。当用户单击一个项目时,WinForms 只是决定稍后检查该项目是否真的被选中,如果它没有被选中,它会再次选择它(即使在此期间以编程方式更改了选择)。但仅此而已,因为除非您在选择自动更改后立即明确单击自动选择的项目,否则不会发生该错误。

谁能重现这个,我该如何解决这个问题?

这发生在我的 Win 10 x64 上,带有 .NET 框架 4.5.2 和 4.7

【问题讨论】:

  • 这和Winforms没有太大关系,ListView是操作系统提供的,已经经历了20年的appcompat hack。该选择是不可靠的,您已经通过测试 SelectedIndices.Count == 0 发现了这一点。您同样希望避免 SelectedIndices.Clear()。
  • @HansPassant 这听起来有点像你在建议我应该避免使用ListView 开始(或至少避免以编程方式选择事物)并以不同的方式设计 GUI。事实上,这就是我目前正在尝试做的事情。但是我还是觉得这个问题很奇怪,因为除非它特别与我的机器有关,否则这意味着每个从代码中选择项目的人迟早都会遇到这个问题?

标签: c# winforms listview


【解决方案1】:

事实证明,您还必须通过ListViewItem.FocusedListView.FocusedItem 设置“焦点项目”,例如:

this.listView2.SelectedIndices.Clear();

if (this.listView1.SelectedIndices.Count != 0 && this.listView1.SelectedIndices[0] < this.listView2.Items.Count)
{
   var item_we_want_to_select = this.listView2.Items[this.listView1.SelectedIndices[0]];
   item_we_want_to_select.Selected = true;
   item_we_want_to_select.Focused = true;
}

然而,这一切听起来很可疑,我不会再将 ListView 用于这样的 GUI。我不能轻松地告诉(也不引用文档)究竟做什么,它是否是正确的方法,以及它是否有任何意外的副作用。在我看来,最初的观察结果似乎是一个 Windows 错误,这是一种可能有效也可能无效的解决方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-21
    • 1970-01-01
    • 2014-04-28
    • 2013-10-27
    • 2019-02-06
    • 2021-05-06
    • 1970-01-01
    相关资源
    最近更新 更多