Как проверить обновление DataTable в обработчике событий RowChanging - PullRequest
0 голосов
/ 29 декабря 2011

У меня есть таблица данных WPF, связанная с таблицей данных.Я заполняю базовый набор данных из произвольной таблицы в базе данных.Я приложил к событиям DataTable RowChanging и RowChanged.Когда пользователь меняет строку, эти события запускаются и позволяют мне проверить строку.

Чтобы получить оптимальное поведение из DataGrid, мне кажется очевидным, что должно быть установлено сообщение e.Row.RowError и исключениевыбрасывается из обработчика событий RowChanging.У меня есть некоторый xaml, который помечает строку как ошибку в заголовке строки, так что она видна, и я получаю хорошую подсказку с сообщением об ошибке.Под оптимальным я подразумеваю, что escape-последовательности, которые ожидаются с этими сетками, работают как ожидалось, когда валидация обрабатывается, как описано.Попытка выполнить ту же проверку из события RowChanged приводит к некоторому причудливому поведению, которое не откатывает изменения должным образом.

Проблема, с которой я столкнулся, заключается в том, что мне нужно обновить базовый DataSet, чтобы все БДприменяются правила проверки, и в обработчике RowChanging можно обнаружить конфликты с изменениями других пользователей.Если действие не выполнено, я могу отметить валидацию, как описано.Но e.Row.RowState входит как неизмененный, и если я передаю содержащий его DataSet в метод обновления моей БД, его метод DataAdapter.Update(myDataTable) не видит строку как измененную и, следовательно, ничего не делает.Это поведение отличается от того, что происходит, когда я делаю то же самое в обработчике RowChanged.В этот момент значения записи (текущие / исходные / предлагаемые) обновляются, и запись помечается как измененная.

Обновление DataAdapter в этот момент приводит к активности базы данных.Но я ошибся в последовательности событий в случае сбоя.Я могу пометить ошибку, но поведение отката для сетки не будет работать правильно (обычно это приводит к тому, что измененные ячейки не откатываются).

У меня вопрос: как получить запись (или копиюзапись ??) в измененном состоянии, чтобы база данных была обновлена?Обычно я использую типизированный DataSet, но на этот раз я собираюсь выбрать произвольные таблицы и, следовательно, использую DataSet.

Ответы [ 2 ]

1 голос
/ 29 декабря 2011

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

0 голосов
/ 29 декабря 2011

Если вам просто нужно изменить RowState на модифицированный, вы бы вызвали метод DataRow.SetModified().

...