views:

2278

answers:

7

I have a DataGridView whose DataSource is a DataTable. This DataTable has a boolean column, which is interpreted as a checkbox in the DataGridView.

employeeSelectionTable.Columns.Add("IsSelected", typeof(bool));
...
employeeSelectionTable.RowChanged += selectionTableRowChanged;
dataGridViewSelectedEmployees.DataSource = employeeSelectionTable;

...

private void selectionTableRowChanged(object sender, DataRowChangeEventArgs e)
{
    if ((bool)e.Row["IsSelected"])
    {
        Console.Writeline("Is Selected");
    }
    else
    {
        Console.Writeline("Is Not Selected");
    }
    break;
}

When the user single-clicks on a checkbox, it gets checked, and selectionTableRowChanged will output "Is Selected."

Similarly, when the user checks it again, the box gets cleared, and selectionTableRowChanged outputs "Is Not Selected."

Here's where I have the problem:

When the user double-clicks on the checkbox, the checkbox gets checked, the RowChanged event gets called ("Is Selected"), and then the checkbox is cleared, and no corresponding RowChanged event gets called. Now the subscriber to the the RowChanged event is out of sync.

My solution right now is to subclass DataGridView and override WndProc to eat WM_LBUTTONDBLCLICK, so any double-clicking on the control is ignored. Is there a better solution?

A: 

Is there some reason it needs to be done that low level? Can the DoubleClick Method just be an empty method that eats it?

Ian Jacobs
A: 

I already tried overriding the OnDoubleClick method in the DataGridView subclass to do nothing, but it still allowed the checkbox to be changed a second time.

Ideally I was looking to have the DataTable's RowChanged event get called twice. Is there a way to affect the underlying DataTable via the overridden OnDoubleClick method?

A: 

Why not just leave IsSelected column unbounded? Use CellContentClick event to push the data to underlying datatable and leave CellDoubleClick or RowChange event alone.

private void dgv_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
  if(e.ColumnIndex == <columnIndex of IsSelected>)
  {
      string value = dgv[e.ColumnIndex, e.RowIndex].EditedFormattedValue;
       if( value == null || Convert.ToBoolean(value) == false)
       {
           //push false to employeeSelectionTable
       }
      else
       {
      //push true to employeeSelectionTable
       }

  }
}
Vivek
+1  A: 

The reason that making an empty DoubleClick event method would not help would be that is executed in addition to the other operations that happen when a double click occurs.

If you look at the windows generated code or examples of programatically adding event handlers, you use += to assign the event handler. This means you are adding that event handler in addition to the others that already exist, you could have multiple event handlers being triggered on the save event.

My instinct would have been to override the DataGridView class, then override the OnDoubleClick method and not call the base OnDoubleClick method.

However, I have tested this real quick and am seeing some interesting results.

I put together the following test class:

using System;
using System.Windows.Forms;

namespace TestApp
{
    class DGV : DataGridView
    {
        private string test = "";

        protected override void OnDoubleClick(EventArgs e)
        {
            MessageBox.Show(test + "OnDoubleClick");
        }

        protected override void OnCellMouseDoubleClick(System.Windows.Forms.DataGridViewCellMouseEventArgs e)
        {
            MessageBox.Show(test + "OnCellMouseDoubleClick");
        }

        protected override void OnCellMouseClick(System.Windows.Forms.DataGridViewCellMouseEventArgs e)
        {
            if (e.Clicks == 1)
            {
                // Had to do this with a variable as using a MessageBox
                // here would block us from pulling off a double click
                test = "1 click ";
                base.OnCellMouseClick(e);
            }
            else
            {
                MessageBox.Show("OnCellMouseClick");
            }
        }
    }
}

Then inserted this into a windows form, adding a checkbox column and ran the program.

On a fresh run, double clicking on the checkbox causes the messagebox display to say "1 click OnDoubleClick".

This means that OnCellMouseClick executed on the first part of the double click and then OnDoubleClick executed on the second click.

Also, unfortunately, the removal of the call to the base methods doesn't seem to be preventing the checkbox from getting the click passed to it.

I suspect that for this approach to work it may have to be taken further and override the DataGridViewCheckBoxColumn and DataGridViewCheckBoxCell that ignores the double click. Assuming this works, you would be able to stop double click on the checkbox but allow it still on your other column controls.

I have posted an answer on another question that talks about creating custom DataGridView columns and cells at here.

ManiacZX
A: 

Don't know why i had to, but i was able to put in a timer tick event attached to a datagridview refresh that just refreshed the dgv after the second click.

In cell click event

**    _RefreshTimer = new Timer();
    _RefreshTimer.Tick += new EventHandler(RefreshTimer_Tick);
    _RefreshTimer.Interval = 100;
    _RefreshTimer.Start();

            }
        }

 }

 void RefreshTimer_Tick(object sender, EventArgs e)
 {
  dgv.Refresh();
  _RefreshTimer.Stop();
  _RefreshTimer = null;
 }**
A: 

In case you want a checkbox column inside a DataGridView, create something like this:

DataGridViewCheckBoxCell checkBoxCell = new MyDataGridViewCheckBoxCell();
...
DataGridViewColumn col = new DataGridViewColumn(checkBoxCell);
...
col.Name = "colCheckBox";
...
this.dgItems.Columns.Add(col);

where dgItems is DataGridView instance.

As you can see I have a MyDataGridViewCheckBoxCell class which is a subclass of the DataGridViewCheckBoxCell class. In this subclass I define:

protected override void OnContentDoubleClick(DataGridViewCellEventArgs e)
{
    //This the trick to keep the checkbox in sync with other actions.
    //base.OnContentDoubleClick(e);
}

When the user now double clicks a checkbox in the checkbox column the checkbox will behave as if it was single clicked. This should solve your sync problem.

A: 

This isn't exactly an elegant solution, but why not simply do the following?

 private void dgv_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
 {
  dgv_CellClick(sender, e);
 }

This would explicitly force the second CellClick event and avoid out of sync.

rsmit212