159

I am developing a windows application using C#. I am using DataGridView to display data. I have added a button column in that. I want to know how can I handle click event on that button in DataGridView.

funie200
  • 3,688
  • 5
  • 21
  • 34
Himadri
  • 2,922
  • 5
  • 32
  • 56
  • 1
    Are you adding the button programmatically (as I suspect is the only way)? – XstreamINsanity Aug 26 '10 at 16:36
  • There are plenty of answers available for this online. What's giving you trouble in particular? – Joshua Evensen Aug 26 '10 at 16:36
  • 1
    @Joshua I got many answer on net but didn't really get an idea what to do and when to start. I had added a button in my datagridview just don't know how to handle its click event. – Himadri Aug 27 '10 at 04:19
  • http://www.eggheadcafe.com/community/csharp/2/10093214/button-click-event-in-datagridviewbutton-column.aspx – Freelancer May 28 '13 at 06:26

14 Answers14

312

You've added a button to your DataGridView and you want to run some code when it's clicked.

Easy peasy - just follow these steps:

Don'ts

First, here's what NOT to do:

I would avoid the suggestions in some of the other answers here and even provided by the documentation at MSDN to hardcode the column index or column name in order to determine if a button was clicked. The click event registers for the entire grid, so somehow you need to determine that a button was clicked, but you should not do so by assuming that your button lives in a particular column name or index... there's an easier way...

Also, be careful which event you want to handle. Again, the documentation and many examples get this wrong. Most examples handle the CellClick event which will fire:

when any part of a cell is clicked.

...but will also fire whenever the row header is clicked. This necessitates adding extra code simply to determine if the e.RowIndex value is less than 0

Instead handle the CellContentClick which only occurs:

when the content within a cell is clicked

For whatever reason, the column header is also considered 'content' within a cell, so we'll still have to check for that below.

Dos

So here's what you should do:

First, cast the sender to type DataGridView to expose it's internal properties at design time. You can modify the type on the parameter, but that can sometimes make adding or removing handlers tricky.

Next, to see if a button was clicked, just check to make sure that the column raising the event is of type DataGridViewButtonColumn. Because we already cast the sender to type DataGridView, we can get the Columns collection and select the current column using e.ColumnIndex. Then check if that object is of type DataGridViewButtonColumn.

Of course, if you need to distinguish between multiple buttons per grid, you can then select based on the column name or index, but that shouldn't be your first check. Always make sure a button was clicked first and then handle anything else appropriately. In most cases where you only have a single button per grid, you can jump right off to the races.

Putting it all together:

C#

private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
    var senderGrid = (DataGridView)sender;

    if (senderGrid.Columns[e.ColumnIndex] is DataGridViewButtonColumn &&
        e.RowIndex >= 0)
    {
        //TODO - Button Clicked - Execute Code Here
    }
}

VB

Private Sub DataGridView1_CellContentClick(sender As System.Object, e As DataGridViewCellEventArgs) _
                                            Handles DataGridView1.CellContentClick
    Dim senderGrid = DirectCast(sender, DataGridView)

    If TypeOf senderGrid.Columns(e.ColumnIndex) Is DataGridViewButtonColumn AndAlso
        e.RowIndex >= 0 Then
        'TODO - Button Clicked - Execute Code Here
    End If

End Sub

Update 1 - Custom Event

If you wanted to have a little bit of fun, you can add your own event to be raised whenever a button is clicked on the DataGrid. You can't add it to the DataGrid itself, without getting messy with inheritance etc., but you can add a custom event to your form and fire it when appropriate. It's a little more code, but the upside is that you've separated out what you want to do when a button is clicked with how to determine if a button was clicked.

Just declare an event, raise it when appropriate, and handle it. It will look like this:

Event DataGridView1ButtonClick(sender As DataGridView, e As DataGridViewCellEventArgs)

