C# datagridview 行切换

C# datagridview row toggle

我希望获得有关 datagridview 的帮助。

我想要做的是当我点击 receiptView (datagridview) 列表中的一个项目(一行)时,它应该切换该行的选择(即如果它已经被选中,它会取消选择,反之亦然) .

到目前为止,我已经找到了这段代码,但是,这并不总是有效:

    private void receiptView_CellClick(object sender, DataGridViewCellEventArgs e)
    {
        if (!_selectionChanged)
        {
            //do other stuff here
            receiptView.ClearSelection();
            _selectionChanged = true;
        }
        else
        {
            //do other stuff here
            _selectionChanged = false;
        }
    }

    private void receiptView_SelectionChanged(object sender, EventArgs e)
    {
        _selectionChanged = true;
    }

仅供参考

问题

发生的事情是,当我 select/deselect 非常快速地(类似于双击)一行时,它最终会变成 "de-syncing" 实际选择状态和 _selectionChanged。我的意思是:

  1. 当一行被选中时,它会highlight/select那一行(正常)
  2. 现在再次选择同一行时(在第一次单击后快速连续)它不会取消选择该行,但似乎在错误的 "do other stuff here" 被执行时保持选中状态(不正常,应该已取消选择)
  3. 第三次单击取消选择(如果是第 2 步,则正常,应该只有两步)

这不会在我每次快速点击时发生,但它会定期发生(大约每 second/third 个点击对)。如果我缓慢而有意地点击,它永远不会发生。

理论

我真的不知道为什么会发生这种情况,但我怀疑由于点击 1 和 2 发生的时间非常接近,第二次点击的事件触发而第一次点击仍然 运行,导致第一次点击单击以将 _selectionChanged 更改为 false,然后单击第二个将其更改回 true,同时保持该行处于选中状态。但是,由于我没有创建任何新线程,而且这是主 UI 线程上的全部 运行,它不应该是序列化执行吗?即点击 1 的事件触发,完成后,点击 2 的事件触发?

问题

如果您知道更简单的方法(在数据网格视图中切换),或者可以看到错误 and/or 有解决方案,我们将不胜感激。

谢谢。

TL;DR

您的第二次点击被 CellDoubleClick 事件劫持。使用来自 CellClick 事件处理程序的相同代码订阅它。
- 请参阅答案末尾的解决方案

调试说明

为了演示发生了什么,我们将以下输出添加到您的方法中,然后 运行 通过您在 OP 中描述的场景。

private void receiptView_CellClick(object sender, DataGridViewCellEventArgs e)
{
    if (!_selectionChanged)
    {
        Console.WriteLine("Cleared.......");
        receiptView.ClearSelection(); // NOTE: Triggers SelectionChanged event.
        _selectionChanged = true;
    }
    else
    {
        Console.WriteLine("Highlighted...");
        _selectionChanged = false;
    }
}

private void receiptView_SelectionChanged(object sender, EventArgs e)
{
    Console.WriteLine("Changed!");
    _selectionChanged = true;
}

当前未选择任何行,以下测试、预期结果和实际结果:

+======================+=================+================+=================+
|       ACTION         | EXPECTED OUTPUT | ACTUAL OUTPUT  |   GUI RESULTS   |
+======================+=================+================+=================+
| Single-click 1st row | Changed!*       | Changed!*      | Row Highlighted |
|                      | Highlighted...  | Highlighted... |                 |
+----------------------+-----------------+----------------+-----------------+
| Double-click 1st row | Cleared.......  | Cleared....... | Row Highlighted |
|                      | Changed!^       | Changed!^      |                 |
|                      | Changed!*       | Changed!*      |                 |
|                      | Highlighted...  |                |                 |
+----------------------+-----------------+----------------+-----------------+
| Single-click 1st row | Cleared.......  | Highlighted... | Row Highlighted |
|                      | Changed!^       |                | (Still...?)     |
+----------------------+-----------------+----------------+-----------------+

* SelectionChanged 在后面的代码中触发;未选择的行现在被选中。
^ SelectionChanged 由调用 ClearSelection() 触发。

分析:您的第一次点击会触发 SelectionChanged 并按预期突出显示该行。接下来,您 双击 该行,认为第一次单击会清除该行(您将手动触发 SelectionChanged),然后第二次单击(再次触发SelectionChanged 在背景中)重新突出显示它。这就是我们出错的地方。

双击,第一次点击按预期触发清除和SelectionChanged事件。我们甚至看到了 SelectionChanged 的第二个预期打印输出,但没有看到 CellClick 事件的突出显示输出。为什么?

当您点击row/cell一次时,CellClick被触发。当您快速连续点击两次时,第一次点击触发 CellClick,第二次点击触发 CellDoubleClick。因此,您的第一次点击会按预期命中您的代码。第二次点击永远不会点击您的代码,除非在后台未选中的行仍然被选中,因此 SelectionChanged 仍然会被第二次触发。从这一点开始,您的 _selectionChanged 标志关闭 - 导致更多不准确的结果。

解决方案:订阅CellDoubleClick事件并在其中执行与CellClick中相同的代码就可以了。

private void receiptView_CellClick(object sender, DataGridViewCellEventArgs e)
{
    this.DoStuff();
}

private void receiptView_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
    this.DoStuff();
}

private void DoStuff()
{
    if (!_selectionChanged)
    {
        //do other stuff here
        receiptView.ClearSelection();
        _selectionChanged = true;
    }
    else
    {
        //do other stuff here
        _selectionChanged = false;
    }
}