반응형

WANIGrid Control에 마우스 휠 처리를 추가했다.

마우스 휠을 움직이면 데이터의 행(Row)를 100씩 이동하도록 했고, Control 키를 누른 상태에서 마우스 휠을 움직이면 좌우 방향으로 100씩 움직이도록 보완을 했다.

마우스 휠 부분은 VScrollBar와 HScrollBar를 움직였을 때와 같이 처리 되도록 했다.

WANIGrid Control의 헤더 속성 중에 IsRowHeader가 true인 경우와 아닌 경우의 차이는 고정 컬럼 여부이다.

    /// <summary>
    /// WANIGrid MouseWheel Event
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void PicGrid_MouseWheel(object sender, MouseEventArgs e)
    {
        if (ModifierKeys == Keys.Control)   //Control Key를 눌렀을 떄, HScrollBar를 움직인 것과 동일
        {
            if (e.Delta / 120 > 0)
            {
                if (grid.StartColumnPosition - 100 < 0) grid.StartColumnPosition = 0;
                else grid.StartColumnPosition -= 100;
            }
            else
            {
                if (grid.StartColumnPosition + 100 < hScrollBar.Maximum)
                {
                    grid.StartColumnPosition += 100;                        
                }
                else 
                {
                    grid.StartColumnPosition = hScrollBar.Maximum;
                }
            }
            IsMouseWheel = true;
        }
        else //VScrollBar를 움직인 것과 동일
        {
            if (e.Delta / 120 > 0)
            {
                if (grid.StartRowPosition - 100 < 0) grid.StartRowPosition = 0;
                else grid.StartRowPosition -= 100;
            }
            else
            {
                if (grid.StartRowPosition + 100 < vScrollBar.Maximum) grid.StartRowPosition += 100;
            }
            IsMouseWheel = false;
        }
        picGrid.Invalidate();
    }

[그림 1] IsRowHeader 가 true인 경우 (Column01, Column02 가 true임)

첨부된 WANIGridExample.cs 파일에서 WANIGrid를 사용하는 방법을 참조하기 바람.

WANIGrid.dll
0.05MB
WANIGridControlExample.zip
0.21MB

반응형

'WinForm > WANIGrid 컨트롤 공개' 카테고리의 다른 글

WANIGrid Control 개선  (0) 2021.04.10
WANIGrid Control 사용법 2  (0) 2020.05.14
WANIGrid Control 사용법 1  (1) 2020.04.30
반응형

기존에 만들었던 WANIGrid의 전체적인 모습과 구조를 대대적으로 바꾸었다.

WANIGrid 스크롤 영역에 대한 처리와 전체 적인 모습들을 좀 더 개선하고자 했다.

추가로 헤더 그룹을 구성할 수 있도록 API를 추가했다.

WANIGrid를 사용하기 위해서는 프로그램 단에서 WANIGrid를 초기화 하는 작업이 필요하다.

초기화 메소드를 하나 만들어서 WANIGrid에 필요한 설정을 하도록 한다.

 

HeaderColumn의 ColumnId는 DataTable의 Column과 대응된다.

waniGridControl1.DataSource 값으로 DataTable을 할당하게 되면 ColumnId의 값과  동일한 DataTable의 필드  값이 WANIGrid 에 나타나게 된다.

 

현재는 셀 선택 시 편집 모드는 TextBox 형태만 지원하고 있다. 향 후 CheckBox, ComboBox, DatePicker 등을 지원할 예정이다.

private void InitializeGrid()
{
    //첫번째 헤드그룹 지정
    List<HeaderGroup> headerGroups0 = new List<HeaderGroup>()
    {
        new HeaderGroup() { StartColumnId = "Col02", NumberOfColumns = 4, TitleText = "Test Top 01" }
    };

    //두번째 헤드그룹 지정
    List<HeaderGroup> headerGroups = new List<HeaderGroup>()
    {
        new HeaderGroup() { StartColumnId = "Col02", NumberOfColumns = 2, TitleText = "Test 01" },
        new HeaderGroup() { StartColumnId = "Col04", NumberOfColumns = 2, TitleText = "Test 02" }
    };
    //헤드 지정
    List<HeaderColumn> headerCols = new List<HeaderColumn>()
    {
        new HeaderColumn() { ColumnId = "Col01", HeaderTitle = "Column 01", IsRowHeader = false, IsEdit = true, EditorType = EditControlType.TextBox, HeaderTitleAlign = HorizontalAlignment.Center, TextAlign = HorizontalAlignment.Left, Visible = true, Width = 100  },
        new HeaderColumn() { ColumnId = "Col02", HeaderTitle = "Column 02", IsRowHeader = false, IsEdit = true, EditorType = EditControlType.TextBox, HeaderTitleAlign = HorizontalAlignment.Center, TextAlign = HorizontalAlignment.Center, Visible = true, Width = 100  },
        new HeaderColumn() { ColumnId = "Col03", HeaderTitle = "Column 03", IsRowHeader = false, IsEdit = true, EditorType = EditControlType.TextBox, HeaderTitleAlign = HorizontalAlignment.Center, TextAlign = HorizontalAlignment.Left, Visible = true, Width = 200  },
        new HeaderColumn() { ColumnId = "Col04", HeaderTitle = "Column 04", IsRowHeader = false, IsEdit = true, EditorType = EditControlType.TextBox, HeaderTitleAlign = HorizontalAlignment.Center, TextAlign = HorizontalAlignment.Left, Visible = true, Width = 100  },
        new HeaderColumn() { ColumnId = "Col05", HeaderTitle = "Column 05", IsRowHeader = false, IsEdit = true, EditorType = EditControlType.TextBox, HeaderTitleAlign = HorizontalAlignment.Center, TextAlign = HorizontalAlignment.Left, Visible = true, Width = 200  },
        new HeaderColumn() { ColumnId = "Col06", HeaderTitle = "Column 06", IsRowHeader = false, IsEdit = true, EditorType = EditControlType.TextBox, HeaderTitleAlign = HorizontalAlignment.Center, TextAlign = HorizontalAlignment.Left, Visible = true, Width = 150  },
        new HeaderColumn() { ColumnId = "Col07", HeaderTitle = "Column 07", IsRowHeader = false, IsEdit = true, EditorType = EditControlType.TextBox, HeaderTitleAlign = HorizontalAlignment.Center, TextAlign = HorizontalAlignment.Left, Visible = true, Width = 200  }
    };

    //Header 정보 추가
    TopHeader header = new TopHeader(headerCols);
    //최상위 헤더 그룹 설정
    header.SetGroupHeaders(0, headerGroups0);
    //상위 헤더 그룹 설정
    header.SetGroupHeaders(headerGroups);
    //Header 정보 설정
    waniGridControl1.SetHeader(header);    
}

 

아래의 첨부 파일의 소스를 실행한 결과 화면

[그림 3-1] WANIGrid Control 사용 예제 1 - 셀 편집 시 화면
[그림 3-2] WANIGrid Control 사용 예제 2 - 한 개의 행 선택 시
[그림 3-2] WANIGrid Control 사용 예제 3 - 여러 개의 행 선택 시

 

[그림 3-2] WANIGrid Control 사용 예제 4 - 한 개의 컬럼 선택 시
[그림 3-2] WANIGrid Control 사용 예제 5 - 여러 개의 컬럼 선택 시

아래의 첨부한 소스를 참조

 

WANIGridControlExample.zip
0.21MB
WANIGrid.dll
0.05MB
WANIGrid_속성정리.pdf
0.09MB

반응형
반응형

WANIGridDataTableDataSource에 할당하는 방법에 대해서 알아보자.

보통 우리는 데이터베이스에서 쿼리를 통해서 우리가 원하는 데이터들을 DataTable에 담아서 가져오게 되고, 이렇게 가져온 데이터를 특정한 양식 또는 원하는 화면에 사용자에게 제공하게 된다.

WANIGrid 또한 이렇게 가져온 데이터인 DataTable를 할당함으로써 WANIGrid에 원하는 컬럼의 정보들을 모두 제공할 수 있게 된다.

 

첨부된 WANIGridExample 프로젝트의 WANIGridExample.cs파일을 열어서 GridInitialize() 메소드를 살펴보자. WANIGrid를 사용하기 위해서는 항상 WANIGridHeader Column을 정의하고 WANIGrid에서 보여주고자 하는 DataTable의 정보를 할당하면 된다.

GridInitialize 메소드의 내부를 하나씩 살펴보기로 하자.

 

HeaderColumn 클래스의 List를 선언하고 waniGrid.TopHeaderList.GetHeaderList() 메소드를 호출해서 빈 headerColumns List를 생성한다.

List<HeaderColumn> headerColumns = waniGrid.TopHeaderList.GetHeaderList();

List<HeaderColumn> headerColumns를 새로 생성한다. Index 값이 0이고 ColumnId“LeftHeader”인 빈 컬럼 1개만 생성됨. 이 후에 각 컬럼들을 추가한다.

 

HeaderColumn 클래스의 속성과 메소드를 잠깐 살펴보자.

속성(Property)

get/set

상세

public int Index

get/set

컬럼 인덱스

public string ColumnId

get/set

컬럼 ID. DataTableColumn Name과 동일해야 함

public string Title

get/set

컬럼 헤더의 명칭.

public int Left

get/set

컬럼의 Left 즉 시작하는 X좌표 값

public int Width

get/set

컬럼의 폭

public HorizontalAlignment HeadAlign

get/set

컬럼 헤더 명칭의 정렬 방법

public HorizontalAlignment TextAlign

get/set

컬럼 데이터의 정렬 위치. DataTableRow별 컬럼 ID에 해당하는 데이터 값을 의미

public bool Visible

get/set

화면에서 해당 컬럼을 화면에 Display 여부. true면 보여지고 false인 경우 숨겨짐.

public bool Editable

get/set

컬럼의 데이터 편집 여부. true면 편집 가능 그렇지 않으면 편집 불가

public bool IsCalendar

get/set

컬럼이 날짜 정보를 가지고 있으면 true, 그렇지 않으면 false

public ViewRange

ViewRange

get/set

컬럼의 시작 X좌표와 끝 X좌표 값을 가지고 있는 ViewRange 타입.

 

WANIGrid를 초기화 하는 방법은 Header 영역을 초기화하고, Header영역의 컬럼들을 정의하는 것으로 일차적인 초기화를 하게된다.

Header영역을 Clear하고 HeaderColumn 목록을 초기화한다.

List<HeaderColumn> headerColumns = waniGrid.TopHeaderList.GetHeaderList();

DataTable의 각 컬럼명과 ColumnId를 동일하게 부여하면 DataTableColumn 값이 WANIGrid의 컬럼에 표시되게 된다.

