반응형

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 메소드는 아래와 같다.

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이면 처리하지 않음

            //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, 

                                 allRowsHeight - (rowHeight * firstVisibleRow) + 1);
        }
    }

 

이렇게 구현한 WANIGrid Control이 실행되었을 때 화면이다.

[그림 11-1] 1개의 컬럼을 선택했을 경우
[그림 11-2] 컨트롤 키를 누른 상태에서 여러 컬럼을 선택했을 경우

WANIGrid Control의 모습이 전보다는 좀 더 진화된 상태가 되었다.

다음에는 Row 선택 기능을 추가해 보기로 하자. 

 

지금까지의 소스는 아래를 참고하면 된다.

WANI Grid_20190725.zip
0.35MB

반응형
반응형

지금까지의 작업으로 Grid Control의 외적인 모습들을 개략적으로 만들어 왔다.

오늘은 Grid Control에 남아 있는 세로 스크롤바 이벤트 처리와 마우스 휠 이벤트 처리를 구현해서 Grid Control에서 정보를 Viewing하기 위한 일반적인 방법들을 구현하고자 한다.

 

저자가 작성한 소스를 본 사람들은 알겠지만 다소 엉성한 부분들이 꽤 있을 수 있다. 하나 하나의 기능을 만들어가면서 내용을 정리하고 있는 수준이다 보니 많은 부분이 깔끔하지 못할 수 있지만 나 처럼 누군가가 만들어 놓은 Grid Control을 사용만 해오다가 직접 내가 이러한 Control을 만들어 보면서 하나씩 배우고 익히는 부분이기에 더 그럴 수 있을 것이다. 

저자가 구현한 부분에 보다 좋은 아이디어나 방법이 있다면 댓글로 많은 조언과 도움을 주시길 희망하며...

 

[그림 10-1] 행 추가를 통해서 세로 스크롤바 및 마우스 휠 사용

세로 스크롤바의 경우에는 행(Row)의 맨 처음과 마지막 까지 스크롤바의 움직임에 따라 행(Row)을 이동하면서 행의 컬럼들을 보여주게 된다.

WANIGrid Control 내에서 세로스크롤바가 처음 나타났을 때의 행(Row) 개수를 파악하고 저장하기 위한 rowsCount 변수를 선언한다.

private int rowsCount = 0;  //Grid Control에서 보여지는 행의 갯수를 저장하기 위한 변수

 

WANIGrid Control의 사이즈가 변경될 때, Control 내의 표기할 수 있는 행(Row)의 개수가 변경될 수 있기에 rowsCount 변수를 초기화 하는 부분을 추가한다.

HScrollBar/VScrollBar를 초기화 하는 메소드에 rowsCount를 초기화한다.

public void InitializeScollBar()
{
    rowsCount = 0; //Control 크기가 변경되었을 경우 행(Row) 개수 초기화

    //가로 스크롤바 설정
    hScrollBar.Left = 1;
    hScrollBar.Width = Width - vScrollBar.Width - 2;
    hScrollBar.Top = Height - hScrollBar.Height - 2;
        
    //세로 스크롤바 설정
    vScrollBar.Left = Width - vScrollBar.Width - 2;
    vScrollBar.Top = topHeaderHeight + 2;
    vScrollBar.Height = Height - topHeaderHeight - hScrollBar.Height - 4;        
}

 

WANIGrid Control의 폭/높이를 기준으로 가로/세로 스크롤바를 보여줄지를 결정하는 메소드인 ReCalcScrollBar에서 rowsCount 변수에 Row개수를 설정하는 부분을 추가한다.

WANIGrid Control에서 세로 스크롤바가 나타나기 전의 Row수를 설정하는 부분을 추가해서 세로 스크롤바 또는 마우스 휠 이벤트 처리 시에 사용할 수 있도록 한다.

private void ReCalcScrollBars()
{
    if (hScrollBar == null || vScrollBar == null) return;
    //컬럼의 전체 폭
    if (grid != null && grid.GridHeaderList != null) 

        allColsWidth = grid.GridHeaderList[grid.GridHeaderList.Count() - 1].Left + grid.GridHeaderList[grid.GridHeaderList.Count() - 1].Width;

    //컬럼의 폭이 클라이언트 사이즈 폭 보다 클 경우 가로 스크롤바를 보여준다.
    if ((allColsWidth > 0) && (allColsWidth > ClientSize.Width - ysclWidth))
    {
        hScrollBar.Visible = true;
        hScrollBar.LargeChange = ((lastVisibleCol - firstVisibleCol) + 1) / 2 + 1;
        lastHScrollValue = ((lastVisibleCol - firstVisibleCol) + 1) / 2 + 1;
    }
    else
    {
        hScrollBar.Visible = false;
        grid.FirstVisibleCol = 0; //Control 크기가 바뀌면서 hScrollBar가 가려지면 Grid의 첫번째 컬럼 부터 그려지도록 처리
    }
    //로우의 높이가 클라이언트 사이즈 높이 보다 클 경우 세로 스크롤바를 보여준다.
    if (allRowsHeight > 0 && (allRowsHeight > Height - topHeaderHeight - xsclHeight))
    {
        vScrollBar.Visible = true;
        if (rowsCount == 0) rowsCount = (allRowsHeight / rowHeight) - 1; //WANIGrid Control에서 세로 스크롤바가 나타나기 전의 Row 개수 설정
        vScrollBar.Maximum = allRowsHeight;
        vScrollBar.LargeChange = rowHeight * 5;
        vScrollBar.SmallChange = rowHeight;
    }
    else
    {
        vScrollBar.Visible = false;
        grid.FirstVisibleRow = 0;
    }
}

 

세로 스크롤바를 움직였을 때 발생하는 이벤트

private void VScrollBar_Scroll(object sender, ScrollEventArgs e)
{
    firstVisibleRow = e.NewValue / rowHeight; //입력되는 세로 스크롤바의 값을 Row 위치로 변환

    if (firstVisibleRow > (allRowsHeight / rowHeight)) return;
    if (firstVisibleRow >= (allRowsHeight / rowHeight) - rowsCount)
    {
        firstVisibleRow = (allRowsHeight / rowHeight) - rowsCount;
        grid.FirstVisibleRow = firstVisibleRow;
        vScrollBar.Value = vScrollBar.Maximum;                
    }
    else
    {
        grid.FirstVisibleRow = firstVisibleRow;
        vScrollBar.Value = firstVisibleRow * rowHeight;
    }
                        
    CalcVisibleRange();
    ReCalcScrollBars();
    Invalidate();
}

 

마우스 휠 처리는 컨트롤 키를 누르고 휠을 움직였을 때와 그렇지 않고 휠만 움직였을 떄로 구분해서 처리한다.

컨트롤 키를 누른 상태에서 휠을 위/아래로 움직이면 가로 스크롤바를 움직인 효과가 나도록 했고, 마우스 휠만 위/아래로 움직였을 경우에는 세로 스크롤바를 움직인 효과가 나도록 처리했다.

마우스 휠의 경우 위로 굴리면 양수 값이 아래로 굴리면 음수 값이 나오게 된다. 

컨트롤 키를 누른 상태에서 마우스 휠을 위로 굴리면 가로 스크롤의 좌측으로, 아래로 굴리면 우측으로 움직이는 효과가 나도록 처리했다.

마우스 휠을 위로 굴리면 세로 스크롤의 위쪽으로, 아래로 굴리면 세로 스크롤의 아래쪽으로 움직인 효과가 나도록 처리했다.

private void Mouse_Wheel(object sender, MouseEventArgs e)
{
    //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) - rowsCount)
            {
                firstVisibleRow = (allRowsHeight / rowHeight) - rowsCount;
                grid.FirstVisibleRow = firstVisibleRow;
                vScrollBar.Value = vScrollBar.Maximum;
            }
            else
            {
                grid.FirstVisibleRow = firstVisibleRow;
                vScrollBar.Value = firstVisibleRow * rowHeight;
            }
        }
    }
    CalcVisibleRange();
    ReCalcScrollBars();
    Invalidate();
}

 

이렇게 해서 지금까지는 Grid Header를 만들고, Row를 추가하고 가로/세로 스크롤바 및 마우스 휠 이벤트 처리까지 구현을 해 보았다.

 

추가 한 행(Row)에 직접 데이터를 입력하고 보여주는 부분을 구현해 나가기에 앞서 WANIGrid Control 내에서 마우스로 클릭한 부분의 좌표를 Grid 내의 Cell 선택 영역으로 표시하기 위한 기능을 구현할 생각이다.

사용자가 WANIGrid Control 내의 Header 영역을 제외한 부분을 클릭하면 클릭 된 영역이 어느 행과 컬럼인지를 식별하기 위한 메소드를 만들어 볼 것이다.

부족하지만 Grid Control에 필요한 기능들을 고민과 학습을 통해서 하나씩 하나씩 천천히 구현해 나가볼 생각이다.

지금까지의 소스는 아래와 같다.

WANI Grid_20190721.zip
0.33MB

저자의 부족한 소스를 보고 조언 및 더 나은 방법이 있다면 기탄없이 댓글을 달아주시길 희망해 본다.

여러분들이 보내주는 의견들이 곧 WANIGrid Control의 기능을 보다 편리하고 고도화해 나갈 수 있는 방법이라 생각한다.

 

 

반응형
반응형

앞 시간에 Grid Header를 그렸고 이제는 가로 스크롤바에 대한 이벤트를 생성해보기로 하자.

Column이 많아지면 가로 스크롤바가 나타나고 스크롤 이벤트에 따라 컬럼들을 이동해서 보여주어야 한다.

이러한 작업을 하기 위해서 먼저 DefaultHeaderGenerator Class의 DrawHeaders 메소드를 살펴 보기로 하자.

이전 소스에서는 첫번째 컬럼부터 마지막 컬럼까지 Header를 보여주기 위한 목적으로 DrawHeaders 메소드를 간단하게 작성했었다.

이제는 화면에 보여져야 할 첫번째 컬럼과 마지막 컬럼을 기준으로 Grid Header정보를 그려야 한다.

이전 소스의 변경 부분은 아래와 같다.

[HeaderGenerator.cs]

Header의 첫번째 컬럼에서 마지막 컬럼까지의 폭이 Client 폭 보다 클 경우에 true, 그렇지 않을 경우에 false를 설정하기 위한 변수 선언

protected bool isLargeLastCol = false;    //첫번째 컬럼에서 마지막 컬럼 까지의 폭이 Client 폭 보다 클 경우 true

 

외부에서 get/set 할 수 있도록 Property 선언

public bool IsLargeLastCol
{
    get { return isLargeLastCol; }
    set { isLargeLastCol = value; }
}

 

[DefaultHeaderGenerator.cs]

public override void DrawHeaders(int firstVisibleCol, int lastVisibleCol, int controlWidth, Graphics graphics, Rectangle rect)
{
    SolidBrush brush = new SolidBrush(SystemColors.Control);
    Pen pen = new Pen(SystemColors.ControlLight);

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

    for (int i = firstVisibleCol; i <= lastVisibleCol; i++)
    {
        if (i == firstVisibleCol) columnStartX += leftHeaderWidth;  //첫 시작컬럼의 폭을 leftHeaderWidth 만큼 설정
        int headerWidth = this._headers[i].Width;   //i 번째 컬럼의 폭을 설정

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

        //헤더영역의 사각형을 채우고 테두리를 그린다.
        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);
        graphics.DrawString(this._headers[i].Title, headerFont, blackBrush, colRec, sf);

         columnStartX += headerWidth;
    }
}

 

[WANIGrid.cs]

가로 스크롤바를 움직였을 때 동작하기 위한 변수를 추가로 선언한다.

private int allColsWidth = 0;   //Grid 컬럼 전체의 폭
private int lastHScrollValue = 0; //가로 스크롤바를 최대값까지 갔을 경우 보여지는 컬럼 수를 2로 나눈 값 - 스크롤 시 이동 거리를 말함
private bool chkLast = false; //마지막 컬럼의 폭을 지정했던 폭 만큼 보여졌을 때 true

 

생성자에 hScrollBar의 최소 변경 값을 설정한다.

public WANIGrid()
{            
    InitializeComponent();
    if (grid == null) grid = new WANI_Grid.Grid.Grid();             
    hScrollBar.SmallChange = 1;
}

 

이벤트 핸들러 초기화

public void EventHandlerInitialize()
{
    hScrollBar.Scroll += new ScrollEventHandler(HScrollBar_Scroll);
}

 

가로 스크롤바를 스크롤 했을 때 발생하는 이벤트

        public void HScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            //가로 스크롤바를 움직여서 마지막 컬럼이 Client 영역에 나타났을 때
            if (e.NewValue >= (grid.GridHeaderList.Count - lastHScrollValue))
            {
                //마지막 컬럼의 전체가 Client 영역에 모두 나타나지 않았을 때
                if (grid.HeaderGen.IsLargeLastCol)
                {
                    //마지막 컬럼을 1칸 앞으로 땡긴다.
                    if (grid.LastVisibleCol >= grid.GridHeaderList.Count - 1)
                    {
                        if (!chkLast)
                        {
                            firstVisibleCol += 1;
                            grid.FirstVisibleCol = firstVisibleCol;
                            e.NewValue = firstVisibleCol;
                            chkLast = true;
                        }
                    }
                }
                else
                {
                    if (e.NewValue <= grid.GridHeaderList.Count - lastHScrollValue)
                    {
                        firstVisibleCol = e.NewValue;
                        grid.FirstVisibleCol = firstVisibleCol;
                        chkLast = false;
                    }
                }
            }
            else
            {
                if (e.NewValue < (grid.GridHeaderList.Count - lastHScrollValue))
                {
                    if (firstVisibleCol < grid.LastVisibleCol)
                    {
                        firstVisibleCol = e.NewValue;
                        grid.FirstVisibleCol = firstVisibleCol;
                        chkLast = false;
                    }
                }
            }
            CalcVisibleRange();
            ReCalcScrollBars();
            Invalidate();
        }

 

가로/세로 스크롤바를 재계산 하는 메소드. 세로 스크롤바 재계산 로직은 추후에 추가될 것이다.

        private void ReCalcScrollBars()
        {
            if (hScrollBar == null || vScrollBar == null) return;

            if (grid != null && grid.GridHeaderList != null) 

                allColsWidth = grid.GridHeaderList[grid.GridHeaderList.Count() - 1].Left + grid.GridHeaderList[grid.GridHeaderList.Count() - 1].Width;

 

            //컬럼의 폭이 클라이언트 사이즈 폭 보다 클 경우 가로 스크롤바를 보여준다.
            if ((allColsWidth > 0) && (allColsWidth > ClientSize.Width - ysclWidth))
            {
                hScrollBar.Visible = true;
                hScrollBar.LargeChange = ((lastVisibleCol - firstVisibleCol) + 1) / 2 + 1;
                lastHScrollValue = ((lastVisibleCol - firstVisibleCol) + 1) / 2 + 1;
            }
            else
            {
                hScrollBar.Visible = false;
                grid.FirstVisibleCol = 0; //Control 크기가 바뀌면서 hScrollBar가 가려지면 Grid의 첫번째 컬럼 부터 그려지도록 처리
            }
        }

 

WANIGrid Control Load 시에 스크롤바의 이벤트 핸들러를 초기화 한다.

        private void WANIGrid_Load(object sender, EventArgs e)
        {
            //더블버퍼링(Double Buffering) 처리
            this.SetStyle(ControlStyles.DoubleBuffer, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.UserPaint, true);

            rc = new Rectangle(0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height);
            EventHandlerInitialize(); //스크롤 이벤트 초기화
            InitializeScollBar();
            Invalidate();
        }

 

WANIGrid Control Resize 이벤트 시에 ReCalcScrollBars 추가 한다.

        private void WANIGrid_Resize(object sender, EventArgs e)
        {
            rc = new Rectangle(0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height);
            InitializeScollBar();
            ReCalcScrollBars();
            Invalidate();
        }

 

WANIGrid Control Paint 이벤트 시에 ReCalcScrollBars 추가 한다.
        private void WANIGrid_Paint(object sender, PaintEventArgs e)
        {
            CalcVisibleRange();
            ReCalcScrollBars();
            DrawBackground(e.Graphics, rc);
            DrawHeaders(e.Graphics, rc);
        }

 

WANIGrid Control SizeChanged 이벤트 시에 ReCalcScrollBars 추가 한다.
        private void WANIGrid_SizeChanged(object sender, EventArgs e)
        {
            InitializeScollBar();
            ReCalcScrollBars();
            Invalidate();
        }

 

WANIGrid Control ClientSizeChanged 이벤트 시에 ReCalcScrollBars 추가 한다.
        private void WANIGrid_ClientSizeChanged(object sender, EventArgs e)
        {
            rc = new Rectangle(0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height);
            InitializeScollBar();
            ReCalcScrollBars();
            Invalidate();
        }

 

이렇게 소스를 변경하고 실행하면 스크롤바를 움직일때 마다 컬럼이 바뀌어짐을 알 수 있다.

[그림 7-1] 실행 첫 화면
[그림 7-2] 스크롤 이벤트 발생 시 변경된 화면

이렇게 해서 WANIGrid Control의 가로 스크롤 이벤트 발생 시 Grid Header의 컬럼 정보를 볼 수 있도록 했다.

행이 추가 되기 전 전체 컬럼을 모두 살펴볼 수 있는 기능을 개발한 것이며, 오늘 까지 이렇게 만든 기능들은 추후 계속해서 변화 또는 개선이 되어질 것이다. 

그때 마다 변경된 사항들에 대해서 설명을 해 나갈 것이다.

지금까지의 소스는 아래의 첨부를 참조하기 바란다.

 

WANI Grid_20190620.zip
0.39MB

다음에는 행을 추가하기 위해 고려해야 할 사항들에 대해서 알아 보기로 하자.

 

 

반응형
반응형

Grid 전체 모습과 기능을 갖추기 위해서 Grid의 전체 기능을 총괄할 수 있는 Grid Class를 만들어 보기로 하는데 지금까지 Grid Header를 그리기 위한 작업들을 주욱 해 왔다. 따라서 Header 영역의 컬럼을 그릴 수 있는 기능을 구현해 보기로 하자.

 

Grid에 대한 모든 책임과 권한을 가지는 Class인 Grid Class를 생성해보기로 하겠다.

Grid의 전체 기능 중 Header 영역을 설정하고 그리는 기능을 추가하기 위해서 필요한 변수 부터 먼저 설정하기로 하자.

헤더를 생성하기 위한 HeaderGenerator 를 선언하고 Header의 높이와 Header 왼쪽의 빈 공간을 설정한다.

가로 스크롤바를 이용할 경우 현재 보여져야 할 화면 영역의 첫번째 Column과 마지막 Column 정보를 저장하기 위한 변수 선언, Header Title의 폰트 선언 및 설정을 한다.

private HeaderGenerator headerGen;
private int topHeaderHeight = 20;   //Grid의 Header 높이
private int leftHeaderWidth = 22; //Grid의 맨 왼쪽 빈Column Width
private int firstVisibleCol = 0;    //화면상에서 처음 보여져야할 컬럼
private int lastVisibleCol = 0;     //화면상에서 마지막에보여질 컬럼    
private Font headerFont = new Font("맑은 고딕", 9, FontStyle.Bold);
private SolidBrush blackBrush = new SolidBrush(Color.Black);

 

각 변수들을 Class 외부에서 get/set 할 수 있도록 Property 설정을 추가한다.

 

Header를 그리기 위한 DrawHeader 메소드를 만든다.

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

 

Header 영역을 그리기 위한 Grid Class의 정의는 이것으로 마무리하고, WANIGrid Control에 대해서 알아보자.

WANIGrid Control은 디자인 모드에서 개발자가 직접 가져다 사용할 수 있는 Control 이며, 실제 Grid의 제어 영역이기도 하다.

WANIGrid Control이 가장 Core이며 핵심이 되는 부분이다. 앞으로도 이 Control에 많은 기능들이 추가 될 것이다.

현재 WANIControl에서 Header영역을 그리기 위해 필요한 주요 기능들을 나열해 보자.

  • GridType에 따른 Grid 정보에 대한 Get/Set Method - 나타내고자 하는 Grid Type 설정
  • Grid의 왼쪽 빈 공간에 대한 폭 설정 - Grid 첫번째 컬럼의 폭을 기본으로 22로 설정을 했으나, 언제든 목적에 따라 폭을 변경할 수 있도록 하기 위해서 임.
  • 가로/세로 스크롤바 위치 선정을 위한 초기화 - Control이 생성 또는 변경 되는 사이즈에 따라 가로/세로 스크롤바의 위치를 초기화
  • Grid Header를 생성하기 위한 SetHeader Method - WANIControl 외부에서 Header 정보를 만들어서 설정할 수 있도록 지원
  • 현재 보여지는 Grid 영역의 계산을 담당하는 Method - 현재 보여지는 첫 컬럼/로우, 마지막 컬럼/로우를 계산
  • WANIGrid Control의 Backgroud를 그리는 DrawBackground Method - WANIGrid Control의 기본 Background 화면을 생성
  • Grid Header를 그리기 위한 DrawHeaders Method - GridType에 맞는 Grid 모습을 그린다
  • Control Event 처리 - Load/Resize/Paint/SizeChanged/ClientSizeChanged - WANIGrid Control의 변경 Event에 따른 Grid 모습을 다시 그릴 수 있도록 처리하기 위해서 이벤트 처리가 필요

생각보다 많은 기능들을 처리해야 하는 부분이라 현재는 소스가 이렇게 구성되지만 향 후에 Refactoring을 통한 많은 개선과 변화가 있을 수 있는 부분이기도 하다.

WANIGrid Control의 소스에 대한 대략적인 내용들을 살펴 보았다. 상기의 내용을 기반으로 소스를 읽어보면 한결 이해하기가 쉬울 것이다.

지금은 단순히 지정된 Grid Header와 제목만 보여주는 기능만을 가지고 있지만 앞으로 계속 진행하면서 주요 기능들을 하나씩 추가해 나갈 것이다.

좋은 아이디어나 개선사항이 있으면 언제든 주저하지 말고 댓글을 남겨주시길 바라며....

[그림 6-1] WANIGrid Control 실행 모습

간략하게 설명한 내용의 대한 소스코드는 아래의 파일을 참조하자.

WANI Grid_20190616.zip
0.28MB

 

 

 

반응형

+ Recent posts