지난 포스팅에서 그냥 체크박스 열을 추가해버리면 된다 라는 식의 해결책을 언급했었는데, 실제로 간단히 구현해 보았습니다.
(실제 행 선택 로직은 추후 업데이트 예정)
관련글 : https://waydd.tistory.com/3
1. 제네릭 bool 처리
데이터베이스에서 GridView로 데이터를 바인딩할 때는 bool 타입이 없는 경우가 많습니다.
그래서 아래 코에서는 true/false 값을 제네릭으로 처리할 수 있도록 구성했습니다.
또한, GridViewColumnHelper를 사용하면 체크박스뿐 아니라 다른 RepositoryItem도 간단히 생성할 수 있습니다.
2. CustomUnboundColumnData 이벤트
체크박스 열을 UnboundColumn 으로 구성하면 데이터 표시 및 변경 시점에 따라 다음과 같이 이벤트를 처리할 수 있습니다.
- e.IsGetData == true
- GridView가 데이터를 표시 해야 할 때 호출됩니다.
- 열이 화면에 렌더링 되거나 RefreshRow가 호출될 때 발생합니다.
- e.IsSetData == true
- 사용자가 Unbound 열의 데이터를 수정 할 경우 호출됩니다.
- 이때 변경된 데이터를 직접 데이터소스에 반영 해야 합니다.
→ 기본적으로 UI와 데이터 간 자동 동기화가 없으므로, 별도의 로직 구현이 필요합니다.
※ UnboundColumn 사용시 주의사항
- FieldName : 열의 필드 이름을 반드시 지정
- UnboundType : 명시적으로 설정 필요
- FieldName은 기본 데이터소스의 필드와 절대 중복되면 안됨
- 참고 : https://supportcenter.devexpress.com/ticket/details/q552716/the-customunboundcolumndata-gridview-event-is-not-firing
3. CellValueChanging 이벤트
- 편집 종료 직전, 편집기가 닫히기 전에 발생
- 이 시점에서 데이터소스를 직접 변경하는 방식으로 동작시킬 수 있습니다.
위 구조로 구현한 코드는 아래와 같으며, 체크박스 UI는 자유롭게 다루면서도 실제 행 선택 로직은 이후에 연결할 수 있는 확장성을 확보할 수 있습니다.
코드 구현 )
class GridViewColumnHelper {
protected readonly GridView _gridView;
public GridViewColumnHelper(GridView gridView) {
_gridView = gridView ?? throw new ArgumentNullException(nameof(gridView));
}
public void AddRepositoryColumn(string fieldName, string caption, RepositoryItem repositoryItem, int visibleIndex) {
if (string.IsNullOrEmpty(fieldName))
throw new ArgumentException("Field name cannot be null or empty.", nameof(fieldName));
if (repositoryItem == null)
throw new ArgumentNullException(nameof(repositoryItem));
var gridColumn = _gridView.Columns.AddField(fieldName);
gridColumn.Caption = caption;
gridColumn.VisibleIndex = visibleIndex;
gridColumn.ColumnEdit = repositoryItem;
gridColumn.Visible = true;
}
public T CreateRepositoryItem<T>() where T : RepositoryItem, new() {
return new T();
}
}
class CheckBoxColumnHelper<T> : GridViewColumnHelper {
private readonly Dictionary<int, T> _checkboxStates = new Dictionary<int, T>();
private readonly Func<bool, T> _valueTrueConverter;
private readonly Func<bool, T> _valueFalseConverter;
private readonly Func<T, bool> _stateToBoolConverter;
private string _checkBoxFieldName;
public CheckBoxColumnHelper(GridView gridView, Func<bool, T> valueTrueConverter, Func<bool, T> valueFalseConverter, Func<T, bool> stateToBoolConverter)
: base(gridView) {
_valueTrueConverter = valueTrueConverter ?? throw new ArgumentNullException(nameof(valueTrueConverter));
_valueFalseConverter = valueFalseConverter ?? throw new ArgumentNullException(nameof(valueFalseConverter));
_stateToBoolConverter = stateToBoolConverter ?? throw new ArgumentNullException(nameof(stateToBoolConverter));
_gridView.CustomUnboundColumnData += GridView_CustomUnboundColumnData;
_gridView.CellValueChanging += GridView_CellValueChanging;
// MultiSelection 모드 활성화
_gridView.OptionsSelection.MultiSelect = true;
_gridView.OptionsSelection.MultiSelectMode = GridMultiSelectMode.RowSelect;
}
public void AddCheckBoxColumn(string fieldName, string caption, int visibleIndex) {
_checkBoxFieldName = fieldName;
RepositoryItemCheckEdit checkEdit = CreateRepositoryItem<RepositoryItemCheckEdit>();
checkEdit.ValueChecked = _valueTrueConverter(true);
checkEdit.ValueUnchecked = _valueFalseConverter(false);
checkEdit.AllowGrayed = false;
AddRepositoryColumn(fieldName, caption, checkEdit, visibleIndex);
_gridView.Columns[fieldName].UnboundType = DevExpress.Data.UnboundColumnType.Object; // 제네릭 타입을 사용하므로 Object
_gridView.Columns[fieldName].UnboundDataType = typeof(T);
}
private void GridView_CustomUnboundColumnData(object sender, DevExpress.XtraGrid.Views.Base.CustomColumnDataEventArgs e) {
if (e.Column.FieldName == _checkBoxFieldName) {
if (e.IsGetData) {
e.Value = _checkboxStates.ContainsKey(e.ListSourceRowIndex)
? _checkboxStates[e.ListSourceRowIndex]
: _valueFalseConverter(false); // 기본값
} else if (e.IsSetData) {
_checkboxStates[e.ListSourceRowIndex] = (T)e.Value;
}
}
}
private void GridView_CellValueChanging(object sender, DevExpress.XtraGrid.Views.Base.CellValueChangedEventArgs e) {
if (e.Column.FieldName == _checkBoxFieldName) {
_gridView.SetRowCellValue(e.RowHandle, _checkBoxFieldName, (T)e.Value);
_gridView.RefreshRow(e.RowHandle);
}
}
}
사용 예시)
var checkBoxHelper = new CheckBoxColumnHelper<int>(
gridView1,
valueTrueConverter: isChecked => 1,
valueFalseConverter: isChecked => 0,
stateToBoolConverter: state => state == 1
);
checkBoxHelper.AddCheckBoxColumn("Selection", "Int Column", 0);
참일때 값, 거짓일때의 값, 참일때의 수식(함수) 를 매개변수로 받습니다.
'Development > .NET' 카테고리의 다른 글
| 커스텀 체크박스 열 추가 (수정) (0) | 2024.12.04 |
|---|---|
| VisibleIndex와 실제 컬럼 구조의 차이 (UI 구조와 데이터 구조) (0) | 2024.11.28 |
| SelectionChanged 이벤트와 CollectionChangeAction.Refresh의 필요성 (0) | 2024.11.21 |
| 이벤트를 구독해서 값 변경을 감지하기 (C#) (0) | 2024.11.20 |
| DevExpress DetailBand의 FillEmptySpace 속성 이해와 활용 (0) | 2024.11.12 |