Child DataGridView redraw and reset to default when parent object change

Child DataGridView redraw and reset to default when parent object change

Old forum URL: forums.lhotka.net/forums/t/4284.aspx


wilfreds posted on Monday, February 04, 2008

I have a simple parent child relationship between two objects. It is the classic OrderHead - OrderRow relationship. The order rows are displayed in a DatagridView. In the grid view I also have product pictures. I'm trying to do the loading as efficient as possible and I have set the grid to VirtualMode=true and handle the CellValueNeeded event.

My problem is that when the user changes something in the parent OrderHead he triggers a full redraw of the child datagridview. And even worse, if the user changes something in the OrderRow that also will change something in the order head like TotalAmount he also triggers a full redraw of the grid while editing the grid. Every time this happens I then have to reload all the pictures.


You can easily see the behavior in the ProjectTracker vb project:
Go to "Projects->Edit project" and choose your favorite.
In the resources grid, change the height of some of the rows.
Then make a change in the "Description" field in the head.
Now, move away from the field to commit the change.
Now suddenly all the row heights are reset back to the default height in the grid.

Does anyone have a solution for this?

Thanks

Wilfred

wilfreds replied on Tuesday, February 05, 2008

I forgot to tell that it is a Win forms project.

So, to brake it down to a more simple question:
Why is it that when I set a property in a root object I have to reaply every properties in the bound grid control that is populated with child objects? Things that I have to set again are colors, size and unbound pictures.

Is it a problem with the CSLA framework?
Or the databinding core?
Or is it a problem with the DataGridView controll?

lukky replied on Friday, September 18, 2009

Hi,

I'd like to resurrect this thread as I'm seeing the same behavior.

Basically, modifying a property on the Parent BO causes the DataGridView (bound to child collection) to refresh. This happens, for example, if the Parent BO overrides OnChildChanged() and recalculates the total amount of the order/invoice. It will also happen if a property is modified on the Parent BO directly, for example through a bound TextBox.

Where it gets annoying is when the user is editing the item price in column 8 of the DGV, presses the down arrow to go to the next item's price, and *BAM*, the highlighted cell goes back to the first column, which is sure to generate a lot of complaints from users because they have to manually move the highlighted cell back to column #8 every time.

The scenario is:

How do I prevent the DGV from going back to column #1 every time the Parent BO changes ?

Thank you


lukky replied on Friday, September 18, 2009

Hi,

I've created a minimal test case to reproduce the behavior.

It will run against the Northwind database.

The DataGridView is bound to the OrderDetails, while the single TextBox is bound to a field on the OrderHeader.

If you select a cell in the rightmost column of the DGV, then move to the TextBox and modify its value, when you tab-off, the highlighted cell in the DGV moves back to column 1.

Though not implemented in this sample project, the same behavior is shown if the fact of modifying a child row updates a field on the Parent BO (through OnChildChanged() for example)

I think this should not be happening. I did another test with DataSets, and the behavior is not there.

What could be causing this ?

Thanks

Tom_W replied on Friday, September 18, 2009

Hey Luc

I just downloaded your sample and ran it up and I can confirm I'm getting the same results.

What's slightly interesting is that I added an Infragistics grid too and it doesn't exhibit that behaviour, focus stays on the previously selected cell.

This is slightly interesting for two reasons, first it suggests that maybe this is a DGV issue and second it's a rare example of the Infragistics grid actually behaving itself!

I tried using two bindingsources, but that didn't help and obviously there's nothing fancy going on in your classes so I'm at a loss I'm afraid.

lukky replied on Friday, September 18, 2009

Thanks Tom,

Playing with that beast, if I set RaiseListChangedEvents to false, then the grid behaves, but then I lose refreshing of the controls bound to the Parent BO.

That's funny, as I'd expect controls bound to the Parent to react to the BindingSource.CurrentItemChanged event as well. Apparently they don't.

I can't believe I'd be the first one to ever notice this Surprise [:O]


lukky replied on Sunday, September 20, 2009

Hi all,

for the benefit of anyone who might be fighting this same problem, I've come up with a solution. I came up with this idea after noticing that using the left/right arrow to leave the edited cell did cause the LineTotal and OrderTotal to be updated, but the DGV didn't reset the column to the first one. It seems that the problem only happens if the current ROW is changed, not the COLUMN.

Basically, I intercept arrow up/down and the return key in the PreviewKeyDown event handler of the EditControl that is shown when editing a DataGridView cell.

This somehow tricks the DGV to not move the CurrentCell back to column 1, which is annoying as hell when users are fast typing numbers in the grid.

Code looks like this (still prototype):


Private Sub dgvDetail_EditingControlShowing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles dgvDetail.EditingControlShowing
        RemoveHandler e.Control.PreviewKeyDown, AddressOf EditControl_PreviewKeyDown
        AddHandler e.Control.PreviewKeyDown, AddressOf EditControl_PreviewKeyDown
End Sub

Private Sub EditControl_PreviewKeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PreviewKeyDownEventArgs)
        If _editMode = EditMode.Edit Then

            Dim row As Integer = dgvDetail.CurrentCell.RowIndex
            Dim col As Integer = dgvDetail.CurrentCell.ColumnIndex

            If (e.KeyCode = Keys.Down Or e.KeyCode = Keys.Return) And row < dgvDetail.Rows.Count - 1 Then
                dgvDetail.EndEdit()
                dgvDetail.CurrentCell = dgvDetail(col, row + 1)
            End If
            If e.KeyCode = Keys.Up And row > 0 Then
                dgvDetail.EndEdit()
                dgvDetail.CurrentCell = dgvDetail(col, row - 1)
            End If
        End If
End Sub

This is admittedly a hack, but it seems to be working for now.

The problem is caused by the fact that when the Parent BO changes, it raises the ListChanged event, and the DGV reacts to this by refreshing itself and resetting the current column to 0.

This really looks like a "loophole" in the DGV, so I don't really think there's a CSLA way of solving it.

Any further thoughts ?

Regards

Tom_W replied on Monday, September 21, 2009

Glad you've got a fix.

My only other thought on this is what happens if the DGV is bound to a non CSLA collection, i.e. not a datatable/whatever, but another custom collection that inherits say BindingList(T) so has all the binding interfaces baked in (ICAN, IEO etc) - does the DGV still do the same thing?

If so, then as you rightly say there must have been others who have hit this problem and there maybe be some fixes out there on the internet somewhere.  Whether it's worth hunting down depends on how much the hack-fix annoys you!

Copyright (c) Marimer LLC