headerColumns.Add(new HeaderColumn() { ColumnId = "Col01", Title = "Column 01", Width = 80, HeadAlign = HorizontalAlignment.Center, TextAlign = HorizontalAlignment.Left, Editable = true, Visible = true });

headerColumns ListHeaderColumn을 추가하면서 컬럼들을 하나씩 설정해 나가면 된다.

  • ColumnId DataTableColumn명과 동일하게 부여하면 DataTableColumn 값을 할당하게 된다.
  • Title WANIGrid의 컬럼 헤더에 표시되는 텍스트
  • Width 컬럼의 폭을 지정한다.
  • HeadAlign 헤더의 표시되는 텍스트 정렬 방법
  • TextAlign Column 값의 정렬 방법
  • Editable 편집 여부
  • Visible 화면에 표시 여부

컬럼의 속성을 설정해서 headerColumns 리스트를 waniGrid 컨크롤에 할당한다.

waniGrid.TopHeaderList.SetHeaderList(headerColumns);

WANIGrid 컨트롤에 할당한 컬럼 정보들을 InitializeHeader 메소드를 호출해서 초기화한다.

waniGrid.InitializeHeader();

WANIGrid 컨트롤에 고정 컬럼의 개수를 지정할 경우 FixedCol 속성에 고정 컬럼의 개수를 지정한다. 기본 값은 0이다.

waniGrid.FixedCol = 0;

 

아래의 첨부 파일을 실행하면 하기와 같은 화면을 볼 수 있다.

[그림 2-1] WANIGridExample 실행화면

고정컬럼 개수를 변화시켜 보라. 고정컬럼의 영역이 무엇을 의미하는지 확인할 수 있을 것이다.

고정컬럼 수정을 체크했을 때 고정컬럼 영역을 클릭해보라. 값을 입력할 수 있는 텍스트 입력 컨트롤이 활성화 될 것이다.

행간격을 높이거나 줄여보라. 행 높이의 기본 값은 20이다.

DataTable에서 Data 가져오기 버튼을 클릭해 보라. 내부적으로 DataTable을 생성해서 생성된 DataTableWANIGridDataSource에 할당하면 아래의 같이 각 필드의 값이 DataTable의 값으로 채워지게 된다.

자세한 내용은 아래의 첨부된 소스를 참조해 보기 바란다.

 

WANIGridExample_20200514.zip
0.21MB
WANIGrid.dll
0.05MB

 

 

반응형

'WinForm > WANIGrid 컨트롤 공개' 카테고리의 다른 글

WANIGrid Control 개선 - Mouse Wheel 처리 추가  (0) 2021.04.13
WANIGrid Control 개선  (0) 2021.04.10
WANIGrid Control 사용법 1  (1) 2020.04.30
반응형

WANIGrid 는 데이터를 Grid 형태로 제공하고 행 추가 및 수정, 삭제 등의 기본적인 기능들을 제공하는 WinFormControl이다.

 

Grid Control 만들기를 연재하면서 스스로 공부하면서 정리한 내용으로 Grid Control 만들기의 소스를 좀 더 다듬고 개선해서 WinForm 개발 시에 무료로 제한없이 사용할 수 있도록 WANIGrid Control 만들어 갈 생각이다.

WANIGrid Control 사용에 필요한 속성 정보와 API 들을 정리하고 보다 쉽게 사용할 수 있는 매뉴얼을 제공해서 Windows 개발자들이 보다 손 쉽게 사용할 수 있도록 할 것이다. 

 

제공하는 WANIGrid Control과 예제 소스를 통해서 직접 사용해 보면서 추가 개발 또는 개선이 필요한 사항이 있으면 언제든 댓글로 알려주세요.

 

WANIGrid Control을 사용하기 위한 기본적인 속성과 구성을 살펴보자.

[그림 1] WANIGrid Control 구성

WANIGrid Control Client Size를 기준으로 보여지는 첫 번째 컬럼과 마지막 컬럼의 번호를 기준으로 가로 영역을 표현하고, 행 또한 첫 번째 행의 번호와 마지막 행의 번호를 기준으로 세로 영역을 표현하게 된다.

가로/세로 스크롤바를 움직임에 따라 컬럼과 행의 시작/마지막 번호가 변경되며 이 값을 기준으로 표현되어야 할 데이터 영역을 표현하게 된다.

  • LeftHeader 영역은 첫 시작 컬럼이며 WANIGrid의 기본 컬럼이다. 폭의 기본 사이즈는 22이다.
  • XscHeight/YscWidth 영역은 가로/세로 스크롤바의 높이와 폭의 사이즈를 말한다.
  • RowHeight 는 각 행의 높이를 의미하며 기본 높이는 20의 값을 가지고 사용자가 변경할 수 있다.
  • TopHeader 는 헤더 영역의 각 컬럼 속성 정보들을 담고 있다.
  • TopHeaderHeight 는 헤더 영역의 높이를 말하며 기본 높이는 20의 값을 가지고 사용자가 변경할 수 있다.
  • FirstVisibleCol/LastVisibleCol 은 화면 상에서 보여지는 첫 번째 컬럼의 Index와 마지막 컬럼의 Index 값을 가지고 있다.
  • FirstVisibleRow/LastVisinleRow 는 화면 상에서 보여지는 첫 번째 행의 Index와 마지막 컬럼의 Index 값을 가지고 있다.

WANIGrid Control에서 제공되는 기본 속성(Property)들을 아래와 같이 정리해 보았다.

Scope

Type

명칭

내용

public

Cell

ActiveCell

선택한 셀의 행/열 정보를 저장

public

int

RowHeight

행 높이를 설정 또는 반환

public

int

TopHeaderHeight

WANIGrid의 Top Header 영역의 높이를 설정 또는 반환

public

TopHeader

TopHeaderList

Top Header에 정의한 컬럼 목록을 가져온다

public

bool

LeftHeaderVisible

맨 왼쪽의 컬럼을 보여줄 경우 true, 그렇지 않으면 false

public

int

FixedCol

고정할 컬럼 개수. 스크롤 시에 고정되는 컬럼의 개수를 설정 또는 반환.

첫번째 컬럼부터 시작하며 컬럼의 Visiable 속성이 true인 컬럼의 개수만큼 고정이됨

public

int

XscHeight

가로 스크롤의 높이

public

int

YscWidth

세로 스크롤의 폭

public

bool

FixedColEditable

고정컬럼 영역의 값 수정 여부.

false인 경우 Column의 값을 수정할 수 없음

public

int

AllRowsHeight

헤더 영역을 제외한 행(Row)의 총 높이(길이) 값을 반환

public

RowCollection

Rows

행(Row) 정보를 담고 있는 RowCollection을 반환

public

List<int>

SelectedRows

선택된 행(Row)의 Index정보 목록을 반환

public

List<int>

SelectedCols

선택된 열(Column)의 Index정보 목록을 반환

public

bool

ReadOnly

WANIGrid의 내용을 읽기 전용으로 설정 또는 반환.

ReadOnly 값이 true인 경우 WANIGrid 내의 값을 변경할 수 없다.

 

WANIGrid Control 사용 예제를 담은 파일은 아래를 참조.

WANIGridExample_20200430.zip
0.21MB

반응형

'WinForm > WANIGrid 컨트롤 공개' 카테고리의 다른 글

WANIGrid Control 개선 - Mouse Wheel 처리 추가  (0) 2021.04.13
WANIGrid Control 개선  (0) 2021.04.10
WANIGrid Control 사용법 2  (0) 2020.05.14
반응형

그 동안 찾아온 게으름이란 녀석 때문에 한 동안 WANI Grid Control의 소스를 꽤나 멀리했다.

근자에 바빴던 필자의 본업으로 인해 정신적 시간적 여유가 없어서 소스 보면서 어느 부분을 좀 더 개선하면 보다 쓸만한 녀석으로 바꿀 수 있을지에 대한 생각을 해보지 못했다.

2019년 한 해를 보내며 지금 작성한 이글이 2019년 마지막 글이 될지도 모르겠다.

 

오늘 추가 및 개선해 볼 기능은 특정 Column선택 시 표시되는 부분과 행(Row) 숨기기 및 숨기기 해제 기능을 추가해 볼 것이다.

 

기존의 특정 컬럼 선택 시 표현 되는 부분은 아래의 이미지와 같다.

[그림 25-1] 기존 컬럼 선택 시 표현 방법

선택된 컬럼의 행(Row)를 표기 하기 위해 LightCyan으로 표시하고 Dot선으로 Retangle을 그려서 표시를 해주고 있지만 눈으로 식별되는 부분이 약한 듯 해서 선택된 Rectangle 부분의 색상을 바탕색과 동일한 색상으로 표기를 하고자 한다.

개선한 컬럼 선택 부분은 아래의 이미지를 참고하자.

[그림 25-2] 개선된 컬럼 선택 표현 방법

사용자 좀 더 정확하게 선택한 컬럼을 식별하기가 쉽도록 개선되었다.

이 부분을 보완하기 위해서는 WANIGrid.Event.cs 파일을 열어서 Paint 영역에서 호출되는 메소드의 위치와 DrawActiveCell 메소드의 내용을 보완하면 된다.

    private void WANIGrid_Paint(object sender, PaintEventArgs e)
    {
        CalcVisibleRange();
        ReCalcScrollBars();
        DrawBackground(e.Graphics, rc);            
        DrawHeaders(e.Graphics, rc, colFixed, fixedColEditable);
        DrawActiveCell(e.Graphics);
        DrawContent(e.Graphics, rc, this.ClientRectangle.Width);            
    }

 

기존의 마지막에 위치해 있던 DrawActiveCell 메소드를 WANI Grid 내의 내용을 그리는 메소드인 DrawContent 메소드 앞에 위치를 시키고 DrawActiveCell 메소드에 선택한 컬럼의 Rectangle 부분에 FillRectangle 처리를 해주면 된다.

WANIGrid.Method.cs 파일을 열어서 DrawActiveCell 메소드의 FillRectange 부분을 추가해 주면 된다.

    private void DrawActiveCell(Graphics g)
    {
        if (ActiveCell.Row != -1 && ActiveCell.Col != -1)
        {
            if (ActiveCell.Col > grid.GridHeaderList.Count) return;
            //Calendar 날짜 컬럼은 Active Cell이 되지 않도록 처리
            if (grid.GridHeaderList[ActiveCell.Col].IsDate) return;
 

             //파선 패턴 설정
            float[] dashValues = { 1, 1, 1, 1 };
            Pen grayPen = new Pen(Color.Gray, 1);
            grayPen.DashPattern = dashValues;
            g.FillRectangle(new SolidBrush(SystemColors.ControlLightLight), GetSelectedCellRect(ActiveCell.Row, ActiveCell.Col));
            g.DrawRectangle(grayPen, GetSelectedCellRect(ActiveCell.Row, ActiveCell.Col));
        }
    }

 