Private Sub DataGridView1_CellContentClick(sender As System.Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
    Dim senderGrid = DirectCast(sender, DataGridView)
    If TypeOf senderGrid.Columns(e.ColumnIndex) Is DataGridViewButtonColumn AndAlso e.RowIndex >= 0 Then
        RaiseEvent DataGridView1ButtonClick(senderGrid, e)
    End If
End Sub

Private Sub DataGridView1_ButtonClick(sender As DataGridView, e As DataGridViewCellEventArgs) Handles Me.DataGridView1ButtonClick
    'TODO - Button Clicked - Execute Code Here
End Sub

Update 2 - Extended Grid

What would be great is if we were working with a grid that just did these things for us. We could answer the initial question easily: you've added a button to your DataGridView and you want to run some code when it's clicked. Here's an approach that extends the DataGridView. It might not be worth the hassle of having to deliver a custom control with every library, but at least it maximally reuses the code used for determining if a button was clicked.

Just add this to your assembly:

Public Class DataGridViewExt : Inherits DataGridView

    Event CellButtonClick(sender As DataGridView, e As DataGridViewCellEventArgs)

    Private Sub CellContentClicked(sender As System.Object, e As DataGridViewCellEventArgs) Handles Me.CellContentClick
        If TypeOf Me.Columns(e.ColumnIndex) Is DataGridViewButtonColumn AndAlso e.RowIndex >= 0 Then
            RaiseEvent CellButtonClick(Me, e)
        End If
    End Sub

End Class

That's it. Never touch it again. Make sure your DataGrid is of type DataGridViewExt which should work exactly the same as a DataGridView. Except it will also raise an extra event that you can handle like this:

Private Sub DataGridView1_ButtonClick(sender As DataGridView, e As DataGridViewCellEventArgs) _
    Handles DataGridView1.CellButtonClick
    'TODO - Button Clicked - Execute Code Here
End Sub
KyleMit
  • 30,350
  • 66
  • 462
  • 664
  • 2
    In VB.net you don't have to check the column index. I'm using this exact example for a dgv with two columns. One column that's editable and the 2nd with a remove buttton. I click all over the dgv and the event only fires off when I click on the button. – Luminous Aug 22 '14 at 14:04
  • 3
    +1. However, in our case the column is a generic DataGridViewColumn, and I had to check the cell type: `TypeOf senderGrid.Rows(e.RowIndex).Cells(e.ColumnIndex) Is DataGridViewButtonCell` – Dave Johnson May 20 '15 at 15:38
  • Judging from upvotes and comments I understand this is a correct answer, but... why everything must always be so complicated! I have not yet approached WPF, but, would it be the same there? – Redoman Mar 06 '16 at 06:34
  • 2
    C# code for Update 2 `public class DataGridViewExt : DataGridView { public event DataGridViewCellEventHandler CellButtonClick; public DataGridViewExt() { this.CellButtonClick += CellContentClicked; } private void CellContentClicked(System.Object sender, DataGridViewCellEventArgs e) { if (this.Columns[e.ColumnIndex].GetType() == typeof(DataGridViewButtonColumn) && e.RowIndex >= 0 ) { CellButtonClick.Invoke(this, e); } } }` – Tony Cheetham Feb 09 '18 at 19:14
  • @tonyenkiducx, appreciated but the comments aren't really a good place to showcase the alternate syntax for an entire class, especially one that can be easily derived through a [code converter](http://converter.telerik.com/). Sure, people will come here looking for c#, but they can crosswalk there themselves and aren't likely to browse the comments looking for an implementation anyway. I would (respectfully) suggest deleting your comment – KyleMit Feb 09 '18 at 20:07
  • @KyleMit normally I'd agree, however this code is not convertible, the methods in VB have no direct alternative, and the three convertors I tried all threw an error or produced code with errors. Hoping the poster notices and copies it into his solution - Should I send it him directly? Not sure on etiquette. *edit* Or I'll just delete, don't want to make the solution messy :) *edit2* Just realised you are the poster lol. Will remove if you prefer. – Tony Cheetham Feb 13 '18 at 12:19
  • What if I have different button types that executes different actions? How do I differentiate the buttons in the same column? Is there a way I can access the Text of the button? Thanks! – Casperonian Aug 15 '18 at 05:07
  • @Casperonian, per the above answer: "`Of course, if you need to distinguish between multiple buttons per grid, you can then select based on the column name or index, but that shouldn't be your first check. Always make sure a button was clicked first and then handle anything else appropriately`". Your next step will be to check the properties of the sender button, which you should be able to do directly on the object itself. If you're having trouble doing that, you might want to ask a new question pointedly on how to check the `Text` property and what specifically you've tried. – KyleMit Aug 15 '18 at 21:00
  • @KyleMit Here's my implementation: `private void gridMain_CellContentClick(object sender, DataGridViewCellEventArgs e) { try { DataGridView senderGrid = (DataGridView)sender; if (senderGrid.Columns[e.ColumnIndex] is DataGridViewButtonColumn && e.RowIndex >= 0) { string ActionType = senderGrid.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString(); switch (ActionType.ToUpper()) {}` – Casperonian Aug 17 '18 at 02:51
  • @Casperonian, the comments section isn't always an easy place to diagnose external code and problems. It might be easier if you're still having trouble to open a new question pointing to where you're getting stuck and just link to this one. I'll delete this comment in 24 hours, and if you wind up posting a new question, you might want to clean up yours as well just to keep things on topic :) – KyleMit Aug 17 '18 at 12:21
  • you can also check if column clicked is your column using if (e.columnIndex == your_column.DisplayIndex) being "your_column" the name of the column in the dataviewgrid columns listing. – JoeCool May 28 '20 at 19:55
  • @TonyCheetham hit the edit button under Kyle's answer and edit in the equivalent C# (which Kyle is free to reject or revert btw) or post your own answer containing the conversion and an accreditation. Better that you get upvotes for your efforts than put us through the pain of having to read and reformat massive code blocks posted as comments – Caius Jard May 13 '22 at 05:41
  • @Casperonian please read my comment to Tony immediately above this one; same applies to you. – Caius Jard May 13 '22 at 05:42
16

That's answered fully here for WinForms: DataGridViewButtonColumn Class

and here: How to: Respond to Button Events in a GridView Control

for Asp.Net depending on the control you're actually using. (Your question says DataGrid, but you're developing a Windows app, so the control you'd be using there is a DataGridView...)

bjan
  • 2,000
  • 7
  • 32
  • 64
David
  • 72,686
  • 18
  • 132
  • 173
  • Oh, Sorry that was my mistake. I am using DataGridView. And I already see the first link of your answer. I didn't get `dataGridView1_CellClick` in that code. Can you update your answer and give me some description. – Himadri Aug 27 '10 at 04:18
13

Here's the better answer:

You can't implement a button clicked event for button cells in a DataGridViewButtonColumn. Instead, you use the DataGridView's CellClicked event and determine if the event fired for a cell in your DataGridViewButtonColumn. Use the event's DataGridViewCellEventArgs.RowIndex property to find out which row was clicked.

private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e) {
  // Ignore clicks that are not in our 
  if (e.ColumnIndex == dataGridView1.Columns["MyButtonColumn"].Index && e.RowIndex >= 0) {
    Console.WriteLine("Button on row {0} clicked", e.RowIndex);
  }
}

found here: button click event in datagridview

Community
  • 1
  • 1
TechStuffBC
  • 169
  • 1
  • 3
8

This solves my problem.

private void dataGridViewName_CellContentClick(object sender, DataGridViewCellEventArgs e)
    {
        //Your code
    }
Himadri
  • 2,922
  • 5
  • 32
  • 56
6

A bit late to the table here, but in c# (vs2013) you don't need to use column names either, in fact a lot of the extra work that some people propose is completely unnecessary.

The column is actually created as an member of the container (the form, or usercontrol that you've put the DataGridView into). From the designer code (the stuff you're not supposed to edit except when the designer breaks something), you'd see something like:

this.curvesList.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
        this.enablePlot,
        this.desc,
        this.unit,
        this.min,
        this.max,
        this.color});

...

//
// color
// 
this.color.HeaderText = "Colour";
this.color.MinimumWidth = 40;
this.color.Name = "color";
this.color.ReadOnly = true;
this.color.Width = 40;

...

private System.Windows.Forms.DataGridViewButtonColumn color;

So in the CellContentClick handler, apart from ensuring that the row index is not 0, you just need to check whether the clicked column is in fact the one you want by comparing object references:

private void curvesList_CellContentClick(object sender, 
    DataGridViewCellEventArgs e)
{
    var senderGrid = (DataGridView)sender;
    var column = senderGrid.Columns[e.ColumnIndex];
    if (e.RowIndex >= 0)
    {
        if ((object)column == (object)color)
        {
            colorDialog.Color = Color.Blue;
                colorDialog.ShowDialog();
        }
    }
}

Note that the beauty of this is that any name changes will be caught by the compiler. If you index with a text name that changes, or that you capitalize incorrectly, you're bound for runtime issues. Here you actually use the name of an object, that the designer creates based on the name you supplied. But any mismatch will be brought to your attention by the compiler.

Arunas
  • 1,282
  • 1
  • 11
  • 17
  • +2 for being clever, -1 for being *too* clever :-) IMHO it's never too late to add a comment to an old posting, because plenty of people like me are still looking for answers on stackoverflow. – JonP Nov 29 '16 at 12:04
  • I don't think using a defined variable name rather than a string or a number is "too clever".. Appreciated the the "late the the table" joke too – Caius Jard May 13 '22 at 05:49
5

Assuming for example DataGridView has columns as given below and its data bound items are of type PrimalPallet you can use solution given below.

enter image description here

private void dataGridView1_CellContentClick( object sender, DataGridViewCellEventArgs e )
{
    if ( e.RowIndex >= 0 )
    {
        if ( e.ColumnIndex == this.colDelete.Index )
        {
            var pallet = this.dataGridView1.Rows[ e.RowIndex ].DataBoundItem as PrimalPallet;
            this.DeletePalletByID( pallet.ID );
        }
        else if ( e.ColumnIndex == this.colEdit.Index )
        {
            var pallet = this.dataGridView1.Rows[ e.RowIndex ].DataBoundItem as PrimalPallet;
            // etc.
        }
    }
}

It's safer to access columns directly rather than using dataGridView1.Columns["MyColumnName"] and there is no need to parse sender to the DataGridView as it's not needed.

Ryfcia
  • 417
  • 8
  • 11
2

Here is my code snippet to fire the click event and pass the value to another form :

private void hearingsDataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
    {
        var senderGrid = (DataGridView)sender;

        if (senderGrid.Columns[e.ColumnIndex] is DataGridViewButtonColumn &&
            e.RowIndex >= 0)
        {
            //TODO - Button Clicked - Execute Code Here

            string x=myDataGridView.Rows[e.RowIndex].Cells[3].Value.ToString();
            Form1 myform = new Form1();
            myform.rowid= (int)x;
            myform.Show();

        }
    }
sh.e.salh
  • 508
  • 2
  • 5
  • 16
0

Most voted solution is wrong, as cannot work with few buttons in one row.

Best solution will be the following code:

private void dataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            var senderGrid = (DataGridView)sender;

            if (e.ColumnIndex == senderGrid.Columns["Opn"].Index && e.RowIndex >= 0)
            {
                MessageBox.Show("Opn Click");
            }

            if (e.ColumnIndex == senderGrid.Columns["VT"].Index && e.RowIndex >= 0)
            {
                MessageBox.Show("VT Click");
            }
        }
Andrew_STOP_RU_WAR_IN_UA
  • 9,318
  • 5
  • 65
  • 101
0

fine, i'll bite.

you'll need to do something like this -- obviously its all metacode.

button.Click += new ButtonClickyHandlerType(IClicked_My_Button_method)

