OnClick event in WinForms DataGridView - c#

I am using DataGridView in WinForms and by this piece of code I am assigning it columns and values
dataGrid.DataSource = sourceObject;
only by this line all the columns and values into the grid.
How do I handle the onClick event of a specific row or field. I want to do edit a particular item in the grid but I cannot find any way to send the id of an item from the event method.
There is class DataGridViewEventHandler which I do not understand?
I have also tried to add columns manually as a buttons but I did not find way to assign it action method onClick.

You cannot find "OnClick" event for cell inside DataGridView, as it does not exist. Have a look at MSDN Page for DataGridView Events provided for Cell Manipulation and Events
Here are some samples from MSDN, about the events which you may use
Sample CellMouseClick Event and Handler
private void DataGridView1_CellMouseClick(Object sender, DataGridViewCellMouseEventArgs e) {
System.Text.StringBuilder cellInformation = new System.Text.StringBuilder();
cellInformation .AppendFormat("{0} = {1}", "ColumnIndex", e.ColumnIndex );
cellInformation .AppendLine();
cellInformation .AppendFormat("{0} = {1}", "RowIndex", e.RowIndex );
cellInformation .AppendLine();
MessageBox.Show(cellInformation.ToString(), "CellMouseClick Event" );
}
Sample CellClick Event and Handler
private void dataGridView1_CellClick(object sender,
DataGridViewCellEventArgs e)
{
if (turn.Text.Equals(gameOverString)) { return; }
DataGridViewImageCell cell = (DataGridViewImageCell)
dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex];
if (cell.Value == Play)
{
// PlaySomething()
}
else if (cell.Value == Sing)
{
// SingSomeThing();
}
else
{
MessagBox.Show("Please Choose Another Value");
}
}
Hope this helps

Here, you can see a list of events for the DataGridView. If you want to see if a cell has been clicked, you would want to consume the CellMouseclick event. In your code, you can handle the event like this:
private void DataGridView1_CellMouseClick(Object sender, DataGridViewCellMouseEventArgs e)
{
//Do something
}
To get specific details about the cell, then you can use the 'e' property mentioned above. It's of type DataGridViewCellMouseEventArgs. This will give you information about that specific cell. You can handle most of the other events, found in the first link, in the same way. (Not all the events will have DataGridViewCellMouseEventArgs as the argument, of course).

Related

How to get the column index of a DataGridView through click of ContextMenu in Winforms?

I have a DataGridView with 9 columns.
I have added the same ContextMenuStrip for all the column headers.
dataGridView.Columns[i].HeaderCell.ContextMenuStrip = myContextMenuStrip;
myContextMenuStrip contains a single item named Hide Column.
Now, I have an event handler for hidecolumnClick event and I want to find out which column header has been clicked inside the event handler?
Is there a way to do this?
Subscribe to the DataGridView.CellMouseDown event. In the event handler, store the column index or show the required context menu.
Sample Code:
void datagridview1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
//get the RowIndex or ColumnIndex from the event argument
}
}
Hi I have come up with another soution if u want to use the same object of ContextMenu for all the header. check it out..
Bind the CellMouseDown event on Grid-
dataGridView1.CellMouseDown += new DataGridViewCellMouseEventHandler(dataGridView1_CellMouseDown);
and in cellmousedown set the value of column clicked as below -
int columnClicked = -1;
void dataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
columnClicked = e.ColumnIndex;
}
now you can access the column clicked value in context menu item clicked event as below
private void helloToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show(columnClicked.ToString());
}
It is excepted u have assign the context menu to header already..
if you want i can give u the sample also..

DataGridView Event to Catch When Cell Value Has Been Changed by User

