How can I skip the CellValueChanging state of DevExpress ColumnView? - c#

There are 2 distinct UI concept: CellValueChanging vs CellValueChanged.
Quoted from DevExpress Documentation:
The CellValueChanging event is raised each time the edited value is being changed (the user types or deletes a character, chooses a value from the dropdown list, etc.).
On the other hand, CellValueChanged is raised when user had done cell editing by hitting enter or clicking outside of the active cell.
Now my problem is, I have a combobox type column, and I want to always skip the CellValueChanging and make the change final. The current behavior is when user select an item from the combobox, the change doesn't take effect immediately(e.g, the view will not be resorted as per the change). The change is not accepted until user hit enter or click outside of the cell.
---------------07/26/2013 2:25PM update---------------
Sorry that my previous question description misled everybody, so I'll rephrase it.
Here is the current behavior:
Pic1: beginning state. Rows are sorted alphabetically by Target.
Pic2: Change the 2nd row value from B to D
Pic3: After a single mouse left click on item D, the dropdown disappears and the cell value changes to D. However, the rows are not resorted
Pic4: After clicking outside the cell or hitting enter, the rows are resorted.
What I want to achieve is to skip the step in Pic3. In other word, I want any changes committed immediately, without having to make an extra kepboard or mouse click.
What I am showing here is a simplified example of my application. I can't move my CellValueChanged event handler logic to CellValueChanging or EditValueChanged because it would cause some errors.

You want to make some UI change once user edit the cell value, right? I think generally you have to give up handling the CellValueChanged event, but use the CellValueChanging Event instead:
pseudocode:
HandleCellValueChanging(object sender, CellValueChangingEventArgs e)
{
// Get underlying object
// and write the value direct into the object
var rowObj = (YourType)gridview.GetRow(gridView.FocusedRowHandle);
rowObj.TargetField = e.NewValue; // e.Value or e.NewValue, not sure
// Then Do your UI effect
}
Another option is to use RepositoryItemEditor and handle the key-up and/or Mouse-up event instead.

If you want to to catch the moment when an end-user changes some value in gridview's cell editor (e.g. select item in combobox), please handle the EditValueChanged event of a corresponding RepositoryItem. To immediately post this value (make the changes final), you need to call the PostEditor method of a corresponding container:
repositoryItemComboBox1.EditValueChanged += repositoryItemComboBox1_EditValueChanged;
//...
void repositoryItemComboBox1_EditValueChanged(object sender, EventArgs e) {
gridView1.PostEditor();
// do something else if it needed
}
Related example: E3234 - How to update a row's style immediately after a an inplace editor's value is changed

Related

Repopulate DataGridView on CellEndEdit

I'm trying to update a value in the database after the user finishes editing a cell. The DataGridView is populated manually (and dynamically) due to some complexities involved in DataBinding this particular data set.
In the event handler for CellEndEdit, I have called my method to update the value back to the database, and it works as planned. The trouble occurs when I attempt to repopulate the DataGridView. For reference, this method is called PopulateAccounts().
Once a value is entered in the cell, everything works perfectly if the user presses Enter on their keyboard or clicks another control on the form. However, clicking on another cell in the same DataGridView results in the following error:
Operation is not valid because it results in a reentrant call to the SetCurrentCellAddressCore function.
The PopulateAccounts() method includes DataGridView.Rows.Clear() which throws the error. I've researched this error in a few related SO questions, and it seems to be related to threading, which I'm clueless about. One suggested fix is to invoke my PopulateAccounts() method:
BeginInvoke(new MethodInvoker(PopulateAccounts));
This works, but it causes the DataGridView to select all cells preceding the one that was edited (see screenshot below)
Again, this only happens when clicking out of the cell and into another. Otherwise, i.e. pressing Enter or clicking on another control, it simply selects the first cell.
Here is the code for PopulateAccounts() for reference:
// Populates the DataGridView's account records.
private void PopulateAccounts()
{
dgvShed.Rows.Clear();
using (PassShedEntities context = new PassShedEntities(conString))
{
foreach (Account acct in context.Account.Where(a => a.Category_id == (int)lstCategories.SelectedValue))
{
dgvShed.Rows.Add();
foreach (DataGridViewColumn col in dgvShed.Columns)
{
Credential cred = (from c in context.Credential
join f in context.Field on c.Field_id equals f.Id
join a in context.Account on c.Account_id equals a.Id
where c.Account_id == acct.Id
where f.Label == col.Name
select c).SingleOrDefault();
dgvShed.Rows[dgvShed.Rows.Count - 1].Cells[col.Name].Tag = cred.Id;
dgvShed.Rows[dgvShed.Rows.Count - 1].Cells[col.Name].Value = cred.Value;
}
}
}
}
Once a value is entered in the cell, everything works perfectly if the user presses Enter on their keyboard or clicks another control on the form
The above actions doesn't change the CurrentCell and the grid repopulation works fine.
However, clicking on another cell in the same DataGridView results in the following error:
When you click on another cell the CurrentCell of the datagridview is changing and grid internally calls the SetCurrentCellAddressCore to do the cell change. This call internally raises CellEndEdit event which triggers the SetCurrentCellAddressCore again and this cycle goes on resulting in infinite loop. The DataGridView detects this loop and throws exception resulting in the error you have mentioned.
In the above case the my guess is that DataGridView somehow goofed up the selection of cells. You could simply clear the selection at the end of PopulateAccounts with DataGridView.ClearSelection() method.
I've been wrestling with just about the exact same problem and just came up with a solution that works with my specifics. I moved all the code from the CellEndEdit event to the CellValueChanged event (which fires right before the former) and now both options for ending the cell-edit: the enter-stroke and clicking off to another cell, work great.
I'm not sure what the perfectly proper way to use the events is, but this seems to be working!

