How to get the currently edited value when handling the:
public class GridEX // ...
{
// ...
public event ColumnActionEventHandler CellValueChanged;
// ...
};
Trying to obtain the value using:
GridEXCell valueChangedCell = _gridView.CurrentRow.Cells[<desired_column_index>];
object rawValue = valueChangedCell.Value;
// or even with
string rawValue = valueChangedCell.Text;
The only moment at which the valueChangedCell has its value changed is when either the CellUpdated or the UpdatingCell event is fired. But the latter two are fired only in case the user has changed the keyboard input focus to another cell probably for the sake of applying the edited cell's new value. The cell I want to lookup the value of is one containing only a checkbox. I want to perform a given action immediately after the checkbox of a given cell is toggled, not as soon as the user changes the focus e.g. moves to another cell in the table. Saw that in the descriptions of the events some row buffer is mentioned:
[Description("Occurs after changes in a cell are copied into the row's buffer.")]
public event ColumnActionEventHandler CellUpdated;
[Description("Occurs before updating the changes in a cell to the row's buffer")]
public event UpdatingCellEventHandler UpdatingCell;
I presume that the current value of the checkbox is probably kept in some buffer and upon changing the focus the new value is applied to the cell.
Any ideas how to fetch the currently set value of the checkbox upon handling the Janus' GridEX.CellValueChanged?
I fixed the problem adding a method which is firing within the event below: private void
CloseEditMode()
{
gridRubrica.AllowEdit = InheritableBoolean.False;
gridRubrica.AllowEdit = InheritableBoolean.True;
}
private void gridRubrica_CellValueUpdated(object sender, ColumnActionEventArgs e)
{
if (e.Column.Key.Equals("Selected")) { CloseEditMode(); }
}
A little bit late to the party, but since I was having the same difficulty I decided to just flush the pending changes when the Cell Changed is a CheckBox
private void gridEX1_CellChanged(object sender, ColumnActionEventArgs e)
{
if (e.Column.ColumnType == ColumnType.CheckBox)
{
gridEX1.UpdateData(); // Flush any pending changes
}
}
This will trigger the other handler that deals with validation (in my case)
Related
I have a DataGridView where the first column is a DataGridViewCheckBoxColumn. The user checks some checkboxes to indicate which items are to be deleted.
When I hide the form and reload it, I need the DataGridView to remember which checkboxes were checked.
You need to save the changes at least at the point where you close your Form (if you just hide it, why reload then?).
The way I usually work this out is listening to the Dgv's CellEndEdit event:
SomeDataGridView.CellEndEdit += ObjectPropertyChanged
then in the Callback you can get the Object back by using the "DataBoundItem" prop of the Dgv and process / save it however you need:
protected virtual void ObjectPropertyChanged(object sender, DataGridViewVellEventArgs e)
{
var selectedObject = ((DataGridView)sender).Rows[e.RowIndex].DataBoundItem;
//Assuming you stored in a List and each Object has an ID as prop:
var indx = _Objects.IndexOf(_Objects.Where(o => o.ID.Equals(selectedObject.ID)))
_Objects.Remove(indx)
_Objects.Insert(indx, selectedObject)
}
You could also do database update, write to a text-file, save in configuration, ...
I have a DataGridView bound through a BindingSource to a DataTable, and my grid has editable columns where the user inputs the value, and also some read-only columns that are programmatically updated in real time (think like a stock ticker). For the programmatically updated columns I am updating the value in the DataTable and then the value in the DataGridView is updated because of the databinding.
The issue I'm having is that while a user is editing one cell, if another cell is updated programmatically then the text the user has inputted in the first cell gets overwritten (reset to the cell's value before it was edited).
This doesn't seem like an unusual situation to me, but I can't seem to get it to work properly. Anyone know what I might be doing wrong, or can point me towards an example that shows how to do this properly?
This is happening because when the underlying value in the DataTable changes, the DataGridView.DataBindingComplete event gets fired - resetting the bindings.
My first suggestion was that you capture this event and just check if the EditedFormattedValue for the CurrentCell was different than the Value - if so, then set the Value. This worked - until I checked for the first row - which completely ignored my logic.
The only solution I could find to always work was to change how the programmatic real-time updates happened. If possible, instead of updating the DataTable columns, just update the DataGridView columns. These changes will perpetuate to the source but binding will not reset - thus your current editing cell will not lose any changes.
foreach (DataRow row in this.table.Rows)
{
row[1] = someNewValue;
}
foreach (DataGridViewRow row in this.dataGridView1.Rows)
{
row.Cells[1].Value = someNewValue;
}
In the end I worked it out so I'll add my solution here in case it will be helpful to someone else.
I handled the CellBeginEdit, CellEndEdit and CurrentCellDirtyStateChanged events of the DataGridView and added the following code:
private void Grid_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e)
{
((DataRowView)((DataGridView)sender).Rows[e.RowIndex].DataBoundItem).BeginEdit();
}
private void Grid_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
// need this check for when the program is closed while a cell is in edit mode
// otherwise an IndexOutOfRangeException occurs
if (((BindingSource)((DataGridView)sender).DataSource).List.Count > e.RowIndex)
((DataRowView)((DataGridView)sender).Rows[e.RowIndex].DataBoundItem).EndEdit();
}
private void Grid_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
// commit changes to the table as soon as they are entered so they don't
// get overwritten when the DataTable is updated
if (Grid.IsCurrentCellDirty)
Grid.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
I also found that the Esc key functionality of the DataGridView was reverting not only the changes to the cell currently in edit mode, but also the changes where values were set directly in the DataTable (the real time programmatic updates), so I overrode the Esc key functionality for the DataGridView as follows:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (CurrentCell != null && CurrentCell.IsInEditMode)
{
if (keyData == (Keys.Escape))
{
CurrentCell.Value = valueBeforeEdit;
EditingControl.Text = valueBeforeEdit.ToString();
EndEdit();
return true;
}
}
return base.ProcessCmdKey(ref msg, keyData);
}
where valueBeforeEdit holds the value of the cell before editing (set in the DataGridView's CellBeginEdit event handler).
I have a master-detail layout with a section of popup menus (the Details) and a section with a DataGridView which holds the rows.
The popup-menu state is updated when the selected row in the DataGridView changes and the state in the DGV's selected row should update when the popup-menu changes.
All of this works except the row in the DataGridView doesn't immediately update when I change the value of the popup-menu. I have to select a different row in order to see my edits.
I'm assuming this is because the edit hasn't been committed until the selection changes.
My question is: How do I make the change to the popup become immediately reflected in the DataGridView?
I have experimented with calling EndEdit() in the SelectionChangeCommitted handler for the popup-menu, but this has no effect. I'm interested in a technique that would allow me to create a DataGridView that would behave as if there were no Undo mechanism to begin with. Ideally the solution would be generic and transplantable to other projects.
It looks like existing answers work well with BindingSource. In my case, where DataTable was directly used as a DataSource, they didn't work for some reason.
// Other answers didn't work in my setup...
private DataGridView dgv;
private Form1()
{
var table = new DataTable();
// ... fill the table ...
dgv.DataSource = table;
}
After some hair-pulling, I got it work without adding BindingSource indirection:
// Add this event handler to the DataGridView
private void dgv_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
dgv.BindingContext[dgv.DataSource].EndCurrentEdit();
}
private Form1()
{
dgv.CellEndEdit += dgv_CellEndEdit;
// ...
}
Here's what was going on. The answer was in the properties of the ComboBox instances. I needed to change their DataSourceUpdateMode from OnValidation to OnPropertyChanged. This makes sense. The DataGridView was very likely showing the current state of the data. It was just that the data hadn't been edited yet because focus had not left the ComboBox, validating the input.
Thanks to everyone for your responses.
this works well for me:
private void CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
var dgw = (DataGridView) sender;
dgw.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
Use this extension method. It works for all columns types, not just ComboBoxes:
public static void ChangeEditModeToOnPropertyChanged(this DataGridView gv)
{
gv.CurrentCellDirtyStateChanged += (sender, args) =>
{
gv.CommitEdit(DataGridViewDataErrorContexts.Commit);
if (gv.CurrentCell == null)
return;
if (gv.CurrentCell.EditType != typeof(DataGridViewTextBoxEditingControl))
return;
gv.BeginEdit(false);
var textBox = (TextBox)gv.EditingControl;
textBox.SelectionStart = textBox.Text.Length;
};
}
This method commits every change just after the change is made.
When we have a text column, after typing a character, its value will commit to the DataSource and the editmode of the cell will end.
Therefore current cell should return to edit mode, and position of the cursor set to end of text in order to enable user to continue typing reminder of the text.
Call the DataGridView.EndEdit method.
following will work
_dataGrid.EndEdit()
s fine once you set the value.
I'm trying to retrieve the value of a DataGridViewComboBoxCell in the following way
When adding items to the ComboBox:
public void LoadLayouts()
{
ImmutableSet<string> layoutNames = _store.Current.Keys;
var dgvComboBox = (DataGridViewComboBoxColumn)this.schedulesDataGrid.Columns[1];
foreach (string name in layoutNames)
{
dgvComboBox.Items.Add(name);
}
}
When trying to read back the value:
var combo = (DataGridViewComboBoxCell) this.schedulesDataGrid[args.ColumnIndex, args.RowIndex];
string LayoutChosen = (string)combo.Value;
However, even if I can see that there is a value selected in the ComboBox, the Value comes back as null, and the FormattedValue comes back as "".
I've tried just setting the array of names as my DataSource, but then I'm not sure what to set for my Display and Value Members, considering I only have a single value (the name of the layout)
Thoughts?
At the time you attempt to read the cell's value, the row has not committed changes. If you have row headers visible, you will see the pencil icon on the row that has not been committed. This normally happens after the cell looses focus. You can force the issue by hooking into the in-place editing control combo-box and causing it to post EndEdit when it changes values:
void dgv_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
var comboBox = e.Control as ComboBox;
if (comboBox != null)
{
// remove any handler we may have added previously
comboBox.SelectionChangeCommitted -= new EventHandler(comboBox_SelectionChangeCommitted);
// add handler
comboBox.SelectionChangeCommitted += new EventHandler(comboBox_SelectionChangeCommitted);
}
}
void comboBox_SelectionChangeCommitted(object sender, EventArgs e)
{
// Allow current dispatch to complete (combo-box submitting its value)
// then EndEdit to commit the change to the row
dgv.BeginInvoke(new Action(() => dgv.EndEdit()));
}
I have a function that will change a combo box selected index, so combobox_selectionchanged event will rise automatically,but the handler of this event call my function again,so the function will be called twice!!
IS there any way to prevent rising selection_changed event in function below?
private void Refresh_Window()
{
Monthes_ComboBox.SelectedIndex = DM.Month - 1;
}
I wanted to avoid a long description about my problem, so I just asked the question. I'm designing a calendar, the combo_box contains monthes of a year, but there are two buttons that will go to nextmonth or previous month,so I have to change the combo_box index by code, I create a function and I called it in form_load and combobox_Selection_changed and button_click Can I design it in a better way? and Refresh_window doesn't just change the combobox_selectedindex, it changes all Labels and TextBlocks in form, so I just wanted all changes to be done by Refresh_window
private bool _refreshCalled = false;
private void Refresh_Window()
{
_refreshCalled = true;
try
{
....
Monthes_ComboBox.SelectedIndex = DM.Month - 1;
....
}
finally
{
_refreshCalled = false;
}
}
private void OnComboBoxSelectedChanged(object sender, EventArgs e)
{
...
if (!_refreshCalled)
{
Refresh_Window();
}
...
}
if you use a function to raise an event and you call that same function from the event handler, to what I understand this is at minimum badly designer and not optimal.
in general with combo boxes you can simply assign the SelectedValue and the control will select the item with that value then the selected index will change to the index of such item, you would not really need to set the selected index directly...
if you really want to work by index anyway you are free to do but I would avoid this spaghetti coding having a method which change the selected index if the same method is also called by the event handler attached to the event fired...
There are a couple of good ways to do this:
Set a variable that indicates the selected index is changing in the combobox (you'll need to derive from the built-in ComboBox class). If selected index changes again from deeper in the call stack, the variable will still be set and you can read it before trying to change the selected index again.
Similarly, if the call stack involves handlers attached to the combobox, you can override OnSelectedIndexChanged and not perform any logic if this is the second time the selected index has changed.