고정 컬럼 갯수를 파라미터로 입력하면 입력된 헤더 정보 중 Visible 값이 true인 컬럼만 확인해서 고정 컬럼의 마지막 인덱스 값을 반환하는 메소드이다.
/// <summary>
/// FixedCol 수에 맞는 Header의 Index값을 리턴 /// fixedCol 2이지만 실제 Header Column의 Visible 값이 false인 경우 제외하고 실제 Header Column의 Index를 구한다.
/// </summary>
protected int GetLastFixedCol(int colFixed) { int lastFixedCol = 0; int startIndex = 0; foreach (Header head in _headers) { if (startIndex == colFixed) break; if (head.Visible) { lastFixedCol = head.Index; startIndex++; } } return lastFixedCol; }
Grid.cs 파일의 DrawHeader 메소드에 기존의 DrawHeaders 메소드 외에 파라미터로 colFixed가 추가된 DrawHeader를 구현하면 된다.
고정 컬럼의 폭 만큼은 남겨두고 고정 컬럼 이 후의 컬럼들이 가로 스크롤바로 움직였을 경우 사용자에게 보여지도록 로직을 추가해서 만들면 된다.
좀 더 자세히 설명을 한다면
고정 컬럼의 헤더를 그린다
고정 컬럼 이후의 컬럼들에 대해서는 고정 컬럼 전체의 폭 이후 부터 그리도록 처리한다.
이 부분의 코드는 아래의 소스를 참고하기 바란다.
지금까지 작성한 코드에 많은 부분이 미진하지만 필자의 눈에 보완이 필요한 부분과 버그 부분을 조치하고자 한다. 지금 눈에 밟히는 미진함과 버그를 빨리 처리하지 않으면 뒤에 가서 쏟아 부어야 할 노력이 더 많다는 걸 알기에 생각 날때 가능한 빨리 조치하는 것이 정신건강에 좋다.
배우는 학생의 입장에서 WinForm기반의 User Control을 만들어가는 과정을 세세하게 기록해보고자 하는 바램도 있었기에 부끄럽지만 내부의 문제와 버그를 밖으로 드러내 처리한 결과를 같이 공유하고 나의 기록으로서 남기고자 한다.
기존에는 고정 컬럼에 대한 고려를 하지 않은 상태에서 코드를 작성하다 보니 고정 컬럼을 적용했을 때 많은 부분에서 문제가 되는 부분이 있었다.
주로 발생한 영역이 WANIGrid.Event.cs/WANIGrid.Method.cs 파일이었다.
물론 기존에도 문제가 되었지만 모르고 있다가 이번에 고정 컬럼 부분이 추가되면서 더 눈에 띄게 된 경우도 있다.
WANIGrid.Even.cs 파일에서는 화면을 출력하는 메소드인 WANIGrid_Paint 메소드의 DrawHeaders 메소드 호출 시에 입력 파라미터가 하나 더 추가되어 변경되었다.
앞에서 WANIGrid Control 내의 마우스 우측 버튼 클릭 시 나타나게 되는 Context 메뉴 부분을 마무리 하도록 하자.
먼저 WANIGrid 내의 마우스 우측 버튼 클릭 시 Context Menu를 제공할지 말지를 설정할 수 있도록 바꾸면 좋을 듯 싶다. 마우스 우측 버튼 클릭 시 Context Menu가 나타나지 않도록 설정을 부여해야만 할 경우도 있을 수 있기 때문이다. 예를 들어 데이터를 WANIGrid Control에 조회만 할 수 있도록 하고, 새로운 행(Row)을 추가/삭제할 수 없도록 해야 할 경우도 있기 때문이다.
이러한 설정을 할 수 있도록 WANIGrid.cs파일의 변수 설정 부분에 아래의 코드를 추가하도록 한다.
ContextMenu를 보여줄지 말지를 설정할 수 있는 변수인 bool타입의 contextMenuShow
private bool isShowContextMenu = true; //ContextMenu 제공 여부
Property 설정 부분에 아래의 Code를 추가한다. 이렇게 WANIGrid Control 내에서 Mouse 우측 버튼 클릭 시 Context Menu 제공 여부를 설정할 수 있는 변수와 Property를 작성했다.
///<summary> /// 마우스 우측 버튼 클릭 시 ContextMenu 제공 여부 ///</summary> public bool IsShowContextMenu { get { return isShowContextMenu; } set { isShowContextMenu = value; } }
Mouse Down 이벤트 메소드에서 Context Menu를 보여주는 로직을 처리하다 보니 소스 라인이 너무 길어지면서 가독성이 떨어지는 부분이 있어서 Context Menu를 보여주는 부분을 별도의 메소드로 분리를 하자.
선택된 행(Row)의 수가 없거나 1개일 경우, 1개 이상일 경우에 따라 사용할 수 있는 Contex Menu의 활성화/비활성화 부분을 추가한다.
선택된 행(Row)의 갯수가 0보다 작거나 같은 경우에는 "행 추가" 메뉴만 활성화
선택된 행(Row)의 갯수가 1개일 경우에는 모든 메뉴를 활성화
선택된 행(Row)의 갯수가 2개 이상일 경우 "행 추가", "행 삭제" 메뉴만 활성화
/// <summary> /// Context Menu를 보여준다. /// <param name="sender"></param> /// <param name="e"></param> /// </summary> private void ShowContextMenu(object sender, MouseEventArgs e) { if (!isShowContextMenu) return; //마우스 우측 버튼이 클릭된 좌표 값을 얻어온다. Point p = new Point(e.X, e.Y);
Mouse Down 이벤트 발생 시 호출되는 WANIGrid_MouseDown 이벤트를 아래와 같이 변경한다.
/// <summary> /// Mouse Down Event /// <param name="sender"></param> /// <param name="e"></param> /// </summary> private void WANIGrid_MouseDown(object sender, MouseEventArgs e) { //마우스 우측 버튼 클릭 시 Context 메뉴 제공 if (e.Button == MouseButtons.Right) { //WANIGrid Header 영역이 선택되어졌을 경우에는 메뉴 제공하지 않음. if (e.Y < grid.TopHeaderHeight) return;
ShowContextMenu(sender, e); } else if (e.Button == MouseButtons.Left) {
:
:
}
}
앞서 작성했던 Context Menu에 실제 클릭 이벤트 부분을 추가해 보도록 하자.
기존에 Context 메뉴는 만들었으나, 실제 동작하는 메뉴는 행 추가 메뉴 뿐이었다.
나머지 3개 메뉴에 대한 동작들도 같이 완성시켜 보도록 하겠다.
이전 행에 추가(OnMenu_BeforeInsertRow_Click), 다음 행에 추가(OnMenu_AfterInsertRow_Click), 행 삭제(OnMenu_DeleteRow_Click) 메뉴에 대한 이벤트 등록을 추가했다.
/// <summary> /// 마우스 우측 버튼 클릭시 제공 되는 메뉴 초기화 /// </summary> private void InitializeContextMenu() { //System.Globalization.CultureInfo cultureInfo = new System.Globalization.CultureInfo("en-US"); // 한국어 : "ko-KR" //LanguageResource.Culture = cultureInfo; rightClickMenu = new ContextMenu(); rightClickMenu.MenuItems.Add(LanguageResource.Row_Before_Insert); rightClickMenu.MenuItems.Add(LanguageResource.Row_After_Insert); rightClickMenu.MenuItems.Add(LanguageResource.Row_Append); rightClickMenu.MenuItems.Add(LanguageResource.Row_Delete);
rightClickMenu.MenuItems[0].Click += new EventHandler(OnMenu_BeforeInsertRow_Click); rightClickMenu.MenuItems[1].Click += new EventHandler(OnMenu_AfterInsertRow_Click); rightClickMenu.MenuItems[2].Click += new EventHandler(OnMenu_AppenRow_Click); rightClickMenu.MenuItems[3].Click += new EventHandler(OnMenu_DeleteRow_Click); }
먼저 행 추가 메뉴를 클릭했을 때 호출되는 AppendRow() 메소드를 아래와 같이 보완한다.
/// <summary> /// Row 추가 /// </summary> public void AppendRow() { DataRow row = dataSource.NewRow(); Row r = new Row(row); rows.Add(r); dataSource.Rows.Add(row); rowHeight = Font.Height + 4; allRowsHeight += rowHeight; OnRowsChanged(); }
Context Menu 중 행 삭제를 선택했을 때 호출되는 이벤트를 살펴보자.
선택한 행의 갯수 만큼 Looping 돌면서 DeleteRow(int delRow) 메소드 호출과 함께 삭제된 행의 Index정보를 재설정하는 RebuildSelectedRowsIndex 메소드를 호출한다.
선택된 행을 모두 삭제하고 나면 selectedRows 리스트를 Clear한다.
/// <summary> /// 선택한 행(Row) 삭제 /// <param name="sender"></param> /// <param name="e"></param> /// </summary> protected void OnMenu_DeleteRow_Click(object sender, EventArgs e) { if (selectedRows.Count > 0) { for (int i = 0; i < selectedRows.Count; i++) { int crntRow = selectedRows[i]; DeleteRow(crntRow); RebuildSelectedRowsIndex(crntRow); } selectedRows.Clear(); } }
행 삭제 메뉴 클릭 시 선택된 행(Row) 번호를 입력받아 동작하게 되는 DeleteRow(int delRow)는 아래와 같다.
/// <summary> /// Row 삭제 /// <param name="delRow"></param> /// </summary> public void DeleteRow(int delRow) { if (delRow < rows.Count && delRow >= 0) { DataRow row = rows[delRow].DataRow; //전체 Row중에 선택 Row를 가져온다. row.Delete(); //해당 Row 정보를 delete한다. rows.RemoveAt(delRow); //전체 Row중에 선택된 Row를 제거한다. allRowsHeight -= rowHeight; //전제 Row의 높이에서 선택된 Row의 높이를 뺀다 OnRowsChanged(); } }
행 삭제 후 삭제해야 할 행 목록에서 행의 Index 정보를 재설정하는 메소드인 RebuildSelectedRowsIndex를 살펴보자.
이 메소드가 처리하는 것은 선택된 행(Row)의 목록에서 현재 선택된 행 이후의 행(Row)의 Index값을 1씩 빼서 재설정하는 것이다.
/// <summary> /// 1개의 Row 삭제 후 선택한 행들의 Index 정보를 갱신한다. /// <param name="index"></param> /// </summary> private void RebuildSelectedRowsIndex(int index) { for (int i = index; i < selectedRows.Count; i++) { int reIndex = selectedRows[i] - 1; selectedRows[i] = reIndex; } }
이전 행에 추가 메뉴 선택 시 수행되는 이벤트인 OnMenu_BeforeInsertRow_Click 이벤트는 사용자가 마우스 좌측 버튼을 클릭해서 Activie된 셀(Cell)에서 행(Row) 정보를 가져와서 해당 행 이전의 위치에 행(Row)을 추가 한다.
이전 행의 추가는 현재 행(Row)에 행(Row)을 추가하면 이전 행 추가가 된다.
/// <summary> /// 선택 Row 앞에 행(Row)을 추가 /// <param name="sender"></param> /// <param name="e"></param> /// </summary> protected void OnMenu_BeforeInsertRow_Click(object sender, EventArgs e) { int crntRow = ActiveCell.Row; BeforeInsert(crntRow); }
/// <summary> /// 이전 행에 Row 추가 /// <param name="crntRow"></param> /// </summary> public void BeforeInsert(int crntRow) { DataRow row = dataSource.NewRow(); //DataTable인 dataSource에 새로운 행의 DataRow 생성 Row r = new Row(row); //새로운 Row를 생성
if (crntRow < 0) crntRow = 0; //crntRow의 값이 0보다 작으면 0으로 설정 rows.Insert(crntRow, r); //전제 Row를 관리하는 List인 rows에 생성된 Row를 추가 dataSource.Rows.InsertAt(row, crntRow); //생성된 DataRow를 DataTable에 원하는 위치에 Insert allRowsHeight += Font.Height + 4; //전체 Row의 높이 값을 저장하는 allRowsHeight에 추가되는 Row의 높이 값을 더한다. rowHeight = Font.Height + 4; //1개 Row의 높이 값을 rowHeight 변수에 저장 OnRowsChanged(); }
다음 행에 추가 메뉴 선택 시 수행되는 이벤트인 OnMenu_AfterInsertRow_Click 이벤트는 사용자가 마우스 좌측 버튼을 클릭해서 Active된 셀(Cell)에서 행(Row) 정보를 가져와서 해당 행 다음의 위치에 행(Row)을 추가한다.
/// <summary> /// 선택 Row 뒤에 행(Row)을 추가 /// <param name="sender"></param> /// <param name="e"></param> /// </summary> protected void OnMenu_AfterInsertRow_Click(object sender, EventArgs e) { int crntRow = ActiveCell.Row; AfterInsert(crntRow); }
/// <summary> /// 다음 행에 Row 추가 /// <param name="crntRow"></param> /// </summary> public void AfterInsert(int crntRow) { if (crntRow + 1 == rows.Count) //현재 행(Row)가 마지막 행(Row)일 경우 { AppendRow(); } else { DataRow row = dataSource.NewRow(); //DataTable인 dataSource에 새로운 행의 DataRow 생성 Row r = new Row(row); //새로운 Row를 생성 rows.Insert(crntRow + 1, r); //핸재 행의 다음행에 추가 dataSource.Rows.InsertAt(row, crntRow + 1); //생성된 DataRow를 DataTable의 현재행 다음에 Insert allRowsHeight += Font.Height + 4; //전체 Row의 높이 값을 저장하는 allRowsHeight에 추가되는 Row의 높이 값을 더한다. rowHeight = Font.Height + 4; //1개 Row의 높이 값을 rowHeight 변수에 저장 OnRowsChanged(); } }
이렇게 해서 이전에 생성했던 Context Menu의 모든 기능을 구현했다. 구현한 메뉴를 테스트 하다보면 오류나 보완해야 할 부분들이 발생하게 된다.
이러한 작업들을 추가한 부분들도 같이 살펴 보기로 하자.
선택한 셀(Cell)에서 값이 변경되었을 때 호출되는 이벤트 부분에 전체 행(Row) 목록 변수인 rows의 Count가 0일 경우 값을 보여주기 위한 로직을 타지 않도록 처리해야 한다.
선택한 셀(Cell)의 영역을 반환하는 메소드인 GetSelectedCellRect 메소드에서 전체 행(Row) 목록을 관리하는 rows의 Count가 0일 경우에 EndEdit메소드를 호출하고 하위 로직을 타지 않도록 보완했다.
/// <summary> /// 선택한 Cell의 영역을 반환 /// <param name="row"></param> /// <param name="col"></param> /// <returns></returns> /// </summary> protected Rectangle GetSelectedCellRect(int row, int col) { if (row < firstVisibleRow || row > lastVisibleRow) return new Rectangle(0, 0, 0, 0); if (col < firstVisibleCol || col > lastVisibleCol) return new Rectangle(0, 0, 0, 0);
//선택된 Cell의 높이를 구한다. int top = topHeaderHeight; int height = 0; for (int i = firstVisibleRow; i <= lastVisibleRow; i++) { if (rows.Count == 0) { EndEdit(); continue; } height = rows[i].MaxLines * rowHeight; if (row == i) break; top += height; }
:
:
}
기존 코드에서 여러 번 수행되어야 하는 영역을 새로운 메소드로 분리해서 호출해서 사용하도록 변경해보자.
마우스 좌측 버튼 클릭 시 행(Row) 선택 영역의 색을 변경하는 부분의 소스를 SelectedRowsChangeColor라는 변수로 분리해 내었다.
private void SelectedRowsChangeColor(int row) { if (Control.ModifierKeys != Keys.Control && Control.ModifierKeys != Keys.Shift) { if (selectedRows.Contains(row)) { if (selectedRows.Count > 1) //선택된 행(Row)이 2개 이상일 경우 { selectedRows.Clear(); //여러 행(Row)가 선택된 경우 기존의 선택된 행(Row) 무효화 selectedRows.Add(row); //선택 행(Row) 추가 } else selectedRows.Remove(row); //동일한 행(Row)를 2번 선택하면 선택 표시 지움 } else //선택된 행(Row)가 없을 경우 기존 선택 행(Row)를 모두 지우고 선택한 행(Row)를 추가 { selectedRows.Clear(); selectedRows.Add(row); } } else { if (Control.ModifierKeys == Keys.Shift && selectedRows.Count > 0) { int index = selectedRows[0]; int begin = Math.Min(row, index); int end = Math.Max(row, index); selectedRows.Clear(); for (int i = begin; i <= end; i++) { selectedRows.Add(i); } } else if (Control.ModifierKeys == Keys.Control) { if (selectedRows.Contains(row)) selectedRows.Remove(row); //선택된 행(Row)을 다시 선택할 경우 제거해서 행(Row) 선택 무효화 else selectedRows.Add(row); //선택된 행(Row)를 추가 } } //Row를 선택하면 EditBox를 감춘다. editBox.Visible = false; }
이렇게 분리된 메소드 SelectedRowsChangeColor를 호출하는 메소드인 MouseLeftButtonClickInContents 이다.
이 부분에 있는 선택된 영역의 색상 변경을 하기 위해 처리하는 영역 중 중복된 부분을 SelectedRowsChangeColor라는 메소드로 뽑아 낸 것이다.
이렇게 함으로써 이 메소드의 소스 길이도 상당히 짧아지는 효과를 볼 수 있게 되었다.
/// <summary> /// WANIGrid Control의 맨 왼쪽 컬럼에서 마우스 좌측 버튼을 눌렀을 경우 /// <param name="sender"></param> /// <param name="e"></param> /// </summary> private void MouseLeftButtonClickInContents(object sender, MouseEventArgs e) { selectedCols.Clear(); int row = GetRowFromY(e.Y); int col = GetColFromX(e.X); if (row < 0) return; //row값이 -1이면 처리하지 않음 if (e.X < leftHeaderWidth) //맨 좌측의 첫 컬럼을 선택했을 시 Row를 선택하도록 처리 { SelectedRowsChangeColor(row);
}
else //WANIGrid Control 내부의 Content 영역을 마우스 좌측 버튼으로 클릭했을 때
{
selectedRows.Clear(); selectedCols.Clear();
if (row == ActiveCell.Row && col == ActiveCell.Col) { Rectangle r = GetSelectedCellRect(ActiveCell.Row, ActiveCell.Col); int k = 0; for (int i = r.Top; i < r.Bottom && k < rows[row].MaxLines - 1; i *= rowHeight, k++) { if (e.Y >= i && e.Y <= i + rowHeight) break; } if (ActiveCell_ActiveRow != k) EndEdit(); ActiveCell_ActiveRow = k; BeginEdit(); } else { ActiveCell.Row = row; ActiveCell.Col = col; EndEdit(); SelectedRowsChangeColor(ActiveCell.Row); }
}
}
다음 강좌에서는 점점 크기가 커지고 있는 WANIGrid.cs 파일을 보다 효율적으로 관리하기 위한 방법에 대해서 공유하고자 한다.
WANIGrid Control은 지금까지의 작업으로 Grid Control의 가장 기본적인 기능들을 구현해 보았다.
Grid Header의 컬럼(Column)과 컬럼(Column) 사이의 경계 부분을 마우스 좌측 버튼 클릭 후 좌/우로 이동하면서 컬럼의 폭을 조정할 수 있는 기능을 구현해 보기로 하자.
이 기능을 구현하기 위해서 필요한 기능들을 크게 나열해 보면
마우스 이동 시에 WANIGrid의 Header 영역의 X좌표를 체크해서 컬럼과 컬럼 경계선 범위에 마우스가 위치할 경우 마우스 커서의 모양을 Cursors.VSplit 으로 변경
마우스 커스가 Cursors.VSplit으로 변경 상태에서 마우스 좌측 버튼이 눌러졌을 때 컬럼(Column) 폭 변경이 시작됨을 설정
마우스 커서의 모양이 Cursors.VSplit 으로 변경 후 마우스 좌측 버튼을 누른 상태에서 마우스 이동 후 좌측 버튼을 놓았을 때 마우스 X 좌표를 계산해서 컬럼(Column)의 Width 값을 재계산
마우스 커서를 Cursors.Default 다시 설정
마우스를 WANIGrid Header 영역의 컬럼과 컬럼 경계선에 가져가면 아래의 그림과 같이 마우스 커서가 변경된다.
이렇게 변경된 상태에서 마우스 좌측 버튼을 눌러서 컬럼의 폭을 원하는 사이즈 만큼 늘리거나 줄이면 된다.
이러한 작업을 하기위해서 기존의 소스에 추가 및 변경/보완 해야 할 부분들을 살펴보자.
먼저 추가해야 할 변수들을 살펴보기로 하자.
private bool vSpliteLineMouseDown = false; //컬럼 경계선 상에서 마우스 좌측버튼이 눌러졌는지를 저장하기 위한 변수 private int resizeCol = 0; //사이즈 변경이 발생한 컬럼을 저장하기 위한 변수 private Point lastMousePoint = new Point(0, 0); //마우스 좌측 버튼을 누른 상태에서 마지막 이동 Point를 저장하기 위한 변수
마우스 이동 시에 WANIGrid Header 영역의 컬럼과 컬럼 경계선에 마우스가 위치하는지 체크하는 로직을 추가한다.
마우스 좌측 버튼을 누른 상태에서는 선택한 컬럼과 컬럼 경계선 상에 수직선을 그려서 현재 선택된 컬럼의 끝 부분을 가리키게끔 하는 기능과 마우스 좌측 버튼이 눌러지지 않았을 때 이동하는 마우스의 X좌표를 체크해서 마우스 커서를 변경하는 부분으로 구분된다.
MouseMove 이벤트를 생성해서 아래의 코드를 추가한다.
private void WANIGrid_MouseMove(object sender, MouseEventArgs e) { //vSpliteLineMouseDown이 true 이면 마우스 버튼이 눌러진 상태에서 이동 중인 상태임. if (vSpliteLineMouseDown) { if (Math.Abs(e.X - lastMousePoint.X) > 4) DrawVSpliteLine(new Point(e.X, e.Y)); return; } //WANIGrid Header영역의 마우스 위치를 체크
//컬럼과 컬럼 사이의 경계선에 위치하면 Cursors.VSplit로 변경하고 resizeCol을 확인한다. Cursor = Cursors.Default; if (grid.GridHeaderList.Count > 0 && e.Y < topHeaderHeight) { int colLine = leftHeaderWidth; //각 컬럼이 끝나는 지점의 X좌표를 저장하는 변수 for (int i = firstVisibleCol; i <= lastVisibleCol; i++) { if (!grid.GridHeaderList[i].Visible) continue; colLine += grid.GridHeaderList[i].Width; //Header의 컬럼과 컬럼 간의 경계선 상에 마우스 포인트가 위치했을 경우 if (e.X > colLine - 2 && e.X < colLine + 2) { Cursor = Cursors.VSplit; resizeCol = i; break; } } } }
마우스 좌측 버튼을 눌렀을 경우 선택된 경계 영역에 수직선을 그리는 메소드인 DrawVSpliteLine를 작성한다.
컬럼 사이즈 변경을 위해 Header영역의 컬럼과 컬럼 경계선을 선택하고 마우스 좌측 버튼을 누른 상태에서 마우스를 좌/우로 이동할 때 표시되는 세로선을 그리는 메소드이다.
최종적으로 컬럼 폭을 조정하고 난 후에 마우스 버튼을 놓았을 때 발생하는 MouseUp 이벤트에 컬럼의 변경 폭 값을 계산해서 Header 정보에 저장하고 화면을 다시 그리는 작업 및 마우스 포인트 값을 설정하는 부분을 추가한다.
private void WANIGrid_MouseUp(object sender, MouseEventArgs e) { //컬럼 사이즈 변경이 완료 되었을 경우 현재 컬럼의 Width 값을 다시 계산해서 저장한다. if (vSpliteLineMouseDown) { int width = e.X - mousePoint.X; grid.GridHeaderList[resizeCol].Width += width; Invalidate();
mousePoint.X = 0; mousePoint.Y = 0; } //vSpliteLineMouseDown 값을 false, Cursor를 Cursors.Default로 설정 vSpliteLineMouseDown = false; Cursor = Cursors.Default; }
기존의 WANIGrid_MouseDown 이벤트에 마우스 Y좌표가 topHeaderHeight보다 작을 경우에 Cursor값을 체크해서 Cursor.VSplit인 경우에 vSpliteLineMouseDown 값을 true 그렇지 않을 경우 false를 설정하는 로직을 추가한다.
private void WANIGrid_MouseDown(object sender, MouseEventArgs e) { //마우스 우측 버튼 클릭 시 Context 메뉴 제공 if (e.Button == MouseButtons.Right) { //WANIGrid Header 영역이 선택되어졌을 경우에는 메뉴 제공하지 않음. if (e.Y < grid.TopHeaderHeight) return; //마우스 우측 버튼이 클릭된 좌표 값을 얻어온다. Point p = new Point(e.X, e.Y);
rightClickMenu.Show(this, p); } else if (e.Button == MouseButtons.Left) { //WANIGrid Top Header 영역을 마우스 좌측 버튼으로 클릭했을 때 if (e.Y < topHeaderHeight) { //Cursor가 Cursors.VSplite로 변경되었을 경우 vSpliteLineMouseDown을 true로 변경 if (Cursor == Cursors.VSplit) { vSpliteLineMouseDown = true; } else { vSpliteLineMouseDown = false; MouseLeftButtonClickInTopHeadHeight(sender, e); Invalidate(); } //Header 영역에서 왼쪽 마우스 버튼이 눌려졌을 때 마우스 포인트 저장. mousePoint.X = e.X; mousePoint.Y = e.Y; } else //WANIGrid의 Top Header 영역을 제외한 영역에서 마우스 좌측 버튼을 클릭했을 때 { MouseLeftButtonClickInContents(sender, e); Invalidate(); } } }
그리고 MouseMouseLeftButtonClickInTopHeadHeight, MouseLeftButtonClickInContents 메소드 내부에 있었던 Invalidate()를 위의 소스에서 처럼 바깥으로 가져왔다.
mousePoint를 설정하는 부분도 MouseMouseLeftButtonClickInTopHeadHeight 메소드 내부에서 바깥으로 가져왔다.
WANIGrid Control의 사이즈가 변경 되었을 경우, 선택 셀(Cell) Rectangle 부분도 Clear할 수 있도록 처리했다.
WANIGrid의 내용을 그리는 메소드 DrawContent를 아래와 같이 수정 및 추가하도록 한다.
private void DrawContent(Graphics g, Rectangle rc, int controlWidth) { SolidBrush brush = new SolidBrush(SystemColors.ControlLight); Pen pen = new Pen(Color.LightGray);
if (rows.Count <= 0) return; g.Clip = new Region(new Rectangle(1, topHeaderHeight, Width - ysclWidth + 2,
Height - xsclHeight - topHeaderHeight)); try { int columnStartY = topHeaderHeight; for (int i = firstVisibleRow; i <= lastVisibleRow && i < rows.Count; i++) { int columnStartX = 0; int columnWidth = 0;
for (int j = firstVisibleCol; j <= lastVisibleCol && j < grid.GridHeaderList.Count; j++) { Col col = new Col(this.grid.HeaderGen.GetHeaders(), rows[i].DataRow); string content = col.GetColText(j); if (j == firstVisibleCol) { g.FillRectangle(brush, 1, columnStartY + 1, leftHeaderWidth, rowHeight); g.DrawRectangle(pen, 1, columnStartY + 1, leftHeaderWidth, rowHeight); columnStartX += leftHeaderWidth; //첫 시작컬럼의 폭을 leftHeaderWidth 만큼 설정 }
if (!grid.GridHeaderList[j].Visible) continue; //Header의 Column Visible 체크
지금까지는 WANIGrid의 겉 모습을 구현해 왔다. 이제는 실제 셀(Cell)의 데이터 입력과 Display 부분을 구현해 보기로 하자. 셀(Cell)의 데이터 입력은 특정 셀(Cell)을 선택하고 한 번 더 클릭을 하면 TextBox가 나타나서 사용자가 입력을 할 수 있도록 되어져 있다.
사용자가 TextBox에 데이터를 입력하면 해당 행(Row)의 선택 컬럼에 입력한 데이터를 저장하고, 저장된 데이터를 화면에 다시 보여주어서 사용자가 확인할 수 있도록 하는 것이다.
이 기능을 구현하기 위해 필요한 각 기능들을 소스를 보면서 확인하도록 하자.
하나의 행(Row)에서 원하는 컬럼의 값들을 가져오기 위한 Col Class를 살펴보자.
/// 1개의 Row에 대한 Column들의 Text값을 가져오기 위한 클래스 public class Col { #region 변수 private List<Header> colHeaders; //Column의 Header정보를 저장 private DataRow row; //Row의 Column정보를 저장하기 위한 DataRow private HorizontalAlignment alignment; //Column의 Text 정렬 방법 #endregion 변수
#region Property /// Header에 정의된 Column 리스트 설정하거나 반환한다. public List<Header> ColHeaders
{
get { return this.colHeaders; }
set { this.colHeaders = value; }
}
/// Column의 정렬방법 public HorizontalAlignment Alignment { get { return this.alignment; } }
/// Row의 Column정보를 저장하기 위한 DataRow public DataRow Row { get { return this.row; } set { this.row = value; } } #endregion Property
/// Column ID 값으로 DataRow내의 컬럼 값을 반환 public string GetColText(string columnId) { string text = String.Empty; if (row[columnId] != null) text = row[columnId].ToString();
return text; }
/// Column Index에 해당하는 Column 값을 반환 public string GetColText(int index) { string text = String.Empty; int colNo = 0; foreach (Header head in colHeaders) { if (head.Visible) //Column Header에서 해당 Column을 Visible true일 경우에만 값을 반환 - 화면에 Display하기 위함 { if (head.Index == index) { text = row[head.ColumnId].ToString(); alignment = head.TextAlign; } colNo++; } }
return text; } #endregion 생성자 } }
처음 Header를 생성할 때 특정 Column을 화면에서 숨기고자 설정할 수도 있다. 이전의 소스 코드에서는 모든 컬럼(Column)의 Visible을 true로 설정하고 개발을 했었다. 하지만 경우에 따라서는 특정 컬럼(Column)이 숨겨져야 할 경우의 처리를 추가하도록 해야 한다.
WANIGrid의 Header영역을 그리는 DrawHeader함수에 컬럼(Column)의 Visible이 true인 경우에만 화면에 나타나도록 처리하는 부분을 추가하도록 한다.
DefaultHeaderGenerator.cs 파일을 열어서 DrawHeaders 메소드에 아래의 부분을 추가한다.
public override void DrawHeaders(int firstVisibleCol, int lastVisibleCol, int controlWidth, Graphics graphics, Rectangle rect) { SolidBrush brush = new SolidBrush(SystemColors.ControlLight); Pen pen = new Pen(Color.LightGray);
for (int i = firstVisibleCol; i <= lastVisibleCol; i++) { if (i == firstVisibleCol) columnStartX += leftHeaderWidth; //첫 시작컬럼의 폭을 leftHeaderWidth 만큼 설정 int headerWidth = this._headers[i].Width; //i 번째 컬럼의 폭을 설정
if (!this._headers[i].Visible) continue;//기존 코드에 추가하는 부분
//보여지는 컬럼의 폭이 컨트롤의 폭 보다 클경우 if (columnStartX + headerWidth > controlWidth) { headerWidth = controlWidth - columnStartX - 3; if (lastVisibleCol == _headers.Count - 1) IsLargeLastCol = true; } else { IsLargeLastCol = false; }
:
:
}
}
향 후 각 컬럼(Column)에 대한 편집 여부도 설정할 수 있어야 하기에 Header.cs 파일에 boolean 변수와 Property를 추가하도록 한다.
public abstract class Header { #region 변수 private int index; //Index private string columnId; //컬럼 ID private string title; //컬럼 타이틀 private int left; //컬럼 시작위치 private int width; //컬럼 폭 private HorizontalAlignment headAlign; //컬럼 타이틀 정렬위치 private HorizontalAlignment textAlign; //컬럼 내용 정렬위치 private bool visible = true; //컬럼 Visible 여부 private bool editable = true; //컬럼 편집여부 #endregion 변수
:
public bool Editable { get { return editable; } set { editable = value; } }
:
}
WANIGrid.cs 파일에서 추가 또는 변경되는 부분들을 하나씩 살펴보기로 하자.
마우스로 선택한 특정 셀(Cell)의 컬럼(Column) 중 액티브 컬럼을 설정하기 위한 변수를 선언한다. 사용자가 선택한 Active한 Column 값을 저장하기 위한 변수이다.
private int ActiveCell_ActvieCol = -1;
저장된 값을 사용자에게 Display할 때 보여지는 폰드 색상을 검은색으로 설정해서 저장한다. 화면에서 WANIGrid를 그리면서 데이터를 보여줄때 사용할 색상의 Brush이다.
private SolidBrush blackBrush = new SolidBrush(Color.Black);
WANIGrid 생성자에 TextBox인 editBox에 설정을 추가한다.
public WANIGrid() { InitializeComponent(); if (grid == null) grid = new WANI_Grid.Grid.Grid(); rows = new RowCollection(); hScrollBar.SmallChange = 1; rowHeight = Font.Height + 4; vScrollBar.SmallChange = rowHeight; editBox = new TextBox(); editBox.BorderStyle = BorderStyle.None; editBox.BackColor = Color.White; //배경색을 흰색으로 설정 editBox.Font = Font; editBox.Visible = false; Controls.Add(editBox); //마우스 우측 버튼 클릭 시 제공되는 ContextMenu 초기화 InitializeContextMenu(); }
그리고 editBox에 값이 변경되었을 때의 이벤트를 처리할 수 있도록 코드를 추가한다. editBox 값이 변경되면 변경된 값을 행(Row)의 DataRow 변수의 해당 컬럼(Column)에 값을 설정하도록 처리한다.
public void EventHandlerInitialize() { //가로 스크롤바 hScrollBar.Scroll += new ScrollEventHandler(HScrollBar_Scroll); //세로 스크롤바 vScrollBar.Scroll += new ScrollEventHandler(VScrollBar_Scroll); //마우스 휠 this.MouseWheel += new MouseEventHandler(Mouse_Wheel); this.editBox.TextChanged += EditBox_TextChanged; //editBox의 TextChanged 이벤트 추가 }
WANIGrid Header 값을 설정할 때 특정 컬럼(Column)을 숨기고자 할 경우 Visible 값이 false인 경우의 컬럼(Column)은 화면에서 보여지지 않아야 한다. 특히 사용자가 WANIGrid 선택 시 마우스 X좌표 값으로 컬럼(Column) 값으로 변환하는 메소드인 GetColFromX에 Header 값을 체크해서 Visible값이 false인 경우에는 처리하지 않는 로직을 추가한다.
private int GetColFromX(int X) { int col = 0; int tempWidth = leftHeaderWidth;
if (X >= 0 && X <= leftHeaderWidth) { col = -1; } else { for (col = firstVisibleCol; col < lastVisibleCol; col++) { if (!grid.GridHeaderList[col].Visible) continue;//Header의 Visible 값이 false인 경우 int width = grid.GridHeaderList[col].Width; if (X < width + tempWidth) break; tempWidth += width; } } if (col > grid.GridHeaderList.Count - 1) col = -1; return col; }
마우스 Click시 선택 셀(Cell)을 그리기 위한 Rectangle 값을 반환하는 GetSelectedCellRect 메소드에 Header의 Visible값이 false인 경우를 처리하는 로직을 추가한다.
protected Rectangle GetSelectedCellRect(int row, int col) { if (row < firstVisibleRow || row > lastVisibleRow) return new Rectangle(0, 0, 0, 0); if (col < firstVisibleCol || col > lastVisibleCol) return new Rectangle(0, 0, 0, 0);
:
int left = leftHeaderWidth + 2; int width = 0; for (int i = firstVisibleCol; i <= lastVisibleCol; i++) { if (!grid.GridHeaderList[i].Visible) continue; :
}
return new Rectangle(left - 1, top + 1, width - 1, height - 1);
}
WANIGrid의 최상단 Header영역을 클릭 했을 때 선택된 Column색상을 변환하는 부분에도 Header의 Visible값이 false인 경우 처리하지 않도록 하는 로직을 추가해야 한다.
Grid Header 영역 클릭 시 컬럼 선택을 표현하기 위해 배경 색상을 바꾸어 보았다. 마찬가지로 좌측 첫 열의 빈 공간을 클릭하면 행(Row)이 선택되도록 할 것이다.
기존의 컬럼 선택과 큰 차이가 없지만 행(Row) 선택을 표현하기 위한 코드를 같이 살펴보기로 하자.
먼저 WANIGrid.cs 파일에 선택된 행(Row) 정보를 관리하기 위한 변수를 추가한다.
private List<int> selectedRows = new List<int>(); //선택된 행(Rows)들을 관리하기 위한 변수
마우스 다운 이벤트에 마우스 좌측 버튼이 눌러졌을 경우에 Top Header 영역이 아닌 상단 이미지와 같은 맨 좌측 영역에서 클릭 이벤트가 발생했을 경우, 선택된 행(Row)의 색상은 변경하도록 한다.
private void WANIGrid_MouseDown(object sender, MouseEventArgs e) { //마우스 우측 버튼 클릭 시 Context 메뉴 제공 if (e.Button == MouseButtons.Right) { //Grid Header 영역이 선택되어졌을 경우에는 메뉴 제공하지 않음. if (e.Y < grid.TopHeaderHeight) return; //마우스 우측 버튼이 클릭된 좌표 값을 얻어온다. Point p = new Point(e.X, e.Y);
rightClickMenu.Show(this, p); } else if (e.Button == MouseButtons.Left) { //WANIGrid Top Header 영역을 마우스 좌측 버튼으로 클릭했을 때 if (e.Y < topHeaderHeight) { MouseLeftButtonClickInTopHeadHeight(sender, e); } else //WANIGrid의 Top Header 영역을 제외한 영역에서 마우스 좌측 버튼을 클릭했을 때 { MouseLeftButtonClickInContents(sender, e); --> 추가한 영역 } } }
마우스 좌측 버튼을 WANIGrid Control의 Header영역에서 클릭했을 때 호출되는 메소드인 MouseLeftButtonClickInToHeaderHeight에 시프트 키가 눌러졌을 때의 동작을 추가한 소스는 아래와 같다.
//컬럼이 존재할 경우 if (grid.GridHeaderList.Count > 0) { int col = GetColFromX(e.X); if (col < 0) return; //col값이 -1이면 처리하지 않음
//Control Key를 누르지 않은 상태에서 컬럼을 선택했을 경우 if (Control.ModifierKeys != Keys.Control && Control.ModifierKeys != Keys.Shift) { //선택된 컬럼일 경우 if (selectedCols.Contains(col)) { if (selectedCols.Count > 1) //선택된 컬럼이 두 개 이상일 경우 { selectedCols.Clear(); //여러 컬럼이 선택된 경우 기존의 선택된 컬럼 무효화 selectedCols.Add(col); //선택 컬럼 추가 } else selectedCols.Remove(col); //동일한 컬럼을 2번 선택하면 선택 표시 지움 } else //선택된 컬럼이 없을 경우 기존 선택 컬럼을 모두 지우고 선택한 컬럼을 추가 { selectedCols.Clear(); selectedCols.Add(col); } } else { if (Control.ModifierKeys == Keys.Shift && selectedCols.Count > 0) { int index = selectedCols[0]; int begin = Math.Min(col, index); int end = Math.Max(col, index); selectedCols.Clear(); for (int i = begin; i <= end; i++) { selectedCols.Add(i); } } else if (Control.ModifierKeys == Keys.Control) { if (selectedCols.Contains(col)) selectedCols.Remove(col); //선택된 컬럼을 다시 선택할 경우 제거해서 컬럼 선택 무효화 else selectedCols.Add(col); //선택된 컬럼을 추가 } } Invalidate(); } mousePoint.X = e.X; mousePoint.Y = e.Y; }
마우스 좌측 버튼을 WANIGrid Control의 맨 왼쪽 부분에서 클릭했을 때 호출되는 메소드는 아래와 같다.
private void MouseLeftButtonClickInContents(object sender, MouseEventArgs e) { selectedCols.Clear(); int row = GetRowFromY(e.Y); if (row < 0) return; //row값이 -1이면 처리하지 않음 if (e.X < leftHeaderWidth) //맨 좌측의 첫 컬럼을 선택했을 시 Row를 선택하도록 처리 { if (Control.ModifierKeys != Keys.Control && Control.ModifierKeys != Keys.Shift) { if (selectedRows.Contains(row)) { if (selectedRows.Count > 1) //선택된 행(Row)이 2개 이상일 경우 { selectedRows.Clear(); //여러 행(Row)가 선택된 경우 기존의 선택된 행(Row) 무효화 selectedRows.Add(row); //선택 행(Row) 추가 } else selectedRows.Remove(row); //동일한 행(Row)를 2번 선택하면 선택 표시 지움 } else //선택된 행(Row)가 없을 경우 기존 선택 행(Row)를 모두 지우고 선택한 행(Row)를 추가 { selectedRows.Clear(); selectedRows.Add(row); } } else {
//Shift 키를 누르고 선택된 행이 1개 이상일 경우
if (Control.ModifierKeys == Keys.Shift && selectedRows.Count > 0)
{
int index = selectedRows[0]; int begin = Math.Min(row, index); int end = Math.Max(row, index); selectedRows.Clear(); for (int i = begin; i <= end; i++) { selectedRows.Add(i); }
} else if (Control.ModifierKeys == Keys.Control) //컨트롤 키를 누른 상태일 경우
{
//선택된 행(Row)을 다시 선택할 경우 제거해서 행(Row) 선택 무효화
if (selectedRows.Contains(row)) selectedRows.Remove(row); else selectedRows.Add(row); //선택된 행(Row)를 추가
} } } else { selectedRows.Clear(); }
Invalidate(); }
지금까지는 마우스의 좌측 버튼 클릭 시의 위치를 계산해서 선택된 행(Row)을 확인하고 선택 행(Row) 정보를 관리하는 selectedRows 변수에 값을 할당하거나 제거하는 로직을 작성했다.
컨트롤 키와 쉬프트 키가 눌러졌을 때, 행 선택 방법도 정의를 했다. 컨트롤 키를 누른 상태에서 행이나 컬럼을 선택했을 경우에는 여러 행과 컬럼이 선택되도록 했고, 시프트 키를 눌렀을 때에는 시작 행/컬럼 부터 다음 선택 행/컬럼 사이의 모든 행/컬럼이 선택되도록 했다.
실제 선택된 행(Row)의 바탕색을 변경하기 위한 데이터 작업은 완료했으나, 아직은 WANIGrid Control에서는 색상이 변경되는 모습은 볼 수 없다.
실제 선택된 행(Row)의 값이 변경된 후에 Invalidate() 메소드 호출이 되는 순간 WANIGrid Control의 Display 영역을 새롭게 그리기된다.
Control 영역을 다시 그릴 때 선택된 행(Row)의 배경 색이 나타날 수 있도록 해야 한다.
이렇게 하기 위해서는 WANIGrid Control의 배경을 담당하고 있는 DrawBackground 메소드에 선택된 행(Row)의 백그라운드를 그리는 로직을 추가한다.
WANIGrid Control의 겉 모습을 어느 정도 구현을 했다. 행(Row) 추가 기능을 통해서 새로운 행들을 생성하고 가로/세로 스크롤바로 WANIGrid Control 내의 모습을 볼 수 있었다. 또한 마우스 휠을 이용해서 가로/세로 스크롤바를 움직인 것과 동일한 효과도 주었다.
여기에 추가로 Grid의 Top Header 영역을 클릭하면 해당 컬럼의 전체 로우를 선택했음을 표현할 수 있도록 색상을 변경하는 기능을 추가해 보기로 하자.
이번 시간에는 Grid의 Top Header 영역에서 마우스 좌측 버튼을 누르면 해당 위치의 컬럼을 선택한 것으로 표시하는 기능 개발하고자 한다.
WANIGrid.cs 파일을 열어서 선택한 컬럼이 한 개일 수도 있지만 2개 이상의 컬럼을 선택할 수도 있다. Control Key를 누르고 마우스 좌측 버튼을 눌렀을 경우에는 1개 이상의 컬럼을 선택할 수 있도록 처리할 것이다.
마우스 좌측 버튼만 눌렀을 때에는 한 개의 컬럼만 선택될 수 있도록 처리하도록 한다.
WANIGrid Header 영역의 맨 왼쪽 컬럼을 선택했을 경우는 아무런 처리가 되지 않도록 한다. 향 후에 이 영역을 선택했을 경우 모든 영역을 선택하도록 할 수도 있겠지만 지금은 아무런 동작을 하지 않도록 한다.
선택된 컬럼의 Index정보를 관리하기 위한 변수, 선택된 영역의 색상을 지정할 수 있도록 지원하기 위한 변수와 현재 마우스의 좌측 버튼 클릭 시 X, Y 좌표를 저장하기 위한 변수를 선언하도록 한다.
private List<int> selectedCols = new List<int>(); //선택된 컬럼(Columns)들을 관리하기 위한 변수
private Point mousePoint = new Point(0, 0); //Click한 마우스 좌표를 저장하기 위한 변수 private SolidBrush selectedColor = new SolidBrush(Color.LightCyan);
선택된 영역의 색상은 변경하기 위한 Property를 아래와 같이 설정한다. 디폴트 색상이 아닌 다른 색상을 지정하고자 할 경우 아래의 Property를 이용해서 색상을 변경할 수 있다.
public Color SelectedColor { set { selectedColor = new SolidBrush(value); } }
실제 마우스 좌측 버튼이 클릭 되었을 때 마우스 X좌표의 위치로 컬럼의 Index정보를 반환하는 메소드는 아래와 같다.
private int GetColFromX(int X) { int col = 0; int tempWidth = leftHeaderWidth; //맨 첫번째 비어 있는 컬럼의 폭
if (X >= 0 && X <= leftHeaderWidth) //마우스 좌측 버튼 클릭 시 X좌표가 leftHeaderWidth 영역 내에 있을 경우에는 col변수에 -1값을 설정 { col = -1; } else {
//마우스 좌측 버튼 클릭 시 X좌표의 위치가 현재 보여지는 컬럼 내의 위치에 해당하는지 체크 for (col = firstVisibleCol; col < lastVisibleCol; col++) { int width = grid.GridHeaderList[col].Width; if (X < width + tempWidth) break; //X좌표 위치와 컬럼 Index가 매핑 되었을 경우 tempWidth += width; } } if (col > grid.GridHeaderList.Count - 1) col = -1; //매핑된 컬럼 Index값이 실제 컬럼 개수보다 많을 경우 -1을 반환 return col;
}
마우스 좌측 버튼 클릭 시 X좌표에 해당하는 컬럼 Index를 가져오는 메소드까지 만들었다.
이제는 마우스 버튼이 눌러졌을 때 발생하는 이벤트에 마우스 좌측 버튼 클릭 부분을 추가하기로 하자.
MouseDown 이벤트 처리 부분을 살펴보자.
private void WANIGrid_MouseDown(object sender, MouseEventArgs e) { //마우스 우측 버튼 클릭 시 Context 메뉴 제공 if (e.Button == MouseButtons.Right) { //Grid Header 영역이 선택되어졌을 경우에는 메뉴 제공하지 않음. if (e.Y < grid.TopHeaderHeight) return; //마우스 우측 버튼이 클릭된 좌표 값을 얻어온다. Point p = new Point(e.X, e.Y);
rightClickMenu.Show(this, p); } else if (e.Button == MouseButtons.Left) { //WANIGrid Top Header 영역을 마우스 좌측 버튼으로 클릭했을 때 if (e.Y < topHeaderHeight) { MouseLeftButtonClickInTopHeadHeight(sender, e); } } }
실제 WANIGrid Top Header 영역이 클릭 되었을 때 호출되는 MouseLeftButtonClickInTopHeaderHeight 메소드는 아래와 같다.
//컬럼이 존재할 경우 if (grid.GridHeaderList.Count > 0) { int col = GetColFromX(e.X); if (col < 0) return; //col값이 -1이면 처리하지 않음
//Control Key를 누르지 않은 상태에서 컬럼을 선택했을 경우 if (ModifierKeys != Keys.Control) { //선택된 컬럼일 경우 if (selectedCols.Contains(col)) { if (selectedCols.Count > 1) //선택된 컬럼이 두 개 이상일 경우 { selectedCols.Clear(); //여러 컬럼이 선택된 경우 기존의 선택된 컬럼 무효화 selectedCols.Add(col); //선택 컬럼 추가 } else selectedCols.Remove(col); //동일한 컬럼을 2번 선택하면 선택 표시 지움 } else //선택된 컬럼이 없을 경우 기존 선택 컬럼을 모두 지우고 선택한 컬럼을 추가 { selectedCols.Clear(); selectedCols.Add(col); } } else { if (selectedCols.Contains(col)) selectedCols.Remove(col); //선택된 컬럼을 다시 선택할 경우 제거해서 컬럼 선택 무효화 else selectedCols.Add(col); //선택된 컬럼을 추가 } Invalidate(); }
//마우스 좌측 버튼 클릭 시 X/Y좌표를 저장 mousePoint.X = e.X; mousePoint.Y = e.Y; }
지금까지는 마우스 좌측 버튼 클릭 시 마우스 X좌표 위치에 대응하는 컬럼의 Inxex 값을 찾기 위한 로직을 만들었고, 실제 사용자에게 선택된 컬럼을 색상으로 보여주는 부분을 추가해야 실제 WANIGrid Control 실행 시에 사용자와 직접 상호작용하면서 선택된 컬럼 영역을 다이나믹하게 보여주게 된다.
기존의 DrawBackground 메소드에 선택된 컬럼의 배경 색상을 변경하는 기능을 아래와 같이 추가해야 한다.
//선택된 컬럼의 Background를 그린다. for (int i = 0; i < selectedCols.Count; i++) //선택된 컬럼의 갯수 만큼 반복 { int index = selectedCols[i]; int left = leftHeaderWidth;
//WANIGrid Control 영역에서 보여지고 있는 컬럼 내에서 선택된 컬럼의 폭을 구한다. for (int j = firstVisibleCol; j < lastVisibleCol && j < index; j++) { left += grid.GridHeaderList[j].Width; }
if (allRowsHeight > 0) //추가된 행(Row)가 있을 경우에만 처리 { g.FillRectangle(selectedColor, left + 1, topHeaderHeight, grid.GridHeaderList[index].Width + 1,