WPF DataGrid дважды вызывает BeginEdit для IEditableObject?

У меня есть DataGrid, привязанный к коллекции IEditableObject.

Теперь, когда я щелкаю два раза по ячейке, она открывается для редактирования.

Забавно: BeginEdit будет вызываться два раза. Иногда для одного и того же EditableObject, а иногда и для двух разных объектов (особенно когда я использую PgDn, пока не достигну конца DataGrid) сначала будет вызван правильный, а затем какой-то другой элемент из коллекции, который никогда раньше не был в фокусе .

EndEdit также вызывается дважды, но всегда для выбранного элемента, а не для неправильного.

Это известная проблема? Любые обходные пути, чтобы получить только (правильное) одно уведомление.


person Sam    schedule 15.12.2010    source источник
comment
Звучало интересно, поэтому я проверил свой DataGrid, который также привязан к ObservableCollection‹IEditableObject›, и не столкнулся с этим...? Мое представление - это один объект в строке, вы случайно не объединяете объекты, которые оба реализуют IEditableObject или что-то подобное?   -  person Aaron McIver    schedule 15.12.2010
comment
Интересно. Мой объект реализует только IEditableObject и INotifyPropertyChanged, поэтому слияния нет. Однако моя ObservableCollection заключена в собственный ListCollectionView. И я привязал столбец к свойству в классе в моем IEditableObject, а не напрямую к свойству IEditableObject. По крайней мере, теперь у меня есть причина проверить мой источник, спасибо!   -  person Sam    schedule 15.12.2010
comment
Примеры MSDN всегда включают флаг, который не позволяет BeginEdit выполнять свои действия при вызове в любое время, кроме первого. И по моему опыту, да, это называется несколько раз.   -  person peterG    schedule 02.03.2015


Ответы (4)


Если вы посмотрите на трассировку стека в отладчике при вызове BeginEdit, вы увидите, что в первый раз его вызывает представление коллекции, а во второй раз — BindingGroup.

Проблема, по-видимому, в том, что есть две вещи, которые оба думают, что они отвечают за состояние IEditableObject. Когда WPF предоставляет представление коллекции по умолчанию, он будет искать IEditableObject в объектах коллекции и вызывать BeginEdit и либо EndEdit, либо CancelEdit в ответ на вызовы соответствующих IEditableCollectionView методов. Но также BindingGroup будет вызывать методы IEditableObject в ответ на вызовы BeginEdit и CommitEdit или CancelEdit.

DataGrid использует обе функции: когда вы начинаете и завершаете редактирование подряд, он уведомляет IEditableCollectionView и BindingGroup, и обе эти вещи считают, что это их обязанность, в свою очередь, продолжать и уведомлять реализацию IEditableObject на базовом исходном объекте.

Таким образом, это похоже на ошибку в DataGrid — она заставляет два разных объекта вызывать BeginEdit (и связанные методы). И это потому, что он использует редактируемые представления коллекции и группы привязки - судя по всему, они не были предназначены для одновременного использования на одних и тех же объектах, как их использует DataGrid.

Причина, по которой вы не видите этой проблемы с сеткой в ​​Toolkit, заключается в том, что она выглядит немного более старой версией — сравнивая код в ней с тем, что показывает Reflector для .NET 4.0, вы увидите, что .NET 4.0 DataGrid имеет некоторый дополнительный код (новый метод, EnsureItemBindingGroup, и некоторый связанный код в MeasureOverride и OnRowValidationRulesChanged), который гарантирует, что группа привязки всегда существует, запрашиваете вы это или нет. Поэтому, если WPF Toolkit будет обновлен, он, вероятно, расширит аналогичную функцию, если это не будет исправлено. (И я предполагаю, что если вы используете текущую версию (февраль 2010 г., когда я пишу это) набора инструментов WPF и используете свойство ItemBindingGroup для явного запроса группы привязки, вы увидите точно такую ​​​​же проблему.)

Это не объясняет, как вы получаете вызовы BeginEdit для случайных объектов, как вы описали. Я не могу воспроизвести это. Но это объясняет двойные обращения к выбранному объекту. Кажется, лучше всего закодировать ваши исходные объекты так, чтобы они допускали двойные вызовы.

person Ian Griffiths    schedule 03.01.2011

Я не уверен, что бы вы использовали, чтобы прервать событие BeginEdit до того, как оно произойдет, но для EndEdit подойдет простой маркер isDirty. В свой класс Entity, который реализует IEditableObject, добавьте следующее:

    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
person Riegardt Steyn    schedule 19.06.2013
comment
Вы должны установить _isDirty=false в методе CancelEdit. - person white.zaz; 16.02.2015

У меня такая же проблема с использованием .NET Framework 4 DataGrid.

Добавить ссылку на последнюю версию WPFToolkit

Добавлять

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

и замени <DataGrid> на <dg:DataGrid>

person Vanja Andreev    schedule 17.12.2010
comment
Круто и интересно. Это другая версия или почему это решает проблему?? - person Sam; 17.12.2010

+1 @IanGriffiths за диагностику проблемы. Что касается решения (или, скорее, обходного пути), вы можете подсчитать количество «ожидающих» правок. Это означает что-то вроде:

void BeginEdit()
{
    _numEdits++;
}

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

void EndEdit()
{
    CancelEdit();
    if(_numEdits == 0)
        commitEdit();
}
person Adrian Ratnapala    schedule 12.05.2014