좋아, 좀 재미있어졌지만 마침내 해결했다. 핵심은 RowChanging
처리기의 & 수정 이벤트와 RowDeleted
처리기의 삭제 이벤트를 처리하는 것이 었습니다. 나는 다음 사람을 몇 시간 동안 머리를 긁적 없애기에 충분한 코드를 제시 할 것이다.
아래 코드에서 _dataSet은 DataAdapter를 통해 채워진 DataSet입니다. _dataTable은 _dataSet.Tables[0].DefaultView
입니다. _dataTable은 XAML의 DataGrid에 ItemsSource로 바인딩됩니다. 이 코드는 내 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을 만드는 것이 었습니다. 이 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;
}
위의 코드가 구현 된 경우 동작이 거의 올바른 것입니다. 보이는 문제는 레코드를 이동할 때 유효성 검사가 실패 할 때입니다.처음 작동 할 때, 즉 오류 마커가 행 머리글에 표시됩니다. 그러나 편집중인 것처럼 레코드로 이동했지만 값을 변경하지 않은 다음 다시 이동하면 오류 표시기가 사라집니다. 그러나 행으로 다시 이동하고 편집을 취소하기 전에 그리드의 다른 셀로 이동할 수 없습니다. 위해
그 행동의 권리를 얻을, 당신은 그리드에 대한 유효성 검사 규칙을 추가해야합니다 : 지금
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;
}
}
}
:
<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>
그런 다음에 코드 뒤에 다음을 추가 오류 표시가 제대로 작동합니다.
처리해야 할 마지막 문제는 자동 생성 색인 값을 중심으로 이루어집니다. 자동 생성 색인이있는 표가있는 경우 해당 입력란 및 다른 입력란에 다른 값을 입력하고 레코드를 커밋 (탭 해제 또는 반환) 할 수 있습니다. 격자보기가 새로 고쳐지면 다른 필드는 변경되었지만 키는 초기 값을 유지하고 있음을 알 수 있습니다. 다른 모든 행을 검색/새로 고치지 않고도 해당 레코드를 검색/다시 표시하는 방법을 알아야 할 것입니다.
이 노력으로 인해 표준 이스케이프 시퀀스를 통해 예상되는 취소 편집 동작이 유지됩니다. 즉, 레코드 유효성 검사가 실패하면 첫 번째 셀은 현재 셀 편집을 취소합니다. 두 번째 행 편집을 취소합니다.
공유하고 즐겨보세요!
편집 : 강력한 오류 표시를 얻으려면 XAML 및 코드 숨김에 사용 된 유효성 검사 규칙을 추가했습니다. 그런 긴 대답에 대해 유감입니다. 처음에이 모든 것을 알아 냈다면이 방법을 제시하기에 더 적합한 포럼을 선택했을 것입니다.
RowChanging 처리기에서 e.Row.SetModified()를 수행 할 때 발생하는 오류는 우리가 게시하는 사이트를 고려하면 아이러니합니다. ** StackOverflowException 처리되지 않았습니다 **. RowChanging에 대한 재귀를 방지하기 위해 플래그를 설정/관찰하면 값 (Current/Original/Proposed)이 변경되었지만 DB 업데이트 시도가 이루어지지 않습니다. DB를 업데이트하려는 시도는 RowChanged 핸들러에서 발생하지만 롤백 기능이 손실되었습니다. –