Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: SamОтветов: 4

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

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

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

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

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

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

datagridwpfieditableobject

Ответы

Ответ 1 · рейтинг 24 · Ian Griffiths

Если вы посмотрите на трассировку стека в отладчике при вызове 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 для случайных объектов, как вы описали. Я не могу воспроизвести это. Но это объясняет двойные обращения к выбранному объекту. Кажется, лучше всего закодировать ваши исходные объекты так, чтобы они допускали двойные вызовы.

Ответ 2 · рейтинг 4 · Riegardt Steyn

Я не уверен, что бы вы использовали, чтобы прервать событие 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

Ответ 3 · рейтинг 3 · Vanja Andreev

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

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

Добавлять

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

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

Ответ 4 · рейтинг 1 · Adrian Ratnapala

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

void BeginEdit()
{
    _numEdits++;
}

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

void EndEdit()
{
    CancelEdit();
    if(_numEdits == 0)
        commitEdit();
}

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы