简体   繁体   中英

C# datagridview row toggle

I'm looking to get some help with the datagridview.

What I want to do is when I click an item (a row) in the receiptView (datagridview) list, it should toggle that row's selection (ie If it was already selected, it would deselect and vice-versa).

So far I've found this code, however, this doesn't work all the time:

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

FYI

  • When a row is clicked and no row is already selected OR a row is already selected and a new row is now selected it triggers the events in this following order:

    1. receiptView_SelectionChanged
    2. receiptView_CellClick
  • When a row is clicked and it is already selected it only triggers the receiptView_CellClick event

Problem

What's happening is that when I select/deselect a row really quickly (akin to like a double click) it would end up "de-syncing" the actual selection state and _selectionChanged . What I mean by this is that:

  1. When a row is selected once it'll highlight/select that row (normal)
  2. When the same row is now selected again (in quick succession to the first click) it doesn't deselect the row, but seems to stay selected while the wrong "do other stuff here" gets exectuted (not normal, should have deselected)
  3. On the third click it deselects (normal if it were step 2, should only be two steps)

This doesn't happen every time I click quickly but it does happens regularly (about every second/third click-pair). It NEVER happens if I click slowly and deliberately.

Theory

I don't really know why this is happening but I suspect that as Clicks 1 and 2 happen very close to one another, the second click's event triggers while the first's is still running, causing the first click to change the _selectionChanged to false, and the second to change it back to true, while keeping the row selected. However, since I'm not creating any new threads and this is all running on the main UI thread, should it not be a serialized execution? ie Click 1's events trigger, and once it has completed, click 2's events trigger?

Question

If any of you know an easier way to do this (toggling in a datagridview), or can see the bug and/or have a solution it would be greatly appreciated.

Thanks.

TL;DR

Your second click is being hijacked by the CellDoubleClick event. Subscribe to it using the same code from your CellClick event handler.
- See the Solution at the end of the answer.

DEBUGGING EXPLANATION

To demonstrate what's happening, we add the following outputs to your methods, then run through the scenario you described in the 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;
}

With no rows currently selected, the following test, expected results, and actual results:

+======================+=================+================+=================+
|       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 triggered in behind code; non-selected row is now selected.
^ SelectionChanged triggered by call to ClearSelection().

Analysis : Your first click triggers SelectionChanged and highlights the row as expected. Next, you double-click the row, thinking the first click will clear the row (which you will manually cause to trigger SelectionChanged ), and the second click (again, triggering SelectionChanged in the background) re-highlights it. This is where we go wrong.

From the double-click , the first click triggers the clear and SelectionChanged event as expected. We even see the second expected printout from SelectionChanged , but not the highlight printout from the CellClick event. Why?

When you click on a row/cell once, CellClick is triggered. When you click twice in quick succession, CellClick triggers for the first click and CellDoubleClick triggers for the second click. Thus, your first click hits your code as expected. The second click never hits your code, except that in the background the non-selected row was still selected and thus SelectionChanged is still triggered a second time. From this point on, your _selectionChanged flag is off - causing more inaccurate results.

Solution : Subscribe to the CellDoubleClick event and perform the same code in it as in CellClick and you'll be fine.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM