У меня есть DataGrid, привязанный к коллекции IEditableObject.
Теперь, когда я щелкаю два раза по ячейке, она открывается для редактирования.
Забавно: BeginEdit будет вызываться два раза. Иногда для одного и того же EditableObject, а иногда и для двух разных объектов (особенно когда я использую PgDn, пока не достигну конца DataGrid) сначала будет вызван правильный, а затем какой-то другой элемент из коллекции, который никогда раньше не был в фокусе .
EndEdit также вызывается дважды, но всегда для выбранного элемента, а не для неправильного.
Это известная проблема? Любые обходные пути, чтобы получить только (правильное) одно уведомление.
Ответ 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();
}