1

How can I subscribe to an event handler of a CheckBox in a CheckBoxColumn of a DataGridView similar to the CheckChanged or Click event handler of a regular CheckBox? I have one or more columns, in multiple data grids in my application that I want to do this for.

I've looked at CellContentClick and CellClick, but they seem totally inelegant, because they fire for every click in the data grid, not just for the Cell or CheckBox of interest.

gmlobdell
  • 383
  • 3
  • 10
  • 3
    Possible duplicate: http://stackoverflow.com/questions/932040/triggering-a-checkbox-value-changed-event-in-datagridview-c-net, or http://stackoverflow.com/questions/11843488/datagridview-checkbox-event – Patrick Quirk Nov 13 '12 at 01:19
  • Yes, a possible duplicate, but I believe there is an interesting solution. I'm a long time reader, but a new poster to StackOverflow. I'm just trying to contribute here. Is it somehow better to post yet another answer to a long thread, where it may get lost in the noise? – gmlobdell Nov 13 '12 at 01:46
  • I haven't seen any clever way to do this yet (as someone who has wanted to do the same thing). The `EditControl` functionality isn't used by this column type, and the `DataGridViewCheckboxCell` doesn't have any interesting events. I think a new question is fine if you're looking for other information, as long as you make that clear. – Patrick Quirk Nov 13 '12 at 04:45
  • @gmlobdell you can answer to your own questions. Its valid in SO. I'm waiting to see an answer from you! :) – nawfal Nov 13 '12 at 06:58
  • Feedback on the answer below? Votes appreciated. – gmlobdell Nov 15 '12 at 19:47

2 Answers2

3

The solution below came from pouring over the MSDN documentation and a little luck in some tangential threads here and on CodeProject.

The idea is to create a class, derived from DataGridViewCheckBoxCell, that includes a handler that is triggered along with the ContentClick. This may seem like lots of overhead for one DataGridView, but my application has many DataGridViews, so this code is reusable, thus saving time.

/// <summary>
/// DataGridView cell class for check box cells with a OnContentClick event handler.
/// </summary>
public class DataGridViewEventCheckBoxCell : DataGridViewCheckBoxCell
{
    /// <summary>
    /// Event handler for OnContentClick event.
    /// </summary>
    protected EventHandler<DataGridViewCellEventArgs> ContentClickEventHandler { get; set; }

    /// <summary>
    /// Empty constructor. Required. Used by Clone mechanism
    /// </summary>
    public DataGridViewEventCheckBoxCell()
        : base()
    { }

    /// <summary>
    /// Pass through constructor for threeState parameter.
    /// </summary>
    /// <param name="threeState">True for three state check boxes, True, False, Indeterminate.</param>
    public DataGridViewEventCheckBoxCell(bool threeState)
        : base(threeState)
    { }

    /// <summary>
    /// Constructor to set the OnContentClick event handler.  
    /// Signature for handler should be (object sender, DataGridViewCellEventArgs e)
    /// The sender will be the DataGridViewCell that is clicked.
    /// </summary>
    /// <param name="handler">Handler for OnContentClick event</param>
    /// <param name="threeState">True for three state check boxes, True, False, Indeterminate.</param>
    public DataGridViewEventCheckBoxCell(EventHandler<DataGridViewCellEventArgs> handler, bool threeState)
        : base(threeState)
    {
        ContentClickEventHandler = handler;
    }

    /// <summary>
    /// Clone method override.  Required so CheckEventHandler property is cloned.
    /// Individual DataGridViewCells are cloned from the DataGridViewColumn.CellTemplate
    /// </summary>
    /// <returns></returns>
    public override object Clone()
    {
        DataGridViewEventCheckBoxCell clone = (DataGridViewEventCheckBoxCell)base.Clone();
        clone.ContentClickEventHandler = ContentClickEventHandler;
        return clone;
    }

    /// <summary>
    /// Override implementing OnContentClick event propagation 
    /// </summary>
    /// <param name="e">Event arg object, which contains row and column indexes.</param>
    protected override void OnContentClick(DataGridViewCellEventArgs e)
    {
        base.OnContentClick(e);
        if (ContentClickEventHandler != null)
            ContentClickEventHandler(this, e);
    }

    /// <summary>
    /// Override implementing OnContentDoubleClick event propagation 
    /// Required so fast clicks are handled properly.
    /// </summary>
    /// <param name="e">Event arg object, which contains row and column indexes.</param>
    protected override void OnContentDoubleClick(DataGridViewCellEventArgs e)
    {
        base.OnContentDoubleClick(e);
        if (ContentClickEventHandler != null)
            ContentClickEventHandler(this, e);
    }
}