I have a Winforms app written in C#.
In one of my DataGridViews I have set all columns except one called 'Reference' to ReadOnly = true;
I want the application to know when a User has changed anything in the 'Reference' column, but all the events I have tried so far fire a lot more than when a user has made changes. For example CurrentCellChanged fires when the DataGridView is initially rendered and everytime the user simply clicks or tabs along the rows etc.
I'm only interested in catching user changes to data in the 'Reference' column which is the ONLY column where ReadOnly = false;
Which is the best event to use for this?
CellValueChanged is what you need:
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e){
if(dataGridView1.Columns[e.ColumnIndex].Name == "Reference"){
//your code goes here
}
}
I think the event CellEndEdit is also suitable for your want:
private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e){
if(dataGridView1.Columns[e.ColumnIndex].Name == "Reference"){
//your code goes here
}
}
In my case CellValueChanged event was also triggering while the DGV was initializing, so I wanted to use CellEndEdit, as King King mentioned in his answer.
To make King King's second answer more bullet-proof (see JPProgrammer's comment), i.e. only react, if a value was entered in the cell, you can do the following:
private void DataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
int? rowIdx = e?.RowIndex;
int? colIdx = e?.ColumnIndex;
if (rowIdx.HasValue && colIdx.HasValue)
{
var dgv = (DataGridView)sender;
var cell = dgv?.Rows?[rowIdx.Value]?.Cells?[colIdx.Value]?.Value;
if (!string.IsNullOrEmpty(cell?.ToString()))
{
// your code goes here
};
};
}
Notice the null handling by using ?. and ?[ operators. I have written it so that it can be used in a more general way, but of course you can add the check for the "Reference" column, just replace the inner if statement above by the following:
if (dgv.Columns[colIdx.Value].Name == "Reference")
{
if (!string.IsNullOrEmpty(cell?.ToString()))
{
// your code goes here
};
};

C# DataGridView (CheckBox) Cell Click Multiple Callbacks

[C#, Visual Studio 2008, Windows 7 64]
I have a DataGridView in my class. This data grid view uses DataGridViewCheckBoxColumn so that each cell contains a checkbox.
Here is a screenshot of one of the rows:
I want to be able to detect if the user selects the cell (somewhere in the cell but not on top of the checkbox). I also want to detect i the user selects the checkbox. In order to do this my code must set callbacks for both events:
this.CellClick += cellClick; // callback when the user selects a cell
this.CellContentClick += cellContentClick; // callback when the user selects a checkbox
Here are the callback methods:
private void cellContentClick(object sender, DataGridViewCellEventArgs e)
{
toggleCellCheck(e.RowIndex, e.ColumnIndex);
}
private void cellClick(object sender, DataGridViewCellEventArgs e)
{
toggleCellCheck(e.RowIndex, e.ColumnIndex);
}
private void toggleCellCheck(int row, int column)
{
bool isChecked = (bool)this[column, row].EditedFormattedValue;
this.Rows[row].Cells[column].Value = !isChecked;
}
(NOTE: As you can see the toggleCellCheck method gets the checkbox value and toggles it, checked->unchecked or unchecked->checked.)
When the user clicks anywhere in a cell that is not the checkbox only one callback is fired, cellClick. The toggleCellCheck method is subsequently called and the checkbox state flips.
This is the exact behavior I want.
The problem I am having is, when the user clicks directly on a checkbox both events get fired in the following order: cellClick then cellContentClick.
Both callbacks being executed results in the checkbox checked state being toggled after the first callback and then toggled again after the second callback. The net result of course is the checkbox checked status does not change.
Is there some way I can configure the DataGridView class to stop both callbacks from being fired? Or, is there a way I can detect (inside the cellContentClick method) that this is the second callback, or the callback was generated by clicking a checkbox, and then just exit without calling toggleCellCheck?
I was thinking something like the following:
private void cellContentClick(object sender, DataGridViewCellEventArgs e)
{
// if sender/sender child/etc. is of type checkbox then return because
// _cellClick_ has already been called to change the checkbox checked property
// something like the following:
//
// if (typeof(sender) == CheckBox) return;
// else toggleCellCheck(e.RowIndex, e.ColumnIndex);
}
Thanks!
Jan
You should not need the cell content click handler - cell click is called when the checkbox is selected.
It appears that your end goal is to make the grid respond to the cell content being clicked as well as the actual checkbox beiong clicked 1.
To do this, just attach to the cell click event with something like this:
void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (dataGridView1.Columns[e.ColumnIndex].Name == "checkboxcolumn")
{
Console.WriteLine("Click");
bool isChecked = (bool)dataGridView1[e.ColumnIndex, e.RowIndex].EditedFormattedValue;
dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = !isChecked;
dataGridView1.EndEdit();
}
}
1. I would advise against this sort of ui modification - the default behaviours for controls like the DataGridView are wide spread and well tested. Changing them is usually a bad idea.
private void dataGridView2_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
DataGridViewCheckBoxCell dgvcell = (DataGridViewCheckBoxCell)dataGridView2[e.ColumnIndex, e.RowIndex];
if ( ( Convert.ToBoolean(dataGridView2[e.ColumnIndex, e.RowIndex].Value ) == true ) )
{
dgvcell.Value = CheckState.Unchecked;
lst_box2.Items.Add(dataGridView2.Rows[e.RowIndex].Cells["ItemName"].Value.ToString());
lst_box1.Items.Remove(dataGridView2.Rows[e.RowIndex].Cells["ItemName"].Value.ToString());
}
else
{
lst_box1.Items.Add(dataGridView2.Rows[e.RowIndex].Cells["ItemName"].Value.ToString());
lst_box2.Items.Remove(dataGridView2.Rows[e.RowIndex].Cells["ItemName"].Value.ToString());
dgvcell.Value = CheckState.Checked;
}
}

DataGridView - how can I make a checkbox act as a radio button?

