2011-12-28 4 views
0

DataTable에 바인딩 된 WPF DataGrid가 있습니다. 필자는 데이터베이스의 임의의 테이블에서 기본 DataSet을 채 웁니다. DataTable RowChangingRowChanged 이벤트에 모두 첨부했습니다. 사용자가 행을 변경하면 이러한 이벤트가 발생하고 행의 유효성을 검사 할 수 있습니다.RowChanging 이벤트 처리기에서 DataTable 업데이트를 테스트하는 방법

DataGrid에서 최적의 동작을 얻으려면 e.Row.RowError 메시지를 설정하고 RowChanging 이벤트 처리기에서 예외를 발생시켜야합니다. 행 헤더에 행을 오류로 표시하는 일부 xaml이 표시되고 오류 메시지와 함께 유용한 툴팁이 표시됩니다. 최적으로 말하면,이 그리드에서 예상되는 이스케이프 시퀀스는 설명 된대로 유효성 검사가 처리 될 때 예상대로 작동 함을 의미합니다. RowChanged 이벤트에서 동일한 유효성 검사를 수행하면 편집을 제대로 롤백하지 않는 펑키 한 동작이 발생합니다.

문제점은 모든 DB 유효성 검사 규칙이 적용되고 다른 사용자의 변경 사항과 충돌이 RowChanging 처리기에서 감지 될 수 있도록 기본 DataSet을 업데이트해야한다는 것입니다. 작업이 실패하면 설명 된대로 유효성 검사에 플래그를 지정할 수 있습니다. 그러나 e.Row.RowState은 Unchanged로 제공되며 포함하는 DataSet을 DB 업데이트 메서드에 전달하면 해당 DataAdapter.Update(myDataTable) 메서드는 변경된 행을 보지 않으며 아무 것도 수행하지 않습니다. 이 동작은 RowChanged 처리기에서 동일한 작업을 수행 할 때 일어나는 것과는 대조됩니다. 이 시점에서 레코드 (현재/원래/제안 됨) 값이 업데이트되고 레코드가 수정 됨으로 표시됩니다.

그 시점에서 DataAdapter를 업데이트하면 데이터베이스가 활성화됩니다. 하지만, 실패의 경우 이벤트 순서의 잘못된 지점에 있습니다. 오류를 플래그 할 수 있지만 표의 롤백 동작이 올바르게 작동하지 않습니다 (일반적으로 변경된 셀이 롤백되지 않음).

제 질문은 수정 된 상태로 레코드를 가져 와서 (또는 레코드의 사본을 어떻게 얻습니까?) 데이터베이스가 업데이트된다는 것입니다. 저는 일반적으로 형식화 된 데이터 집합을 사용하지만 이번에는 임의의 테이블을 뒤쫓고 있으므로 DataSet을 사용하고 있습니다.

답변

0

변경하려면 RowState를 변경해야한다면 DataRow.SetModified() 메서드를 호출하면됩니다.

+0

RowChanging 처리기에서 e.Row.SetModified()를 수행 할 때 발생하는 오류는 우리가 게시하는 사이트를 고려하면 아이러니합니다. ** StackOverflowException 처리되지 않았습니다 **. RowChanging에 대한 재귀를 방지하기 위해 플래그를 설정/관찰하면 값 (Current/Original/Proposed)이 변경되었지만 DB 업데이트 시도가 이루어지지 않습니다. DB를 업데이트하려는 시도는 RowChanged 핸들러에서 발생하지만 롤백 기능이 손실되었습니다. –

0

좋아, 좀 재미있어졌지만 마침내 해결했다. 핵심은 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 및 코드 숨김에 사용 된 유효성 검사 규칙을 추가했습니다. 그런 긴 대답에 대해 유감입니다. 처음에이 모든 것을 알아 냈다면이 방법을 제시하기에 더 적합한 포럼을 선택했을 것입니다.

관련 문제