Since I wanted to be able to reference this cell class from a column class, I also implemented a class derived from DataGridViewCheckBoxColumn:

/// <summary>
/// DataGridView column class for a check box column with cells that have an OnContentClick handler.
/// </summary>
public class DataGridViewEventCheckBoxColumn : DataGridViewCheckBoxColumn
{
    /// <summary>
    /// Empty constructor.  Pass through to base constructor
    /// </summary>
    public DataGridViewEventCheckBoxColumn()
        : base()
    { }

    /// <summary>
    /// Pass through to base constructor with threeState parameter
    /// </summary>
    /// <param name="threeState">True for three state check boxes, True, False, Indeterminate.</param>
    public DataGridViewEventCheckBoxColumn(bool threeState)
        : base(threeState)
    { }

    /// <summary>
    /// Constructor for setting the OnContentClick event handler for the cell template.
    /// Note that the handler will be called for all clicks, even if the DataGridView is ReadOnly.
    /// For the "new" state of the checkbox, use the EditedFormattedValue property of the cell.
    /// </summary>
    /// <param name="handler">Event handler for OnContentClick.</param>
    /// <param name="threeState">True for three state check boxes, True, False, Indeterminate.</param>
    public DataGridViewEventCheckBoxColumn(EventHandler<DataGridViewCellEventArgs> handler, bool threeState)
        : base(threeState)
    {
        CellTemplate = new DataGridViewEventCheckBoxCell(handler, threeState);
    }
}

Trimming away extraneous code, I'm using it like this:

public void AddCheckBoxColumn(DataGridView grid, EventHandler<DataGridViewCellEventArgs> handler, bool threeState)
{
   grid.Columns.Add(new DataGridViewEventCheckBoxColumn(handler, threeState));
}

The column class could be eliminated, and it could be used as follows:

public void AddCheckBoxColumn(DataGridView grid, EventHandler<DataGridViewCellEventArgs> handler, bool threeState)
{
   DataGridViewCheckBoxColumn column = new DataGridViewCheckBoxColumn(threeState);
   column.CellTemplate = new DataGridViewEventCheckBoxCell(handler, threeState);
   grid.Columns.Add(column);
}

Here's a sample event handler. The state of another column in the data grid is updated, based on the value of this column and conditionally some state information in the WasReleased property. The DataGridView's DataSource is a collection of Specimen objects, so each row's DataBoundItem is a Specimen. The Specimen class is application specific, but it has properties OnHold, displayed by this column; IsReleased, displayed by another column; and WasReleased.

    public static void OnHoldCheckClick(object sender, DataGridViewCellEventArgs e)
    {
        if (sender is DataGridViewEventCheckBoxCell)
        {
            DataGridViewEventCheckBoxCell cell = sender as DataGridViewEventCheckBoxCell;

            if (!cell.ReadOnly)
            {
                // The rows in the DataGridView are bound to Specimen objects
                Specimen specimen = (Specimen)cell.OwningRow.DataBoundItem;
                // Modify the underlying data source
                if ((bool)cell.EditedFormattedValue)
                    specimen.IsReleased = false;
                else if (specimen.WasReleased)
                    specimen.IsReleased = true;
                // Then invalidate the cell in the other column to force it to redraw
                DataGridViewCell releasedCell = cell.OwningRow.Cells["IsReleased"];
                cell.DataGridView.InvalidateCell(releasedCell);
            }
        }
    }

Good style would probably push some of the code in the event handler down into a method in the Specimen class.

gmlobdell
  • 383
  • 3
  • 10
  • +1 for creativity. However, for myself, I only have one datagridview and this is a lot of time/resources to invest for the event. The second link from Patrick Quirk seemed faster solution. Giving that a try. – Encryption Jul 29 '13 at 20:48
1

you can use

e.Item.Cells[1].Text

here e is current row and second cell remember cell index always start with 0 index

  • e is typically an EventHandler parameter of type EventArgs or a type that derives from EventArgs. How does this help? What event would you subscribe to, and of what object/control, so that the event handler is called with (object sender, DataGridViewCellEventArgs e) Usually the sender would be the row, or some other object from which you could get the row. Please edit to provide more details. – gmlobdell Nov 13 '12 at 01:42