WPF DataGrid SelectedCellsChanged event fires twice

I have subscribed via
_itemsGrid.SelectedCellsChanged += Handle_SelectedCellsChangedEvent;
to the WPF DataGrid SelectedCellsChanged event and when logging via
private void Handle_SelectedCellsChangedEvent (object sender, SelectedCellsChangedEventArgs args)
{
Logger.DoLogging("args.AddedCells.Count={0}, args.RemovedCells.Count={1}.", args.AddedCells.Count, args.RemovedCells.Count);
...
}
I see that sometimes for one row selection change there are two calls:
1. AddedCells.Count = 4, RemovedCells.Count = 0
2. AddedCells.Count = 0, RemovedCells.Count = 4
Now this behavior might be unhandy or unexpected, but is in itself no problem.
My real problem is that in certain conditions the data grid forgets to unselect the removed cells.
This means after the event handler are called and processed, the former selected cells are still visually selected.
My assumption is that this forgetting is related to a dialog that I'm displaying to the user.
Has anybody else seen the data grid "forgetting" to unselect cells?
Thanks
Frank
The event not only encapsulates the action of notifying of cells being added to the selection (i.e. the SelectedCells), but it also notifies of cells being removed from the selection. This allows you to take action on both or either selection change types.
I believe this corresponds with the fact that NotifyCollectionChangedEventArgs has a single Action. So to show a change in the SelectedCells collection of the grid, items need to be both added and removed. The notification of this addition and removal is separated into to distinct notifications since the event does not support showing them as one.

Need to add DataGridViewCheckBoxColumn in a Winform Application

I need to put a Checkbox against each Row in a DataGridView. I am making use of DataGridViewCheckBoxColumn for the purpose. But here user is able to select more than 1 row. How to restrict the user from selecting multiple rows in a DataGridView?
You will have to do it yourself when the checkbox is checked.
If you want to respond immediately
when users click a check box cell, you
can handle the
DataGridView.CellContentClick event,
but this event occurs before the cell
value is updated. If you need the new
value at the time of the click, one
option is to calculate what the
expected value will be based on the
current value. Another approach is to
commit the change immediately, and
handle the
DataGridView.CellValueChanged event to
respond to it. 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.
Whichever method you use, when you handle the checkbox being checked, you will need to mark all the other checkboxes as unchecked. How you will do that depends on whether (and how) your DataGridView is data-bound.

DataGridView that always has one row selected

I'm using a DGV to show a list of images with text captions as a picklist. Their must always be a one and only one selection made in the list. I can't find a way to prevent the user from clearing the selection with a control-click on the selected row.
Is there a property in the designer I'm missing that could do this?
If I have to override the behavior in the mouse click events are there other ways the user could clear the current selection that need covered as well?
Is there a third approach I could take that's less cumbersome than my second idea?
The easiest way is to catch the SelectionChanged event and check to see if the user has unselected all rows. If so, reselect the previously selected row. Essentially, you're intercepting their action and switching the selection back. Something like this (code untested but you will get the idea):
DataGridViewRow last_selected_row;
private void dgv_SelectionChanged(object sender, EventArgs e)
{
if (dgv.SelectedRows.Count == 0)
last_selected_row.Selected = true;
else
last_selected_row = dgv.SelectedRows[0];
}
Depending on your application, it might be better to store the row index rather than a reference to the row itself. Also be sure to initialize last_selected_row and update it if you delete any rows.
Any other controls that hook the SelectionChanged event will need to safely handle the case that no rows are selected, in case they fire before the event that switches it back. They can just return immediately though, safe in the knowledge that SelectionChanged will fire again momentarily.
You could also subclass DataGridView and override the OnSelectionChanged method. Then you could reselect the last selected row before the event fires (it will fire when you call base.OnSelectionChanged).
A DGV got a property called multiselect, if you set it to false only one cell/row can be selected at a time.
Just handle the DataBindingComplete event of the datagridview like this:
private void datagridview1_DataBindingComplete(System.Object sender, System.Windows.Forms.DataGridViewBindingCompleteEventArgs e)
{
datagridview1.ClearSelection();
}

I lose my newly inserted row when I cancel the BeforeRowUpdate event

I am using a WinUltraGrid to enter new rows into our database. When a new row is being entered and the user finishes entering the data, I am checking for null values in the BeforeRowUpdate event which occurs when the user leaves that row by clicking the save button or by clicking another row in the grid. If any of the columns are null then I set e.cancel to true to cancel the event after displaying a user friendly message.
My problem is that when I return to the grid, my partially entered row is gone and I have to start over. Any ideas on how to keep the partial data in the new row at the top of my grid? I am using VS 2008 and C# .Net.
Thanks
In general (I don't have experience with the WinUltraGrid component), setting Cancel to true on an event args object does just what you're seeing - cancels the row update event. I suspect that the data is probably rebound after that, clearing the row. What you'd probably need to do is to set up some sort of post-processing after you set the cancel (though timing could be tricky, if the component is rebinding under the covers). What I assume might work would be to save the non-null values in a custom object or dictionary, then after the event is cancelled, go back, check the dictionary/custom object, and push the values into the new row again, setting focus as needed.

Categories