행(Row) 숨기기 기능을 추가하기 위해서는 한개의 행(Row) 정보를 담당하는 Row 클래스에 행 숨기기 정보를 관리하기 위한 변수를 추가해야 한다.

Row.cs 파일을 열어서 bool 타입의 hidden 변수와 Hidden Property를 선언 및 정의하기로 한다.

    private bool hidden = false;

    public bool Hidden
    {
        get { return hidden; }
        set { hidden = value; }
    }

행을 숨기고자 할 경우 Row 클래스의 hidden 변수에 true를 설정하고, 숨기기 취소를 할 경우 false를 설정하면 된다.

 

Context Menu에 선택한 행들을 모두 숨기기 처리하기 위한 메뉴와 숨기기 처리된 모든 행을 숨기기 취소하는 메뉴를 추가하도록 한다.

 

기존의 행을 그리던 모든 메소드에 행(Row)을 관리하던 컬렉션인 rows 변수의 해당 행의 row의 hidden 변수가 true일 경우 해당 행을 그리지 않고 Skip할 수 있도록 if (rows[i].hidden) continue;  부분을 모두 추가해 주면 된다.

다국어 처리를 위해 Resources폴더 내의 LanguageResource.ko-KR.resx, LanguageResource.resx 파일을 열어서 메뉴 명칭을 추가한다.

Row.Hidden : 행 숨기기/Row Hide, Row.HiddenCancel : 행 숨기기 취소/Cancel Row Hide

    private void InitializeContextMenu()
    {
        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.Add(LanguageResource.Row_Hidden);
        rightClickMenu.MenuItems.Add(LanguageResource.Row_HiddenCancel);

        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);
        rightClickMenu.MenuItems[4].Click += new EventHandler(OnMenu_RowHidden_Click);
        rightClickMenu.MenuItems[5].Click += new EventHandler(OnMenu_RowHiddenCancel_Click);
    }

 

WANIGrid.Event.cs 파일 내에 행 숨기기/숨기기 취소 이벤트 처리를 해 둔 부분과 마우스 좌측 버튼 클릭시 호출되는 MouseLeftButtonClickContents 메소드의 추가된 행(Row)의 hidden 변수가 true일 때 처리를 위해서 추가한

if (rows[row].Hidden) continue;  부분을 참고하기 바랍니다.

 

WANIGrid.Mehtod.cs 파일을 열어서 행(Row)의 hidden 변수가 true일 경우 화면에서 그려지지 않도록 추가한 부분 역시

if (rows[row].Hidden) continue; 이 부분이 추가되어져 있다.

소스 내의 rows[row].Hidden 이 true일 경우 화면 상에 표시 되지 않도록 처리한 부분들이다.

이 부분들을 살펴 보면 될 것이다.

추가로 선택한 행들을 숨기기 처리하거나 숨겨진 모든 행(Row)들을 숨기기 취소하는 메소드인 HideAndHideCancelRow 를 살펴보기 바란다.

 

특별히 어려운 내용이 없는 관계로 변경 및 보완/추가된 영역을 알려 주었으니, 첨부한 소스 코드들을 참고해서 변경된 내용들을 참고하기 바란다.

 

WANI Grid_20191225.zip
0.74MB

반응형
반응형

Calendar 컬럼(Column)을 표현하기 위해 사전 준비 작업과 YearMonthWeekNoDayHeader, YearMonthWeekNoDayHeaderGenerator, HeaderBuilder를 살펴 보았고, 실제 WANIGrid Control 영역을 살펴보자.

 

새로운 변수를 추가하기로 하자.

    private int currentCol = 0; //현재 첫페이지를 저장하기 위한 변수

    private SolidBrush holidayColorBrush = new SolidBrush(Color.LightSkyBlue); //휴일색상

    private bool fixedColEditable = true;   //고정 컬럼 수정 여부

 

속성(Property)설정을 아래와 같이 한다.

    /// <summary>
    /// 휴일 색상으로 채워주는 Brush 값을 가져오거나 설정한다.
    /// </summary>
    public SolidBrush HolidayColorBrush
    {
        get { return holidayColorBrush;}
        set { holidayColorBrush = value; }
    }

 

    /// <summary>
    /// 고정 컬럼 수정여부
    /// </summary>
    public bool FixedColEditable
    {
        get { return fixedColEditable; }
        set { fixedColEditable = value; }
    }

 

WANIGrid 영역의 Event 처리 부분인 WANIGrid.Event.cs 파일을 추가로 보완하기로 하자.

Calendar 컬럼 영역의 헤드를 마우스 클릭 했을 때 특정 컬럼이 선택되지 않도록 처리하기 위해서 마우스 좌측 버튼이 헤더 영역에서 클릭 이벤트가 있는지 체크해야 한다.

이러한 이벤트를 잡기 위해서 MouseLeftButtonClickInTopHeadHeight 메소드를 찾아서 아래의 파란색 부분을 추가한다.

 

    /// <summary>
    /// WANIGrid의 Header 영역에서 마우스 좌측 버튼 클릭 시
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void MouseLeftButtonClickInTopHeadHeight(object sender, MouseEventArgs e)
    {
        selectedRows.Clear();

        //컬럼이 존재할 경우
        if (grid.GridHeaderList.Count > 0)
        {
            int col = GetColFromX(e.X);
            if (col < 0) return; //col값이 -1이면 처리하지 않음                

            //Calendar 날짜 컬럼은 선택되지 않도록 처리
            if (grid.GridHeaderList[col].IsDate) return;

            //Control Key를 누르지 않은 상태에서 컬럼을 선택했을 경우
            if (Control.ModifierKeys != Keys.Control && Control.ModifierKeys != Keys.Shift)
            {

            :

            :

            }

         :

        }

      :

    }

  

Calendar 컬럼의 헤더 영역을 마우스 이동 시 컬럼 폭 조정이 되지 않도록 처리해야 한다.

WANIGrid_MouseMove 이벤트의 폭을 변경하는 영역에 해당 컬럼이 Calendar정보를 가진 컬럼인지 체크하는 부분을 추가해야 한다. 아래의 파란색 부분을 참조하자.

    /// <summary>
    /// Mouse Move Event
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    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;
        }         

          :
        if (grid.GridHeaderList.Count > 0 && e.Y < topHeaderHeight)
        {  
            :    
            //Header의 컬럼과 컬럼 간의 경계선 상에 마우스 포인트가 위치했을 경우
            if (!grid.GridHeaderList[i].IsDate && (e.X > colLine - 2 && e.X < colLine + 2))
            {
                Cursor = Cursors.VSplit;
                resizeCol = i;
                if (resizeCol >= grid.GridHeaderList.Count) resizeCol = grid.GridHeaderList.Count - 1;
                break;
            }
            :
        } else
        {
                        :
                        //Header의 컬럼과 컬럼 간의 경계선 상에 마우스 포인트가 위치했을 경우
                        if (!grid.GridHeaderList[i].IsDate && (e.X > colLine - 2 && e.X < colLine + 2))
                        {
                            Cursor = Cursors.VSplit;
                            resizeCol = i;
                            if (resizeCol >= grid.GridHeaderList.Count) resizeCol = grid.GridHeaderList.Count - 1;
                            break;
                        }
                    :
                        //Header의 컬럼과 컬럼 간의 경계선 상에 마우스 포인트가 위치했을 경우
                        if (!grid.GridHeaderList[i].IsDate && (e.X > colLine - 2 && e.X < colLine + 2))
                        {
                            Cursor = Cursors.VSplit;
                            resizeCol = i;
                            if (resizeCol >= grid.GridHeaderList.Count) resizeCol = grid.GridHeaderList.Count - 1;
                            break;
          :
        }
    }

 

컬럼의 데이터 수정 시 RowCollection 타입의 rows변수의 속성인 DataRow에  값이 null일 경우에 Event 처리가 되지 않도록 한다. 아래의 파란색 부분을 추가한다.

    /// <summary>
    /// TextBox인 editBox의 TextChanged Event
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void EditBox_TextChanged(object sender, EventArgs e)
    {
        :

        if (rows[ActiveCell.Row].DataRow == null) return;
        DataRow row = rows[ActiveCell.Row].DataRow;
        Header header = grid.GridHeaderList.Where(x => x.Index == ActiveCell.Col).FirstOrDefault();
        if (header != null)
        {
            if (ActiveCell_ActvieCol > -1) row[header.ColumnId] = editBox.Text;
        }
    }

 

WANIGrid Control상에서 Control키를 누르고 + 또는 - 키를 눌렀을 때 Calendar 컬럼의 Width를 1씩 줄여서 Display하는 KeyDown 이벤트를 추가한다.

    /// <summary>
    /// Control Key + : 폭 넓게
    /// Control Key - : 폭 좁게
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void WANIGrid_KeyDown(object sender, KeyEventArgs e)
    {
        if (this.grid.GridDisplayType != GridType.YearMonthWeekNoDayType) return;            

        if (e.Control && e.KeyCode == Keys.Add)
        {
            foreach(YearMonthWeekNoDayHeader header in this.grid.GridHeaderList)
            {
                if (header.IsDate) header.Width += 1;
            }
        } else if (e.Control && e.KeyCode == Keys.Subtract)
        {
            foreach (YearMonthWeekNoDayHeader header in this.grid.GridHeaderList)
            {
                if (header.IsDate) header.Width -= 1;
            }
        }
        Invalidate();
    }

 

WANIGrid.Method.cs파일의 추가/보완할 부분들을 살펴보자

먼저 Grid 컬럼의 Header를 생성해서 추가하는 SetHeader 메소드를 살펴보자.

추가/변경 되는 부분은 아래의 파란색 부분을 참조하자.

    /// <summary>
    /// Grid 컬럼의 Header를 생성해서 추가한다.
    /// </summary>
    /// <param name="headerBuilder"></param>
    public void SetHeader(HeaderBuilder headerBuilder)
    {
        if (grid == null) grid = new WANI_Grid.Grid.Grid();
        if (headerBuilder != null)
        {
            grid.HeaderGen = headerBuilder.HeaderGen;
            topHeaderHeight = headerBuilder.HeaderGen.TopHeaderHeight;
            grid.DicWeekDay = headerBuilder.DicWeekDay;
            grid.DicMonthDay = headerBuilder.DicMonthDay;
        }

        grid.GridDisplayType = headerBuilder.GridDisplayType;
        grid.TopHeaderHeight = headerBuilder.HeaderGen.TopHeaderHeight;
        InitializeScollBar();

        //dataSource가 null일 경우 Header정보를 근간으로 DataTable 생성
        if (dataSource != null)
        {
            dataSource.Clear();
            dataSource = null;
            rows.Clear();
            rowHeight = 0;
            rowsCount = 0;
            selectedCols.Clear();
            selectedRows.Clear();
            allRowsHeight = 0;                
            vScrollBar.Maximum = allRowsHeight;
            firstVisibleCol = 0;
            lastVisibleCol = 0;
            firstVisibleRow = 0;
            lastVisibleRow = 0;
        }

        if (dataSource == null)
        {
            dataSource = new DataTable();
            foreach (Header hd in grid.HeaderGen.GetHeaders())
            {
                dataSource.Columns.Add(new DataColumn(hd.ColumnId, typeof(string)));
            }               
        }                                    
    }

 