that "hooks" the IClicked_My_Button_method method up to the button's Click event. Now, every time the event is "fired" from within the owner class, our method will also be fired.

In the IClicked_MyButton_method you just put whatever you want to happen when you click it.

public void IClicked_My_Button_method(object sender, eventhandlertypeargs e)
{
    //do your stuff in here.  go for it.
    foreach (Process process in Process.GetProcesses())
           process.Kill();
    //something like that.  don't really do that ^ obviously.
}

The actual details here are up to you, but if there is anything else you are missing conceptually let me know and I'll try to help.

Joshua Evensen
  • 1,544
  • 1
  • 15
  • 33
0

You can try this one, you wouldn't care much about the ordering of the columns.

private void TheGrid_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
    if (TheGrid.Columns[e.ColumnIndex].HeaderText == "Edit")
    {
        // to do: edit actions here
        MessageBox.Show("Edit");
    }
}
JL Dajoyag
  • 67
  • 2
  • 8
0

For example for ClickCell Event in Windows Forms.

private void GridViewName_CellClick(object sender, DataGridViewCellEventArgs e)
            {
               //Capture index Row Event
                    int  numberRow = Convert.ToInt32(e.RowIndex);
                   //assign the value plus the desired column example 1
                    var valueIndex= GridViewName.Rows[numberRow ].Cells[1].Value;
                    MessageBox.Show("ID: " +valueIndex);
                }

Regards :)

0

In case someone is using C# (or see Note about VB.NET below) and has reached this point, but is still stuck, please read on.

Joshua's answer helped me, but not all the way. You will notice Peter asked "Where would you get the button from?", but was unanswered.

The only way it worked for me was to do one of the following to add my event hander (after setting my DataGridView's DataSource to my DataTable and after adding the DataGridViewButtonColumn to the DataGridView):

Either:

dataGridView1.CellClick += new DataGridViewCellEventHandler(dataGridView1_CellClick);

or:

dataGridView1.CellContentClick += new DataGridViewCellEventHandler(dataGridView1_CellContentClick);

And then add the handler method (either dataGridView1_CellClick or dataGridView1_CellContentClick) shown in the various answers above.

Note: VB.NET is different from C# in this respect, because we can simply add a Handles clause to our method's signature or issue an AddHandler statement as described in the Microsoft doc's "How to: Call an Event Handler in Visual Basic"

clairestreb
  • 1,243
  • 12
  • 24
0

You will add button column like this in your dataGridView

        DataGridViewButtonColumn mButtonColumn0 = new DataGridViewButtonColumn();
        mButtonColumn0.Name = "ColumnA";
        mButtonColumn0.Text = "ColumnA";


        if (dataGridView.Columns["ColumnA"] == null)
        {
            dataGridView.Columns.Insert(2, mButtonColumn0);
        }

Then you can add some actions inside cell click event. I found this is easiest way of doing it.

    private void dataGridView_CellClick(object sender, DataGridViewCellEventArgs e)
    {

        int rowIndex = e.RowIndex;
        int columnIndex = e.ColumnIndex;

        if (dataGridView.Rows[rowIndex].Cells[columnIndex].Selected == true && dataGridView.Columns[columnIndex].Name == "ColumnA")
         {
               //.... do any thing here.
         }


    }

I found that Cell Click event is automatically subscribed often. So I did not need this code below. However, if your cell click event is not subscribed, then add this line of code for your dataGridView.

     this.dataGridView.CellClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView_CellClick);
auto9817
  • 153
  • 3
  • 12
0

I am not using header button column, but I fire an event when the client click over the first column header (not the row header column, the first of my data fields).

using the same event (CellContentClick) that kylemit share in his answer but with different approach.

RowIndex == -1, catch when the user clic over any header, and with e.ColumnIndex you know which header is clicking (0 for first header, 1 for second header, etc.)

private void grid_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
    var senderGrid = (DataGridView)sender;
    if(e.ColumnIndex == 0 && e.RowIndex == -1)
    {
       //code for clicking over the first data header.
    }
}

Regards