Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF DataGrid calls BeginEdit on an IEditableObject two times?

I've got a DataGrid bound to a collection of IEditableObject's.

Now when I click two times in a cell, it will be opened for editing.

Funny thing is: BeginEdit will be called two times. Sometimes for the same EditableObject, but sometimes for two different objects (especially when I use PgDn until I hit the end of the DataGrid) the correct one will be called first, then some other item from the collection, which never had been in focus before.

EndEdit is called twice, too, but always for the selected Item, not for the wrong one.

Is this a known problem? Any workarounds to get only (the right) one notification.

like image 597
Sam Avatar asked Dec 15 '10 14:12

Sam


4 Answers

If you look at the stack trace in the debugger when BeginEdit is called, you'll see that the first time, it's the collection view calling it, and the second time, it's a BindingGroup.

The problem appears to be that there are two things that both think they are in charge of the IEditableObject state. When WPF provides a default collection view, it will look for IEditableObject on the objects in the collection, and will call BeginEdit and either EndEdit or CancelEdit in response to calls to the corresponding IEditableCollectionView methods. But also, the BindingGroup will call the IEditableObject methods in response to calls to BeginEdit and CommitEdit or CancelEdit.

The DataGrid uses both features: when you start and complete edits in a row, it notifies the IEditableCollectionView and the BindingGroup and both of those things think that it's their responsibility in turn to go on and notify the IEditableObject implementation on the underlying source object.

So it looks rather like a bug in the DataGrid - it causes two different objects to call BeginEdit (and related methods). And it's because it makes use of editable collection views and binding groups - from the look of it, those weren't designed to be used at the same time on the same objects, in the way that the DataGrid uses them.

The reason you don't see this problem with the grid in the Toolkit is that it appears to be a slightly older version - comparing the code in that with what Reflector shows for .NET 4.0, you'll see that the .NET 4.0 DataGrid has some extra code (a new method, EnsureItemBindingGroup, and some related code in MeasureOverride and OnRowValidationRulesChanged) that ensures that a binding group always exists, whether you ask for it or not. So if the WPF Toolkit is updated, it'll probably grow a similar feature unless this gets fixed. (And I would guess that if you use the current edition - February 2010 as I write this - of the WPF Toolkit, and you use the ItemBindingGroup property to ask explicitly for a binding group, you'd see exactly the same problem.)

This doesn't explain how you'd get calls to BeginEdit on random objects as you've described. I can't repro that. But it does explain double calls on the selected object. The best thing to do appears to be to code your source objects so that they will tolerate the double calls.

like image 68
Ian Griffiths Avatar answered Jan 02 '23 03:01

Ian Griffiths


I'm not sure what you'd use to interrupt the BeginEdit event before it happens, but for EndEdit a simple isDirty marker will do. In your Entity class that implements IEditableObject, add the following:

    private bool _isDirty = false;

    #region IEditableObject Members

    public void BeginEdit()
    {
        // Bug Fix: Windows Controls call EndEdit twice; Once
        // from IEditableCollectionView, and once from BindingGroup.
        // This makes sure it only happens once after a BeginEdit.
        _isDirty = true;
    }

    public void CancelEdit() { }

    public void EndEdit()
    {
        if (ItemEndEdit != null && _isDirty)
        {
            _isDirty = false;
            ItemEndEdit(this);
        }
    }

    #endregion
like image 31
Riegardt Steyn Avatar answered Jan 02 '23 05:01

Riegardt Steyn


I have same problem using .NET Framework 4 DataGrid.

Add Reference to last version of WPFToolkit

Add

xmlns:dg="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"

and change <DataGrid> with <dg:DataGrid>

like image 22
Vanja Andreev Avatar answered Jan 02 '23 03:01

Vanja Andreev


+1 @IanGriffiths for diagonsing the Problem. As for a solution (or workaround rather), you can count the number of "pending" Edits. That means something like:

void BeginEdit()
{
    _numEdits++;
}

void CancelEdit()
{
    if(--_numEdits < 0)
        throw new Exception("WTF?"); 
}

void EndEdit()
{
    CancelEdit();
    if(_numEdits == 0)
        commitEdit();
}
like image 24
Adrian Ratnapala Avatar answered Jan 02 '23 03:01

Adrian Ratnapala