I have a Windows Forms app that displays a list of objects in a DataGridView.
This control renders bool values as checkboxes.
There's a set of three checkboxes in the object properties that are mutually exclusive. At most one of them can be true. Accordingly I'd like the checkboxes to act like a set of radio buttons.
Just a side remark from the old guy: I think people these days don't even know why these are called radio buttons. In the old days a radio in a car had 4 or 5 buttons, and depressing any one of them caused all the others to pop out. They were mutually exclusive. "Radio button" is probably not a helpful description these days because radios don't have buttons like that anymore, I don't think.
How can I do it? I figure if I attach a "CheckedChanged" event to the checkboxes, and I know the row, I will be able find all the other checkboxes.
What event can I hook to grab the checkbox control when it is first rendered, so that I Can attach the CheckedChanged event to it? I know about DataGridView.CellFormatting, but I think that's wrong because it gets called every time the DataGridView paints. I really need an event that is called only the first time the DGV is rendered.
Thanks to KeithS for the helpful answer.
When I looked in the doc for CellValueChanged, I found this helpful bit:
The DataGridView.CellValueChanged event occurs when the user-specified value is committed, which typically occurs when focus leaves the cell.
In the case of check box cells, however, you will typically want to handle the change immediately. To commit the change when the cell is clicked, you must handle the DataGridView.CurrentCellDirtyStateChanged event. In the handler, if the current cell is a check box cell, call the DataGridView.CommitEdit method and pass in the Commit value.
This is the code I used to get the radio behavior:
void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
// Manually raise the CellValueChanged event
// by calling the CommitEdit method.
if (dataGridView1.IsCurrentCellDirty)
{
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
public void dataGridView1_CellValueChanged(object sender,
DataGridViewCellEventArgs e)
{
// If a check box cell is clicked, this event handler sets the value
// of a few other checkboxes in the same row as the clicked cell.
if (e.RowIndex < 0) return; // row is sometimes negative?
int ix = e.ColumnIndex;
if (ix>=1 && ix<=3)
{
var row = dataGridView1.Rows[e.RowIndex];
DataGridViewCheckBoxCell checkCell =
(DataGridViewCheckBoxCell) row.Cells[ix];
bool isChecked = (Boolean)checkCell.Value;
if (isChecked)
{
// Only turn off other checkboxes if this one is ON.
// It's ok for all of them to be OFF simultaneously.
for (int i=1; i <= 3; i++)
{
if (i != ix)
{
((DataGridViewCheckBoxCell) row.Cells[i]).Value = false;
}
}
}
dataGridView1.Invalidate();
}
}
The one you want is CellContentClick, on the DGV itself. Attach a handler that checks to see if that column of the DGV is a CheckBoxCell, and if so, uncheck all other checkboxes on the row.
Just a note though, for a CheckBoxCell, this event fires before the checkbox value actually changes. This means that regardless of what you do to the current cell, it will be overridden by events that fire later. The behavior that will shake out of this is that you can have none of the cells on a row checked, by checking one box on the row and then checking it again (whether you try to set the checkbox value in the handler or not, the checkbox will end up cleared after the second click). To overcome that and force one of the checkboxes to be checked, you can handle CellValueChanged instead, and if the cell that changed is the current cell and is unchecked, check it.
private void dataGridViewProduit_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if ((sender as DataGridView).CurrentCell is DataGridViewCheckBoxCell)
{
if (Convert.ToBoolean(((sender as DataGridView).CurrentCell as DataGridViewCheckBoxCell).Value))
{
foreach (DataGridViewRow row in (sender as DataGridView).Rows)
{
if (row.Index != (sender as DataGridView).CurrentCell.RowIndex && Convert.ToBoolean(row.Cells[e.ColumnIndex].Value) == true)
{
row.Cells[e.ColumnIndex].Value = false;
}
}
}
}
}
private void dataGridViewClient_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (this.dataGridViewClient.IsCurrentCellDirty)
{
dataGridViewClient.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
It would get too easy here:
Conisder your checkbox column is the 2nd column in your datagridview.
private void YourDatagridview_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (IsHandleCreated)
{
if (YourDatagridview.CurrentCell == YourDatagridview.Rows[e.RowIndex].Cells[1])
{
if (Convert.ToBoolean(YourDatagridview.CurrentCell.Value) == true)
{
for (int i = 0; i < YourDatagridview.RowCount; i++)
{
if (YourDatagridview.Rows[i].Cells[1] != YourDatagridview.CurrentCell)
{
YourDatagridview.Rows[i].Cells[1].Value = false;
}
}
}
}
}
}
And call this too:
private void YourDatagridview_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (this.YourDatagridview.IsCurrentCellDirty)
{
YourDatagridview.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
}
and Voila!!

DataGridView's bound table only generates one RowChanged event on a double-click. How do I make it do two?

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?
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.
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.
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.
Is there some reason it needs to be done that low level? Can the DoubleClick Method just be an empty method that eats it?
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?
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
}
}
}
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;
}**

Categories