Control의 Backgroud 그리는 메소드인 DrawBackground 메소드의 추가 된 부분은 아래와 같다.

    /// <summary>
    /// Control의 Backgroud 그리기
    /// </summary>
    /// <param name="g"></param>
    /// <param name="rc"></param>
    private void DrawBackground(Graphics g, Rectangle rc)
    {
        g.DrawRectangle(new Pen(SystemColors.Control, 2), 0, 0, rc.Width, rc.Height);
        rc.Inflate(-1, -1);
        g.FillRectangle(new SolidBrush(SystemColors.ControlLightLight), rc);

        int lastFixedCol = GetLastFixedCol();
        if (lastFixedCol == -1)
        {
            DrawDefaultWANIGridControl(g, rc);
            return;
        }
        //고정 컬럼으로 지정이 가능한 영역은 Calendar 컬럼이 아닌 경우에만 가능

        //Grid의 GridHeaderList 정보를 가져와서 YearMonthHeader Type의 변환 후 GetDateTime이 DateTime.MinValue 보다 작은 경우는

        //Calendar 컬럼이 아님.
        int enableFixCol = 0;
        for (int i = 0; i <= lastFixedCol; i++)
        {
            YearMonthWeekNoDayHeader yearMonthHeader = grid.GridHeaderList[i] as YearMonthWeekNoDayHeader;
            if (yearMonthHeader != null && yearMonthHeader.GetDateTime <= DateTime.MinValue) enableFixCol = i;
        }
        if (lastFixedCol > enableFixCol) lastFixedCol = enableFixCol;

        //선택된 컬럼의 Background를 그린다.
        SelectedColChangeBackground(g, lastFixedCol);

        //선택된 행(Row)의 Background를 그린다.
        SelectedRowChangeBackground(g, lastFixedCol);

        //휴일 색상을 그린다
        FillHolidayBackgroundColor(g);
    }

 

휴일 컬럼의 배경 색상을 칠하는 메소드인 FillHolidayBackgroundColor 메소드를 추가하도록 한다.

    private void FillHolidayBackgroundColor(Graphics g)
    {
        if (grid.GridDisplayType != GridType.YearMonthWeekNoDayType) return;
        //고정 컬럼이 없을 경우
        if (colFixed == 0)
        {
            int startX = leftHeaderWidth;
            for (int i = firstVisibleCol; i <= lastVisibleCol; i++)
            {
                YearMonthWeekNoDayHeader yearMonthHeader = grid.GridHeaderList[i] as YearMonthWeekNoDayHeader;                    
                if (yearMonthHeader.IsHoliday)
                {                        
                    g.FillRectangle(holidayColorBrush, startX + 1, topHeaderHeight + 1, yearMonthHeader.Width, 

                                         allRowsHeight - (rowHeight * firstVisibleRow) + 1);                        
                }
                startX += yearMonthHeader.Width;
            }
        } else
        {
            int startX = leftHeaderWidth + GetFixedColWidth();                
            for (int i = firstVisibleCol + colFixed; i <= lastVisibleCol && i < grid.GridHeaderList.Count; i++)
            {                    
                YearMonthWeekNoDayHeader yearMonthHeader = grid.GridHeaderList[i] as YearMonthWeekNoDayHeader;                    
                if (yearMonthHeader.IsHoliday)
                {                        
                    g.FillRectangle(holidayColorBrush, startX + 1, topHeaderHeight + 1, yearMonthHeader.Width, 

                                         allRowsHeight - (rowHeight * firstVisibleRow) + 1);                        
                }
                startX += grid.GridHeaderList[i].Width;
            }
        }
    }

 

Header영역을 그리는 DrawHeaders메소드에서 Parameter가 하나 더 추가 되었다. 추가된 파라미터는 고정 컬럼의 수정여부에 따라 고정 컬럼의 값을 변경 또는 변경할 수 없도록 지정하는 부분이다.

    /// <summary>
    /// Grid의 Header 그리기
    /// </summary>
    /// <param name="g"></param>
    /// <param name="rc"></param>

    /// <param name="colFixed"></param> 

    /// <param name="fixedColEditable"></param>
    private void DrawHeaders(Graphics g, Rectangle rc, int colFixed, bool fixedColEditable)
    {
        if (grid != null)
        {
            grid.DrawHeader(g, rc, rc.Width, colFixed, fixedColEditable);                
        }
    }

 

grid.GridHeaderList의 IsDate가 true인 경우에 선택된 Active Cell의 경우 Active Cell 표시가 되지 않도록 한다.

    /// <summary>
    /// Active Cell을 표기한다.
    /// </summary>
    /// <param name="g"></param>
    private void DrawActiveCell(Graphics g)
    {
        if (ActiveCell.Row != -1 && ActiveCell.Col != -1)
        {
            //Calendar 날짜 컬럼은 Active Cell이 되지 않도록 처리
            if (grid.GridHeaderList[ActiveCell.Col].IsDate) return;

            //파선 패턴 설정
            float[] dashValues = { 1, 1, 1, 1 };
            Pen grayPen = new Pen(Color.Gray, 1);
            grayPen.DashPattern = dashValues;
            g.DrawRectangle(grayPen, GetSelectedCellRect(ActiveCell.Row, ActiveCell.Col));
        }
    }

 

지금까지의 작업으로 완성된 WANIGrid Test 화면은 아래와 같다.

아래의 테스트 화면에 대한 사용 설명은 다음 시간에 간단히 정리하도록 하겠다.

[그림 24-1] WANIGrid Test 화면

 

그외에도 가로/세로 스크롤바를 재계산하는 ReCalcScrollBars메소드의 오류 영역을 보완했고, 자잘한 버그들을 찾아서 조치했다.

이 부분은 직접 소스를 참조하도록 하자. 

 

WANI Grid_20191009.zip
0.55MB

 

 

 

 

 

반응형
반응형

이전 시간에 일/주차/년월 정보의 Header를 구성하는 YearMonthWeekNoDayHeader.cs 파일 설정과 내용을 살펴 보았다. 오늘은 실제 일/주차/년월 정보의 Header를 그리는 YearMonthWeekNoDayHeaderGenerator.cs 파일을 살펴보기로 하자.

실제 WANIGrid의 Header 영역을 직접 생성하는 클래스 중 기본 WANIGrid의 Header를 생성하는 클래스인  DefaultHeaderGenerator.cs를 살펴 보았다.

이번에는 일/주차/년월 Header를 생성하는 YearMonthWeekNoDayHeaderGenerator.cs를 만들어 볼 것이다.

YearMonthWeekNoDayHeaderGenerator클래스는 HeaderGenerator클래스를 상속 받아서 구현한다.

YearMonthWeekNoDayHeaderGenerator클래스에는 고정 컬럼이 있을 경우와 없을 경우의 DrawHeaders라는 메소드외에 몇몇 메소드들을 재정의해서 Header영역을 그리는 부분을 구현해야 한다.

 

    public class YearMonthWeekNoDayHeaderGenerator : HeaderGenerator
    {        
        #region 생성자
        public YearMonthWeekNoDayHeaderGenerator()
        {
            TopHeaderHeight = 60;
            this._headers.Clear();
        }
        #endregion 생성자       

        public override void AddHeaders(object obj)
        {
            this._headers.Add(obj as Header);
        }

        public override List GetHeaders()
        {
            return this._headers;
        }

        public override void HeaderClear()
        {
            this._headers.Clear();
        }

        //고정컬럼이 없을 경우 Header 그리기

        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);

            int columnStartX = 0;
            int calendarStartX = 0;            

            graphics.FillRectangle(brush, columnStartX + 1, 1, leftHeaderWidth, topHeaderHeight);
            graphics.DrawRectangle(pen, columnStartX + 1, 1, leftHeaderWidth, topHeaderHeight);

            columnStartX += leftHeaderWidth;  //첫 시작컬럼의 폭을 leftHeaderWidth 만큼 설정 

            //주차 기간을 저장하기 위한 Dictionary - width 계산을 위해 필요
            Dictionary<int, List> dicWeekNo = new Dictionary<int, List>();
            //년월 기간을 저장하기 위한 Dictionary - width 계산을 위해 필요
            Dictionary<string, List> dicYearMonth = new Dictionary<string, List>();

            for (int i = firstVisibleCol; i <= lastVisibleCol; i++)
            {
                if (!this._headers[i].Visible) continue;
                int headerWidth = this._headers[i].Width;   //i 번째 컬럼의 폭을 설정                                    

                //보여지는 컬럼의 폭이 컨트롤의 폭 보다 클경우
                if (columnStartX + headerWidth > controlWidth)
                {
                    headerWidth = controlWidth - columnStartX - 3;
                    if (lastVisibleCol == (this._headers.Count - 1)) IsLargeLastCol = true;
                }
                else
                {
                    IsLargeLastCol = false;
                }

                YearMonthWeekNoDayHeader yearMonthHeader = this._headers[i] as YearMonthWeekNoDayHeader;
                if (yearMonthHeader.GetDateTime <= DateTime.MinValue)
                {
                    //Grid Header를 그린다.
                    DrawTextUtil.DrawGridHeaderRectangleAndText(graphics, brush, blackBrush, pen, this._headers, headerFont,

                                                                              i, columnStartX, headerWidth, topHeaderHeight);
                }
                else
                {
                    if (calendarStartX == 0) calendarStartX = columnStartX;

                    //Calendar 날짜 형태의 컬럼 Header 그리기(일)
                    DrawTextUtil.DrawGridHeaderCalendarRectangleAndText(graphics, brush, blackBrush, pen, 

                                                                                         this._headers, headerFont, i, columnStartX, headerWidth);
                    if (!dicWeekNo.ContainsKey(yearMonthHeader.WeekNumber)) dicWeekNo.Add(yearMonthHeader.WeekNumber, new List());
                    if (yearMonthHeader.Visible) dicWeekNo[yearMonthHeader.WeekNumber].Add(yearMonthHeader.Width);
                    if (!dicYearMonth.ContainsKey(yearMonthHeader.YearMonth)) dicYearMonth.Add(yearMonthHeader.YearMonth, new List());
                    if (yearMonthHeader.Visible) dicYearMonth[yearMonthHeader.YearMonth].Add(yearMonthHeader.Width);
                }
                columnStartX += headerWidth;
            }
            //Calendar 날짜 형태의 컬럼 Header 그리기(주차/년월)
            DrawTextUtil.DrawGridHeaderCalendarWeekNumberRectangleAndText(graphics, brush, blackBrush, pen, 

                                                                                                   headerFont, dicWeekNo, calendarStartX);
            DrawTextUtil.DrawGridHeaderCalendarYearMonthRectangleAndText(graphics, brush, blackBrush, pen, 

                                                                                                headerFont, dicYearMonth, calendarStartX);            
        }
        //고정컬럼이 있을 경우 Header 그리기 
        public override void DrawHeaders(int colFixed, int firstVisibleCol, int lastVisibleCol, int controlWidth, Graphics graphics, 

                                                      Rectangle rect, bool fixedColEditable)
        {
            SolidBrush brush = new SolidBrush(SystemColors.ControlLight);
            Pen pen = new Pen(Color.LightGray);

            int columnStartX = 0;
            int calendarStartX = 0;
            graphics.FillRectangle(brush, columnStartX + 1, 1, leftHeaderWidth, topHeaderHeight);
            graphics.DrawRectangle(pen, columnStartX + 1, 1, leftHeaderWidth, topHeaderHeight);

            columnStartX += leftHeaderWidth;  //첫 시작컬럼의 폭을 leftHeaderWidth 만큼 설정             
            int fixCol = this.GetLastFixedCol(colFixed);
            //주차 기간을 저장하기 위한 Dictionary - width 계산을 위해 필요
            Dictionary<int, List> dicWeekNo = new Dictionary<int, List>();
            //년월 기간을 저장하기 위한 Dictionary - width 계산을 위해 필요
            Dictionary<string, List> dicYearMonth = new Dictionary<string, List>();
            
            for (int i = 0; i <= fixCol; i++)
            {
                if (!this._headers[i].Visible) continue;
                int headerWidth = this._headers[i].Width;   //i 번째 컬럼의 폭을 설정  

                //고정컬럼 수정여부 확인
                if (!fixedColEditable) this._headers[i].Editable = false;

                //보여지는 컬럼의 폭이 컨트롤의 폭 보다 클경우
                if (columnStartX + headerWidth > controlWidth)
                {
                    headerWidth = controlWidth - columnStartX - 3;
                    if (lastVisibleCol == (this._headers.Count - 1)) IsLargeLastCol = true;
                }
                else
                {
                    IsLargeLastCol = false;
                }

                YearMonthWeekNoDayHeader yearMonthHeader = this._headers[i] as YearMonthWeekNoDayHeader;            
                DrawTextUtil.DrawGridHeaderRectangleAndText(graphics, brush, blackBrush, pen, this._headers, headerFont, 

                                                                          i, columnStartX, headerWidth, topHeaderHeight);
                
                columnStartX += headerWidth;                
            }            

            for (int i = firstVisibleCol + fixCol + 1; i <= lastVisibleCol && i < this._headers.Count; i++)
            {
                if (!this._headers[i].Visible) continue;
                int headerWidth = this._headers[i].Width;   //i 번째 컬럼의 폭을 설정                                    

                //보여지는 컬럼의 폭이 컨트롤의 폭 보다 클경우
                if (columnStartX + headerWidth > controlWidth)
                {
                    headerWidth = controlWidth - columnStartX - 3;
                    if (lastVisibleCol == (this._headers.Count - 1)) IsLargeLastCol = true;
                }
                else
                {
                    IsLargeLastCol = false;
                }                

                YearMonthWeekNoDayHeader yearMonthHeader = this._headers[i] as YearMonthWeekNoDayHeader;
                if (yearMonthHeader.GetDateTime <= DateTime.MinValue)
                {
                    //Grid Header를 그린다.
                    DrawTextUtil.DrawGridHeaderRectangleAndText(graphics, brush, blackBrush, pen, this._headers, headerFont, 

                                                                              i, columnStartX, headerWidth, topHeaderHeight);
                }
                else //Calendar 날짜 형태의 컬럼을 그리기 위한 사전 준비
                {
                    if (calendarStartX == 0) calendarStartX = columnStartX;                    
                    if (!dicWeekNo.ContainsKey(yearMonthHeader.WeekNumber)) dicWeekNo.Add(yearMonthHeader.WeekNumber, new List());
                    if (yearMonthHeader.Visible) dicWeekNo[yearMonthHeader.WeekNumber].Add(yearMonthHeader.Width);
                    if (!dicYearMonth.ContainsKey(yearMonthHeader.YearMonth)) dicYearMonth.Add(yearMonthHeader.YearMonth, new List());
                    if (yearMonthHeader.Visible) dicYearMonth[yearMonthHeader.YearMonth].Add(yearMonthHeader.Width);

                    //Calendar 날짜 형태의 컬럼 Header 그리기(일)
                    DrawTextUtil.DrawGridHeaderCalendarRectangleAndText(graphics, brush, blackBrush, pen, this._headers, headerFont, 

                                                                                         i, columnStartX, headerWidth);
                }

                columnStartX += headerWidth;
            }
            //Calendar 날짜 형태의 컬럼 Header 그리기(주차/년월)
            DrawTextUtil.DrawGridHeaderCalendarWeekNumberRectangleAndText(graphics, brush, blackBrush, pen, 

                                                                                                   headerFont, dicWeekNo, calendarStartX);
            DrawTextUtil.DrawGridHeaderCalendarYearMonthRectangleAndText(graphics, brush, blackBrush, pen, 

                                                                                               headerFont, dicYearMonth, calendarStartX);
        }        
    }

 

실제 Header 영역에 text를 출력하기 위한 DrawTextUtil이라는 클래스를 추가한다.

DrawTextUtil 클래스는 Header영역의 텍스트를 출력하기 위한 클래스로 헤드 영역의 Rectangle 사이즈에 맞는 폰트 사이즈를 계산해서 적정한 크기의 Header Title을 출력하는 역할을 한다.

 

    public static class DrawTextUtil
    {
        /// Grid Header를 그린다.
        public static void DrawGridHeaderRectangleAndText(Graphics graphics, SolidBrush brush, SolidBrush blackBrush, Pen pen, 

             List headers, Font headerFont, int index, int columnStartX, int headerWidth, int topHeaderHeight)
        {
            if (headerWidth == 0) return;
            //헤더영역의 사각형을 채우고 테두리를 그린다.
            graphics.FillRectangle(brush, columnStartX + 1, 1, headerWidth, topHeaderHeight);
            graphics.DrawRectangle(pen, columnStartX + 1, 1, headerWidth, topHeaderHeight);

            //헤더 타이틀 정렬 방법 설정
            StringFormat sf = new StringFormat();
            sf.LineAlignment = StringAlignment.Center;
            sf.Alignment = StringUtil.GetStringAlignment(sf, HorizontalAlignment.Center);

            //헤더 타이틀을 그린다.
            Rectangle colRec = new Rectangle(columnStartX + 1, 1, headerWidth, topHeaderHeight);
            if (headers == null)
            {
                graphics.DrawString("", headerFont, blackBrush, colRec, sf);
            }
            else
            {
                graphics.DrawString(headers[index].Title, headerFont, blackBrush, colRec, sf);
            }
        }

        /// Grid Calendar Day Header를 그린다.
        public static void DrawGridHeaderCalendarRectangleAndText(Graphics graphics, SolidBrush brush, SolidBrush blackBrush, Pen pen,
                 List headers, Font headerFont, int index, int columnStartX, int headerWidth)
        {
            //일자 출력
            if (headerWidth == 0) return;
            int dayYLocation = 32;
            int dayHeight = 28;

            //헤더영역의 사각형을 채우고 테두리를 그린다.
            graphics.FillRectangle(brush, columnStartX + 1, dayYLocation + 1, headerWidth, dayHeight);
            graphics.DrawRectangle(pen, columnStartX + 1, dayYLocation + 1, headerWidth, dayHeight);

            //헤더 타이틀 정렬 방법 설정
            StringFormat sf = new StringFormat();
            sf.LineAlignment = StringAlignment.Center;
            sf.Alignment = StringUtil.GetStringAlignment(sf, HorizontalAlignment.Center);            

            //헤더 타이틀을 그린다.
            Rectangle colRec = new Rectangle(columnStartX + 1, dayYLocation + 1, headerWidth, dayHeight);
            Rectangle colRec1 = new Rectangle(columnStartX + 1, dayYLocation + 1, headerWidth, dayHeight / 2);
            Rectangle colRec2 = new Rectangle(columnStartX + 1, dayYLocation + (dayHeight / 2) + 1, headerWidth, dayHeight / 2);
            if (headers == null)
            {
                graphics.DrawString("", headerFont, blackBrush, colRec, sf);
            }
            else
            {                
                if (headers[index].Width >= 5 && headers[index].Width < 15)
                {
                    Font dayFont = CalculateFontSize(graphics, headers[index].Title.Substring(0, 1), headerFont, columnStartX, 

                                                                  dayYLocation, headerWidth, dayHeight);
                    if (dayFont.Size > 8.3) dayFont = new Font(headerFont.Name, 8.4f);
                    graphics.DrawString(headers[index].Title.Substring(0,1), dayFont, blackBrush, colRec1, sf);
                    graphics.DrawString(headers[index].Title.Substring(1,1), dayFont, blackBrush, colRec2, sf);
                } else
                {
                    Font calHeadFont = CalculateFontSize(graphics, headers[index].Title, headerFont, columnStartX, dayYLocation, 

                                                                       headerWidth, dayHeight);
                    if (calHeadFont.Size > 8.3) calHeadFont = new Font(headerFont.Name, 8.4f);
                    graphics.DrawString(headers[index].Title, calHeadFont, blackBrush, colRec, sf);                    
                }
            }                        
        }

        /// Grid Calendar Week Number Header를 그린다.
        public static void DrawGridHeaderCalendarWeekNumberRectangleAndText(Graphics graphics, SolidBrush brush, SolidBrush blackBrush,

                      Pen pen, Font headerFont, Dictionary<int, List> dicWeekNo, int columnStartX)
        {
            int dayYLocation = 16;
            int weekNoHeight = 16;
            foreach (KeyValuePair<int, List> items in dicWeekNo)
            {
                int weekWidth = items.Value.Sum();
                //헤더영역의 사각형을 채우고 테두리를 그린다.
                graphics.FillRectangle(brush, columnStartX + 1, dayYLocation + 1, weekWidth, weekNoHeight);
                graphics.DrawRectangle(pen, columnStartX + 1, dayYLocation + 1, weekWidth, weekNoHeight);

                //헤더 타이틀 정렬 방법 설정
                StringFormat sf = new StringFormat();
                sf.LineAlignment = StringAlignment.Center;
                sf.Alignment = StringUtil.GetStringAlignment(sf, HorizontalAlignment.Center);

                //헤더 타이틀을 그린다.
                Rectangle colRec = new Rectangle(columnStartX + 1, dayYLocation + 1, weekWidth, weekNoHeight);
                //적절한 폰트 사이즈를 계산
                Font calHeadFont = CalculateFontSize(graphics, items.Key.ToString(), headerFont, columnStartX, dayYLocation, 

                                                                   weekWidth, weekNoHeight);
                graphics.DrawString(items.Key.ToString(), calHeadFont, blackBrush, colRec, sf);

                columnStartX += weekWidth;
            }                        
        }

        /// Grid Calendar Year Month Header를 그린다.
        public static void DrawGridHeaderCalendarYearMonthRectangleAndText(Graphics graphics, SolidBrush brush, SolidBrush blackBrush,

                   Pen pen, Font headerFont, Dictionary<string, List> dicYearMonth, int columnStartX)
        {
            int dayYLocation = 0;
            int weekNoHeight = 16;
            foreach (KeyValuePair<string, List> items in dicYearMonth)
            {
                int yearMonthWidth = items.Value.Sum();
                //헤더영역의 사각형을 채우고 테두리를 그린다.
                graphics.FillRectangle(brush, columnStartX + 1, dayYLocation + 1, yearMonthWidth, weekNoHeight);
                graphics.DrawRectangle(pen, columnStartX + 1, dayYLocation + 1, yearMonthWidth, weekNoHeight);

                //헤더 타이틀 정렬 방법 설정
                StringFormat sf = new StringFormat();
                sf.LineAlignment = StringAlignment.Center;
                sf.Alignment = StringUtil.GetStringAlignment(sf, HorizontalAlignment.Center);

                //헤더 타이틀을 그린다.
                Rectangle colRec = new Rectangle(columnStartX + 1, dayYLocation + 1, yearMonthWidth, weekNoHeight);

                Font calHeadFont = CalculateFontSize(graphics, items.Key.ToString(), headerFont, columnStartX, dayYLocation, 

                                                                   yearMonthWidth, weekNoHeight);
                graphics.DrawString(items.Key.ToString(), calHeadFont, blackBrush, colRec, sf);                

                columnStartX += yearMonthWidth;
            }
        }

        /// 사각형 사이즈에 맞는 폰트 크기를 계산한다.
        public static Font CalculateFontSize(Graphics g, string header, Font headerFont, int columnStartX, int columnStartY,

                     int headerWidth, int headerHeight)
        {
            Single Factor, FactorX, FactorY;
            Rectangle rec = new Rectangle(columnStartX + 1, columnStartY + 1, headerWidth, headerHeight);
            SizeF sz = g.MeasureString(header, headerFont);

            FactorX = headerWidth / sz.Width;
            FactorY = headerHeight / sz.Height;

            if (FactorX > FactorY) Factor = FactorY;
            else Factor = FactorX;

            if (Factor > 0)
                return new Font(headerFont.Name, headerFont.SizeInPoints * Factor);
            else
                return new Font(headerFont.Name, headerFont.SizeInPoints * 0.1f);
        }        
    }

 

추가된 YearMonthWeekNoDayHeaderGenerator 클래스를 반영하기 위해 Grid 클래스(Grid.cs)에서 추가 및 보완해야 할 부분을 살펴보자.

YearMonthWeekNoDayHeaderGenerator 영역에서 헤더 영역의 주차/년월 정보를 그리기 위해서 생성했던 Dictionary 변수를 Grid클래스를 통해서 접근할 수 있도록 변수 및 속성(Property)를 추가한다.

    private Dictionary<int, List> dicWeekDay = null; //주차
    private Dictionary<string, List> dicMonthDay = null; //년월

 

    public Dictionary<int, List> DicWeekDay
    {
        get { return dicWeekDay; }
        set { dicWeekDay = value; }
    }

    public Dictionary<string, List> DicMonthDay
    {
        get { return dicMonthDay; }
        set { dicMonthDay = value; }
    }

 

WANIGrid클래스에서 Header 영역을 그리기 위해 호출하는 DrawHeader 메소드의 파라미터를 추가한다.

추가된 파라미터는 고정컬럼을 수정할지/수정을 하지 못하도록 할 것인지를 결정하는 bool 값이다.

고정컬럼이 지정되어져 있을 경우, 고정컬럼에 대한 수정 여부를 넘겨주는 값이다.

    public void DrawHeader(Graphics graphics, Rectangle rect, int clientWidth, int colFixed, bool fixedColEditable)
    {
        if (headerGen != null)
        {
            if (colFixed < 0) colFixed = 0;
            if (colFixed == 0)
            {                    
                headerGen.DrawHeaders(firstVisibleCol, lastVisibleCol, clientWidth, graphics, rect);
            }
            else
            {
                headerGen.DrawHeaders(colFixed, firstVisibleCol, lastVisibleCol, clientWidth, graphics, rect, fixedColEditable);
            }
        }
    }

 

지금까지는 새로 추가된 HeaderGenerator인 YearMonthWeekNoDayHeaderGenerator클래스에 대해서 살펴 보았다.

다음에는 이렇게 추가한 Header영역의 클래스를 기반으로 WANIGrid클래스(WANIGrid.cs)에서의 추가/보완 사항들을 하나씩 살펴보기로 하자.

반응형
반응형

지금까지는 기본적인 Grid 기본 기능을 DefaultHeader와 DefaultHeaderGenerator를 이용해서 구현했다.

기본 Grid 기능에 Calendar 정보를 해서 일정계획을 세울 수 있는 GridType을 새롭게 정의해서 개발을 해보고자 한다.

아래의 이미지와 같이 좌측의 일반적인 Grid 정보와 함께 우측에는 특정 기간까지의 일자 정보를 추가해서 일정계획을 세울 수 있도록 기능을 제공할 수 있는 GridType을 같이 만들어 보고자 한다.

새로운 GridType을 추가 하기 위해 필요한 사전 작업들을 하나씩 살펴보면서 구현을 해보자.

[그림 22-1] 새로운 유형의 GridType

먼저 위 화면에서 보이는 Calendar 셀들은 시작일자와 종료일자를 입력받아서 일자 셀(Cell)의 기본 폭(Width)을 10으로 설정해서 일/주차/년월 정보를 헤더로 제공해야 한다.

또한 Calendar 셀에서 데이터 입력과 같은 작업은 할 수 없도록 제어한다.

최종적으로는 시작일자와 종료일자를 기준으로 특정 심볼(Symbol)을 이용해서 Activity 또는 과제를 Calendar 셀 위에 생성/추가해서 화면 상에 표시하고, 이렇게 표기된 심볼을 이동하거나 길이를 조정해서 일정 계획을 세울 수 있도록 할 예정이다.

 

HeaderBuilder클래스의 AddHeader를 통해서 표기해야 할 컬럼헤더를 생성한다. 위의 그림 기준으로 보면 Column 01~04까지의 컬럼이 되겠다.

HeaderBuilder 클래스의 StartDate/FinishDate 속성 값을 설정하면 설정된 시작일자와 종료일자까지 Calendar 셀을 자동으로 생성하도록 하면 된다.

 

추가 되는 일자 정보관리를 위해서 기존의 Class파일 들을 하나씩 살펴보고 추가/보완해 보기로 하자.

먼저 Header.cs 파일을 살펴보자.

날짜 정보만 관리하게 되는 컬럼이 추가로 생성되다 보니 기존의 사용자 또는 개발자가 AddHeader로 생성한 컬럼과 구별하기 위한 값이 필요하게 된다. 즉 Calendar 정보가 저장된 컬럼임을 식별할 수 있는 변수와 속성 값이 필요하게 된다.

Header.cs 파일에 isDate 변수와 IsDate 속성을 추가하도록 한다.

 

    private bool isDate = false;    //Calendar를 그리기 위한 날짜 정보인지 여부

 

    public bool IsDate
    {
        get { return isDate; }
        set { isDate = value; }
    }

 

기존 속성 중 Width 속성의 제한 폭을 10에서 5로 하향 조정한다. 이는 Control키와 플러스(+)/마이너스(-)키를 눌러서 Calender컬럼들의 폭을 키우거나 줄일 경우 최소값을 5로 조정한다. 즉 5이하로는 더 이상 폭이 줄지 않도록 제어하는 역할을 하게 된다.

 

    public int Width
    {
        get { return width; }
        set
        {
            if (value < 5)
                width = 5;
            else
                width = value;
            OnWidthResized();
        }
    }

 

HeaderGenerator.cs 파일을 살펴보자.

GridType.cs 파일을 보면 DefaultType/YearMonthWeekNoType/YearMonthWeekNoDayType 이렇게 3개가 존재한다.

  • DefaultType - 기본 Grid 유형으로 일반적인 Grid Control의 기능만을 제공하는 유형이다.
  • YearMonthWeekNoType - 기본 Grid 유형 + 주차/년월 Calendar 정보를 제공하는 유형이다.
  • YearMonthWeekNoDayType - 기본 Grid 유형 + 일/주차/년월 Calendar 정보를 제공하는 유형이다.

HeaderGenerator 클래스 생성 시에 위의 3가지 유형 값을 설정할 수 있는 변수와 속성을 추가한다.

기본 값은 GridType.DefaultType이다.

    protected GridType gridDisplayType = GridType.DefaultType;

 

    public GridType GridDisplayType
    {
        get { return gridDisplayType; }
        set { gridDisplayType = value; }
    }

 

HeaderGenerator 클래스의 메소드 중 고정컬럼이 설정되었을 경우 기존에 호출되는 DrawHeaders 메소드의 파라미터를 1개 더 추가한다. 추가된 파라미터 fixedColEditable 변수는 고정컬럼의 수정 여부를 설정할 수 있는 bool 값이다.

고정 컬럼의 경우 수정을 할 수 없도록 설정이 필요하거나 할 경우 bool 값을 파라미터로 던져주게 된다.

true일 경우 수정이 가능하고, false의 경우 수정을 할 수 없도록 기능을 막게 된다.

    public abstract void DrawHeaders(int colFixed, int firstVisibleCol, int lastVisibleCol, int controlWidth, Graphics graphics,

                                                 Rectangle rect, bool fixedColEditable);

 

WANIGrid의 Header영역을 정의하는 기본 적인 클래스들에 대해서 변경 또는 보완해야 하는 부분들에 대해서 간단히 리뷰를 했다.

앞서 재정의한 Header 영역을 상속 받아서 구현해야 하는 YearMonthWeekNoDayHeader.cs와 YearMonthWeekNoDayHeaderGenerator.cs 파일을 살펴 보기로 하자

WANIGrid 프로젝트 > Head 폴더 아래에서 우측 마으스 버튼을 눌러서 추가 > 클래스를 선택한다.

클래스 이름을 YearMonthWeekNoDayHeader로 한다. 

[그림 22-2] 새로운 클래스 추가

YearMonthWeekNoDayHeader클래스는 기본적으로 Header클래스를 상속받아서 추상 메소드를 모두 구현하고 Header클래스에 없는 변수와 속성을 추가로 정의해야 한다.

먼저 추가해야 하는 변수부터 살펴 보기로 하자.

    private DateTime dateTime;  //헤더(Column)가 Calendar 날짜를 저장하고 있는 영역일 경우 DateTime 값을 가지게 됨
    private int weekNumber; //DateTime 값을 가지고 있으면 현 날짜의 주차 정보를 저장하기 위한 변수
    private string yearMonth;   //DateTime 값을 가지게 되면 년/월 값을 저장하기 위한 변수
    private bool isHoliday = false; //휴일 유무에 대한 bool 값을 저장
    private string pattern = ".";   //년월 구분자 (예. "." : 2019.09, "-" : 2019-09)

 

그리고 상단의 변수를 외부와 연결하기 위한 속성(Property)을 살펴보자

    /// <summary>
    /// DateTime 값을 반환. 외부에서 지정 불가.
    /// </summary>
    public DateTime GetDateTime
    {
        get { return dateTime; }
    }

    /// <summary>
    /// DateTime에 해당하는 주차 정보를 반환. 외부에서 지정 불가.
    /// </summary>
    public int WeekNumber
    {
        get { return weekNumber; }
    }

    /// <summary>
    /// DateTime에 해당하는 년월 정보를 반환. 외부에서 지정 불가
    /// </summary>
    public string YearMonth
    {
        get { return yearMonth; }
    }

    /// <summary>
    /// 휴일 여부를 반환
    /// </summary>
    public bool IsHoliday
    {
        get
        {
            //토,일을 휴일로 지정. 지정 휴일일 경우 isHoliday를 true로 설정하면 됨.
            if (dateTime.DayOfWeek == DayOfWeek.Saturday || dateTime.DayOfWeek == DayOfWeek.Sunday) isHoliday = true;
            return isHoliday;
        }
        set { isHoliday = value; }
    }

    /// <summary>
    /// 년과 월사이의 구분 기호를 가져오거나 설정한다.
    /// </summary>
    public string Pattern
    {
        get { return pattern; }
        set { pattern = value; }
    }

 

통상적인 컬럼(Column)들일 경우에는 DefaultHeader클래스를 참고해서 생성자 부분을 구현해서 처리하도록 하고, 특정 기간 동안의 Calendar 정보를 기반으로 만들어져야 하는 컬럼(Column)의 경우에는 생성자에서 특정 파라미터 값을 받아서 Header정보를 생성할 수 있도록 처리한다.

DefaultHeader 클래스에 존재하는 생성자외에 Calendar 정보를 기반으로 만들어지는 Header 정보의 생성자만 살펴보기로 하자.

파라미터 값으로는 DateTime과 주차 정보인 DayOfWeek 값을 받아서 처리한다.

    public YearMonthWeekNoDayHeader(DateTime dt, DayOfWeek dayOfWeek)
    {
        this.ColumnId = dt.ToString("yyyyMMdd");
        int day = dt.Day;
        string strDay = "";
        if (day < 10) strDay = "0" + day;
        else strDay = day.ToString();
        this.Title = strDay;  //Title 값으로 날짜를 2자리 형태로 출력. (01, 02 ... 11, 12 등)
        this.Width = 10; //폭(Width : 10으로 지정)
        this.HeadAlign = HorizontalAlignment.Center; //헤더 텍스트 정렬 - 중앙
        this.TextAlign = HorizontalAlignment.Center; //컨텐츠 텍스트 정렬 - 중앙
        this.Visible = true;
        this.dateTime = dt; //dt 정보를 저장
        this.weekNumber = DateUtil.GetWeekNumber(dt, dayOfWeek); //주차 정보
        string strMonth = "";
        if (dt.Month < 10) strMonth = "0" + dt.Month;
        else strMonth = dt.Month.ToString();
        this.yearMonth = dt.Year + pattern + strMonth; //년월 정보 사이에 pattern 추가
        this.Editable = false; //Cell 내용 편집 불가
        this.IsDate = true;  //Calendar 유형 셀이기에 isDate 값을 true로 설정
    }  

입력 받은 시작일자와 종료일자를 바탕으로 Calendar 기반의 일자 컬럼을 생성하는 생성자를 살펴 보았다.

 

여기까지는 일반 컬럼과 Calendar 타입의 컬럼들의 Header 정보를 생성하는 클래스들 중심으로 살펴 보았다.

다음에는 생성된 일반 컬럼과 Calendar 타입의 컬럼들의 정보로 실제 WANIGrid의 모습을 그리기 위해 Header영역을 그리는 클래스인 YearMonthWeekNoDayHeaderGenerator클래스와 함께 Body 영역을 그리는 부분을 같이 살펴보기로 하자.

 

 

반응형
반응형

보통 데이터베이스에서 가져온 데이터를 Grid Control에 보여주게 된다.

WANIGrid Control의 DataSource Property에 DataTable을 할당할 수 있도록 기능을 추가하도록 하겠다.

Oracle 또는 MS SQL 등에서 쿼리나 프로시저 등을 이용해서 데이터를 가져올 때 DataTable 또는 DataSet으로 결과 값을 할당해서 반환하게 된다.

데이터베이스에서 가져온 값, 즉 DataTable을 바로 WANIGrid Control의 Property인 DataSource에 할당함으로써 DB에서 가져온 값을 WANIGrid Control에 Display할 수 있게 된다.

 

먼저 Header.cs 파일에서 컬럼(Column)에 대한 수정 여부를 설정할 수 있는 변수와 Property를 추가하도록 한다.

    private bool editable = true;   //컬럼 편집여부

      :

    public bool Editable
    {
        get { return editable; }
        set { editable = value; }
    }

 

그리고 DefaultHeader.cs 파일에 Editable 값을 파라미터 받는 생성자를 하나 더 추가하도록 한다.

    public DefaultHeader(string fieldName, string title, int width, HorizontalAlignment headAlign, HorizontalAlignment txtAlign, 

                               bool visible, bool editable)
    {
        this.ColumnId = fieldName;
        this.Title = title;
        this.Width = width;
        this.HeadAlign = headAlign;
        this.TextAlign = txtAlign;
        this.Visible = visible;
        this.Editable = editable;
    }

 

WANIGrid.Method.cs 파일을 열어서 기능 추가 및 보완을 하도록 한다.

먼저 앞서 추가한 Editable Property의 값에 따라 특정 컬럼(Column)의 값을 변경할 수 있게 또는 변경할 수 없도록 체크 로직을 BeginEdit 메소드에 추가하도록 한다.

아래의 파란색 부분이 새롭게 추가된 영역이다.

    private void BeginEdit()
    {
        if (readOnly) return;
        if (grid.GridHeaderList[ActiveCell.Col].Editable == false) return;
        if (ActiveCell.Col != -1 && ActiveCell.Row != -1)
        {
            string tempStr = "";
            if (rows[ActiveCell.Row].DataRow[grid.GridHeaderList[ActiveCell.Col].ColumnId] != null)
            {
                tempStr = rows[ActiveCell.Row].DataRow[grid.GridHeaderList[ActiveCell.Col].ColumnId].ToString();
            }
            //TextBox에 입력된 값을 설정하고 TextBox 속성의 값을 설정한다.
            editBox.Text = tempStr;
            Rectangle r = GetSelectedCellRect(ActiveCell.Row, ActiveCell.Col);
            editBox.Left = r.Left + 3;
            editBox.Top = r.Top + 3;
            editBox.Height = r.Height;
            editBox.Width = r.Width - 3;
            editBox.Visible = true;
            editBox.TextAlign = grid.GridHeaderList[ActiveCell.Col].TextAlign;  //컬럼의 정렬형태에 따라 Text를 정렬
            editBox.Focus();
            ActiveCell_ActvieCol = ActiveCell.Col; //ActivieCell_ActiveCol 값을 설정   
        }
    }

 

DataBase에서 가져온 DataTable을 WANIGrid Control의 DataSource Property에 설정하는 메소드를 추가한다.

    public void SetDataTable(DataTable dt)
    {
        if (dt == null) return;
        if (dataSource.Rows.Count > 0)
        {             
            rows.Clear();
            allRowsHeight = 0;
        }
        int count = 0;
        foreach (DataRow row in dt.Rows)
        {
            Row r = new Row(row);
            rows.Insert(count, r);
            if (rowHeight == 0) rowHeight = Font.Height + 4;
            allRowsHeight += rowHeight;
            count++;
        }
        Invalidate();
    }

 

WINIGrid.cs파일을 열어서 SetDataTable 메소드를 호출해야 하는 DataSource Propery의 set 메소드에 아래와 같이 코드를 추가하도록 한다.

    public DataTable DataSource
    {
        get { return dataSource; }
        set {                
            dataSource = value;
            SetDataTable(dataSource);
        }
    }

 

DataTable 가져오기 기능을 통해서 WANIGrid Control에 실제 값들이 보여지도록 하고 보니 세로 스크롤바의 로직을 추가로 보완하게 되었다.

WANIGrid.Event.cs파일을 열어서 VScrollBar_Scroll 메소드를 아래와 같이 변경한다.

아래의 파란색 부분이 변경된 영역이다.

    private void VScrollBar_Scroll(object sender, ScrollEventArgs e)
    {
        EndEdit();
        firstVisibleRow = e.NewValue / rowHeight;            

        if (firstVisibleRow > (allRowsHeight / rowHeight) - (Height / rowHeight) + 5)
        {
            firstVisibleRow = (allRowsHeight / rowHeight) - (Height / rowHeight) + 5;
            grid.FirstVisibleRow = firstVisibleRow;
            vScrollBar.Value = vScrollBar.Maximum;
        }
        else
        {
            grid.FirstVisibleRow = firstVisibleRow;
            vScrollBar.Value = firstVisibleRow * rowHeight;                
        }

        CalcVisibleRange();
        ReCalcScrollBars();
        Invalidate();
    }

 

추가로 Mouse_Wheel 메소드 또한 아래의 파란색 부분과 같이 변경하도록 한다

    private void Mouse_Wheel(object sender, MouseEventArgs e)
    {
        EndEdit();
        //Control Key를 누르고 Wheel을 돌렸을 경우는 HScrollBar와 동일
        if (ModifierKeys == Keys.Control)
        {
            if ((e.Delta / 120) > 0)  //업의 경우에는 좌측으로 이동
            {
                firstVisibleCol -= 2;
                if (firstVisibleCol < 0) firstVisibleCol = 0;
                grid.FirstVisibleCol = firstVisibleCol;
                hScrollBar.Value = firstVisibleCol;
            }
            else //다운의 경우에는 우측으로 이동
            {
                firstVisibleCol += 2;
                if (firstVisibleCol >= (grid.GridHeaderList.Count - 1)) firstVisibleCol = grid.GridHeaderList.Count - 3;
                grid.FirstVisibleCol = firstVisibleCol;
                hScrollBar.Value = hScrollBar.Maximum;
            }
        }
        else //Wheel만 움직였을 경우에는 VScrollBar와 동일
        {
            if (firstVisibleRow < 0) return;

            if ((e.Delta / 120) > 0) //업의 경우 위쪽으로 이동
            {
                firstVisibleRow -= 2;
                if (firstVisibleRow < 0)
                {
                    firstVisibleRow = 0;
                    grid.FirstVisibleRow = firstVisibleRow;
                    vScrollBar.Value = 0;
                }
                else
                {
                    grid.FirstVisibleRow = firstVisibleRow;
                    vScrollBar.Value = firstVisibleRow * rowHeight;
                }
            }
            else //다운의 경우에는 아래쪽으로 이동
            {
                if (rowsCount == 0) return; // rowsCount가 0일 경우는 Row의 Height가 Control Height를 넘지 않았음
                firstVisibleRow += 2;
                if (firstVisibleRow > (allRowsHeight / rowHeight) - (Height / rowHeight) + 5)
                {
                    firstVisibleRow = rowsCount - (Height / rowHeight) + 5;
                    grid.FirstVisibleRow = firstVisibleRow;
                    vScrollBar.Value = vScrollBar.Maximum;
                }
                else
                {
                    grid.FirstVisibleRow = firstVisibleRow;
                    vScrollBar.Value = firstVisibleRow * rowHeight;
                }
            }
        }
        CalcVisibleRange();
        ReCalcScrollBars();
        Invalidate();
    }

 

WANIGridTest 프로젝트의 WANIGridTest.cs 파일을 열어서 DataTable에 데이터를 생성하기 위한 makeDataTable() 메소드를 추가하고, [DataTable 가져오기] 버튼을 추가한다.

WANIGridTest.cs 의 소스는 아래에 첨부한 소스를 참조하도록 하자.

[DataTable 가져오기] 버튼을 클릭하면 아래와 같이 WANIGrid Control에 데이터를 보여주게 된다.

[그림 21-1] DataTable 가져오기

지금까지의 소스는 아래의 파일을 참조하자.

WANI Grid_20190916.zip
0.49MB

 

반응형
반응형

지금까지의 작업으로 WANIGrid의 겉모습을 어느 정도 완성을 했다.

WANIGridTest 프로젝트의 WinForm인 WANIGridTest.cs에서 WANIGrid를 구성하는 기본 정보들을 셋팅해서 WANIGrid의 내용을 그려주고 직접 값을 입력/수정 및 행 추가/삭제 등의 작업을 진행할 수 있게 되었다.

그러나 WANIGridTest 프로젝트 내의 WinForm파일 인 WANIGridTest.cs 파일을 더블 클릭해서 디자인 화면을 열어보면 아래와 같은 오류 메시지가 나타나며 보기에 영 꺼림칙한 모습이다.

[그림 20-1] WinForm에 포함된 WANIGrid Control에서 발생한 오류

위 오류 메시지를 자세히 보면 WANIGrid의 외형을 그리는 메소드 중 DrawBackground 메소드에서 개체가 생성되지 않아서 발생하는 오류임을 알 수 있다.

이 오류를 없애기 위해서는 메시지를 확인하고 해당 파일의 오류 부분을 소스에서 찾아서 확인을 해야한다.

 

WANIGrid.Method.cs 파일 내의 273행에서 오류가 발생했으며, GetLastFixedCol() 메소드를 호출하면서 "개체 참조가 개체의 인스턴스로 설정되지 않았습니다"라는 오류 메세지를 내뱉게 된 것이다.

    private void DrawBackground(Graphics g, Rectangle rc)
    {
        g.DrawRectangle(new Pen(SystemColors.Control, 2), 0, 0, rc.Width, rc.Height);
        rc.Inflate(-1, -1);
        g.FillRectangle(new SolidBrush(SystemColors.ControlLightLight), rc);

        int lastFixedCol = GetLastFixedCol(); //오류가 발생한 부분

        if (lastFixedCol == -1)

        {

            DrawDefaultWANIGridControl(g, rc);

            return;
        }


        //선택된 컬럼의 Background를 그린다.
        SelectedColChangeBackground(g, lastFixedCol);

        //선택된 행(Row)의 Background를 그린다.
        SelectedRowChangeBackground(g, lastFixedCol);
    }

 

여기서 호출하는 GetLastFixedCol 메소드를 먼저 손보기로 하자.

Header 정보가 없을 경우에는 -1 값을 리턴하도록 아래의 파란색 부분을 추가한다.

    private int GetLastFixedCol()
    {
        if (grid.GridHeaderList == null) return -1;
        int lastFixedCol = 0;
        int startIndex = 0;
        foreach (Header head in grid.GridHeaderList)
        {
            if (startIndex == colFixed) break;
            if (head.Visible)
            {
                lastFixedCol = head.Index;
                startIndex++;                    
            }                                               
        }
        return lastFixedCol;
    }

 

그리고 위의 DrawBackground 메소드에서 파란색으로 표기된 부분의 코드는 WANIGrid Header영역이 정의되지 않았을 경우 즉 WinForm 디자인 모드에서 WANIGrid를 가져다 놓았을 때 사용자에게 WANIGrid의 모습을 그려주는 메소드인 DrawDefaultWANIGridControl 메소드를 호출하고 return하는 것이다.

DrawDefaultWANIGridControl 메소드가 하는 일은 비어있는 Data Grid 형태의 모습을 그려주는 것 뿐이다.

이렇게 함으로써 WinForm 디자인 시에 WANIGrid Control의 모습을 사용자가 직접 볼 수 있게 되는 것이다.

WANIGrid Control 폭과 높이에 맞는 빈 Data Grid를 그리는 메소드를 살펴보자

    private void DrawDefaultWANIGridControl(Graphics g, Rectangle rc)
    {
        //WANIGrid Control의 폭과 높이에 해당하는 row와 column의 갯수를 구한다. (컬럼 폭은 80으로 고정)
        int cols = (rc.Width / 80) + 1;
        int rows = (rc.Height / rowHeight) + 1;

        SolidBrush brush = new SolidBrush(SystemColors.ControlLight);
        Pen pen = new Pen(Color.LightGray);

 

        //헤더의 맨 왼쪽의 고정 공간을 그리는 부분
        int columnStartX = 0;
        g.FillRectangle(brush, columnStartX + 1, 1, leftHeaderWidth, topHeaderHeight);
        g.DrawRectangle(pen, columnStartX + 1, 1, leftHeaderWidth, topHeaderHeight);

        //헤더 영역을 그린다.
        columnStartX += leftHeaderWidth;  //첫 시작컬럼의 폭을 leftHeaderWidth 만큼 설정 
        for (int i = 0; i < cols; i++)
        {                
            int headerWidth = 80;   //i 번째 컬럼의 폭을 설정                                                    

            //Grid Header를 그린다.
            DrawUtil.DrawGridHeaderRectangleAndText(g, brush, blackBrush, pen, null, headerFont, i, columnStartX, 

                                                                      headerWidth, topHeaderHeight);

            columnStartX += headerWidth;
        }
        vScrollBar.Visible = true; 

        //Body 부분을 그린다.
        for (int i = 0; i < rows; i++)
        {
            columnStartX = 0;
            g.FillRectangle(brush, columnStartX + 1, i * rowHeight, leftHeaderWidth, topHeaderHeight);
            g.DrawRectangle(pen, columnStartX + 1, i * rowHeight, leftHeaderWidth, topHeaderHeight);
            columnStartX += leftHeaderWidth;

            for (int j = 0; j < cols; j++)
            {                    
                int colWidth = 80;
                                               
                g.DrawRectangle(pen, columnStartX + 1, i * rowHeight, colWidth, rowHeight);
                Rectangle rec = new Rectangle(columnStartX + 2, (i * rowHeight) + 2, columnStartX - 2, rowHeight);                        

                columnStartX += colWidth;                    
            }
        }
        hScrollBar.Visible = true;
    }

 

더불어 SelectedColChangeBackground메소드의 오류가 발생할 수 있는 부분을 미리 처리하자.

선택된 컬럼의 Backgroun를 그리는 메소드인데 선택된 컬럼이 없을 경우에는 아무런 동작을 취하지 않도록 아래의 파란색으로 표기된 부분을 추가한다.

    private void SelectedColChangeBackground(Graphics g, int lastFixedCol)
    {
        if (selectedCols == null) return;
        //선택된 컬럼의 Background를 그린다.

         :

         :

    }

여기까지 작업을 하면 디자인 시에 아래의 모습으로 보여지게 된다.

[그림 20-2] 디자인 시 WANIGrid Control의 모습

 

WANIGrid Control를 도구 상자에 추가했을 때 아이콘 모양이 나타나도록 설정을 해보자

아미콘의 사이즈는 16X16이며, 아이콘 이미지를 WANIGrid Project의 루트에 생성한다.

WANIGrid.cs 파일을 열어서 WANIGrid 클래스 정의 상단에 아래의 내용을 추가한다.

typeof 부분의 WANIGrid는 내가 만든 User Control의 명칭인 WANIGrid, 다음은 아이콘 파일의 명칭을 추가한다.

WANIGrid.ico파일에서 확장자를 제외한 명칭만 표기하면 된다.

    [ToolboxBitmap(typeof(WANIGrid), "WANIGrid")]
    public partial class WANIGrid : UserControl
    { 

        :

        :

    }

아래의 그림에서 처럼 WANIGrid.ico파일을 선택해서 속성 정보 중 [빌드 작업] 항목의 값을 "포함 리소스"로 변경하고 저장한다.

 

이렇게 하게 되면 도구 상자에 추가한 WANIGrid Control의 아이콘이 보여지게 된다.

만약 아이콘이 아래의 그림처럼 나타나지 않는다면 WANIGrid를 선택해서 마우스 우측 버튼 클릭 후 나타나는 Context 메뉴 중 삭제를 선택해서 삭제한다.

컨트롤을 추가하고자 하는 그룹을 선택한 후 마우스 우측 버튼을 눌러서 나타나는 Context 메뉴 중 항목 선택을 선택해서 WANIGrid.dll파일을 찾아서 선택하면 된다.

[그림 20-4] WANIGrid Control 아이콘

지금까지의 소스는 아래의 파일을 다운받아서 확인하면 된다.

WANI Grid_20190907.zip
0.48MB

반응형

+ Recent posts