ОК, стало немного интересно, но я наконец-то все понял.Ключ должен был обработать события добавления и изменения в обработчике RowChanging
и событие удаления в обработчике RowDeleted
.Я представлю достаточно кода, чтобы сэкономить следующему человеку несколько часов от царапин на голове.
В приведенном ниже коде _dataSet - это DataSet, заполненный через DataAdapter._dataTable - _dataSet.Tables[0].DefaultView
._dataTable привязывается к DataGrid в XAML как ItemSource.Этот код находится в моей ViewModel, но он также мог быть и в коде модели.Я немного сократил его, так что, возможно, придется настроить его так, чтобы он работал для вас в коде.
private void AttachDataTableEvents()
{
_dataTable.RowChanging += new DataRowChangeEventHandler(DataTable_RowChanging);
_dataTable.RowChanged += new DataRowChangeEventHandler(DataTable_RowChanged);
_dataTable.RowDeleting += new DataRowChangeEventHandler(DataTable_RowDeleting);
_dataTable.RowDeleted += new DataRowChangeEventHandler(DataTable_RowDeleted);
}
private void DataTable_RowChanging(object sender, DataRowChangeEventArgs e)
{
Trace.WriteLine(string.Format("DataTable_RowChanging(): Action {0}, RowState {1}", e.Action, e.Row.RowState));
if (e.Action == DataRowAction.Add)
{
e.Row.ClearErrors();
DataTable updateDataTable = CreateUpdateDataTableForRowAdd(_dataSet, 0, e.Row);
int rowsAffected;
string errorMessage;
if (!UpdateTableData(updateDataTable, out rowsAffected, out errorMessage))
{
e.Row.RowError = errorMessage;
throw new ArgumentException(errorMessage);
}
}
else if (e.Action == DataRowAction.Change)
{
e.Row.ClearErrors();
DataTable updateDataTable = CreateUpdateDataTableForRowChange(_dataSet, 0, e.Row);
int rowsAffected;
string errorMessage;
if (!UpdateTableData(updateDataTable, out rowsAffected, out errorMessage))
{
e.Row.RowError = errorMessage;
throw new ArgumentException(errorMessage);
}
}
}
private void DataTable_RowChanged(object sender, DataRowChangeEventArgs e)
{
Trace.WriteLine(string.Format("DataTable_RowChanged(): Action {0}, RowState {1}", e.Action, e.Row.RowState));
if (e.Action == DataRowAction.Add)
{
e.Row.AcceptChanges();
}
else if (e.Action == DataRowAction.Change)
{
e.Row.AcceptChanges();
}
}
private void DataTable_RowDeleting(object sender, DataRowChangeEventArgs e)
{
Trace.WriteLine(string.Format("DataTable_RowDeleting(): Action {0}, RowState {1}", e.Action, e.Row.RowState));
// can't stop the operation here
}
private void DataTable_RowDeleted(object sender, DataRowChangeEventArgs e)
{
Trace.WriteLine(string.Format("DataTable_RowDeleted(): Action {0}, RowState {1}", e.Action, e.Row.RowState));
DataTable updateDataTable = CreateUpdateDataTableForRowDelete(_dataSet, 0, e.Row);
int rowsAffected;
string errorMessage;
if (!UpdateTableData(updateDataTable, out rowsAffected, out errorMessage))
{
e.Row.RejectChanges();
Mediator mediator = _iUnityContainer.Resolve<Mediator>();
mediator.NotifyColleagues<string>(MediatorMessages.NotifyViaModalDialog, errorMessage);
}
else
{
e.Row.AcceptChanges();
}
}
Ключом было создание новой таблицы данных с записью для обновления.Этот DataTable затем передается в метод DataAdapter.Update (dataTable).Для событий добавления / изменения / удаления был создан клон схемы DataSet, а затем в DataTable была добавлена запись в правильном состоянии.Три вспомогательные функции, показанные ниже, вернули DataTable с записью в соответствующем состоянии и с правильной информацией столбца в текущих / исходных / предлагаемых членах.
private static DataTable CreateUpdateDataTableForRowAdd(DataSet originalDataSet, int originalDataTableIndex, DataRow addedDataRow)
{
DataSet updateDataSet = originalDataSet.Clone();
DataTable updateDataTable = updateDataSet.Tables[originalDataTableIndex];
DataRow dataRow = updateDataTable.NewRow();
int columnCount = updateDataTable.Columns.Count;
for (int i = 0; i < columnCount; ++i)
{
dataRow[i] = addedDataRow[i, DataRowVersion.Proposed];
}
updateDataTable.Rows.Add(dataRow);
// dataRow state is *Added*
return updateDataTable;
}
private static DataTable CreateUpdateDataTableForRowChange(DataSet originalDataSet, int originalDataTableIndex, DataRow changedDataRow)
{
DataSet updateDataSet = originalDataSet.Clone();
DataTable updateDataTable = updateDataSet.Tables[originalDataTableIndex];
DataRow dataRow = updateDataTable.NewRow();
int columnCount = updateDataTable.Columns.Count;
for (int i = 0; i < columnCount; ++i)
{
dataRow[i] = changedDataRow[i, DataRowVersion.Original];
}
updateDataTable.Rows.Add(dataRow);
dataRow.AcceptChanges();
dataRow.BeginEdit();
for (int i = 0; i < columnCount; ++i)
{
dataRow[i] = changedDataRow[i, DataRowVersion.Proposed];
}
dataRow.EndEdit();
// dataRow state is *Modified*
return updateDataTable;
}
private static DataTable CreateUpdateDataTableForRowDelete(DataSet originalDataSet, int originalDataTableIndex, DataRow deletedDataRow)
{
DataSet updateDataSet = originalDataSet.Clone();
DataTable updateDataTable = updateDataSet.Tables[originalDataTableIndex];
DataRow dataRow = updateDataTable.NewRow();
int columnCount = updateDataTable.Columns.Count;
for (int i = 0; i < columnCount; ++i)
{
dataRow[i] = deletedDataRow[i, DataRowVersion.Original];
}
updateDataTable.Rows.Add(dataRow);
dataRow.AcceptChanges();
dataRow.Delete();
// dataRow state is *Deleted*
return updateDataTable;
}
Если приведенный выше код реализован, поведениепочти правильно.Проблема, которая видна, заключается в том, что при сбое проверки происходит сбой проверки.Первый раз, когда он работает, т. Е. Маркер ошибки отображается в заголовке строки.Однако, если вы перемещаетесь в запись, как будто редактируете, но не меняете никаких значений, а затем снова выключаете, индикатор ошибки гаснет.Однако вам по-прежнему запрещается перемещаться в другую ячейку в сетке, прежде чем вернуться к строке и отменить редактирование.
Для правильного поведения вам необходимо добавить правило проверки для сетки:
<DataGrid Grid.Column="1" Grid.Row="1" AutoGenerateColumns="True" ItemsSource="{Binding TableDataView}" Name="_gridTableGrid" CanUserDeleteRows="True" CanUserAddRows="True" RowHeaderWidth="25" CanUserResizeRows="False">
<DataGrid.RowValidationRules>
<local:DataGridRowValidationRule ValidationStep="CommittedValue" />
</DataGrid.RowValidationRules>
</DataGrid>
Затем в выделенном фрагменте кода добавьте следующее:
public class DataGridRowValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
BindingGroup bindingGroup = (BindingGroup)value;
if (bindingGroup.Items.Count > 0)
{
System.Data.DataRowView dataRowView = bindingGroup.Items[0] as System.Data.DataRowView;
if (dataRowView.Row.HasErrors)
{
string errorMessage = string.IsNullOrWhiteSpace(dataRowView.Row.RowError) ? "There is an unspecified error in the row" : dataRowView.Row.RowError;
return new ValidationResult(false, errorMessage);
}
else
{
return ValidationResult.ValidResult;
}
}
else
{
return ValidationResult.ValidResult;
}
}
}
Теперь индикация ошибок работает надежно.
Последняя проблема, которую необходимо устранитьс вращается вокруг автоматически сгенерированных значений индекса.Если есть таблица с автоматически сгенерированным индексом, можно ввести другое значение в этом поле, а также в других полях и зафиксировать запись (убрать ее или вернуть).Если вид сетки обновится, мы увидим, что другие поля изменились, но ключ сохранил свое первоначальное значение.Мне придется выяснить, как извлечь / отобразить эту запись без необходимости извлекать / обновлять все другие строки (произвольное и, возможно, большое число).
Это усилие привело к сохранению поведения отмены редактирования, ожидаемого пристандартная escape-последовательность.Т.е., если проверка записи не удалась, первая отменяет текущее редактирование ячейки;вторая отменяет редактирование строки.
Поделитесь и наслаждайтесь!
Редактировать: я добавил правило проверки, используемое в XAML, и код, чтобы получить надежную индикацию ошибки.Извините за такой длинный ответ.Если бы я понял все это в первую очередь, я бы выбрал более подходящий форум для представления метода.