반응형

WANIGrid에서 행(Row) 추가, 행(Row)과 컬럼(Column) 선택 등에 대한 처리를 했다. 이제 WANIGrid Control의 실제 보여지는 부분의 최종이라고 할 수 있는 Cell선택 및 데이터 입력 처리를 위한 선택 Cell의 TextBox 입력을 할 수 있도록 구현해 보고자 한다.

 

[그림 13-1] Cell 선택 시
[그림 13-2] Cell 선택 후 TextBox 제공

 

데이터를 입력하기 위한 Cell선택 시 선택 Cell임을 표현하고, 같은 Cell을 두 번 이상 클릭해서 데이터를 입력할 수 있는 TextBox를 제공하는 것이다.

이러한 기능을 구현하기 위해서 먼저 Cell 클래스를 보자.

Cell 클래스에는 행(Row)과 열(Column) 값을 관리하기 위한 클래스 이다.

public class Cell
{
    private int row;
    private int col;

    public int Row
    {
        get { return row; }
        set { row = value; }
    }

    public int Col
    {
        get { return col; }
        set { col = value; }
    }

    public Cell(int r, int c)
    {
        row = r;
        col = c;
    }

    public void Clear()
    {
        row = -1;
        col = -1;
    }
}

 

이렇게 새로 정의한 Cell Class와 Cell 선택 시 Cell이 선택되었음을 보여주고 데이터를 입력할 수 있는 TextBox를 제공하기 위한 변수를 아래와 같이 WANIGrid.cs에 변수 선언 부분에 선언 및 정의한다.

private Cell ActiveCell = new Cell(-1, -1);
private int ActiveCell_ActiveRow = 0;   //Active Cell의 Row
private bool readOnly = false; //ReadOnly 여부
private TextBox editBox = null;   //데이터 입력을 위한 TextBox 선언
private GridState gridState = GridState.NONE;   //Grid 상태를 저장하기 위한 열거형 변수

 

WANIGrid.cs 파일의 상단에 선택된 Cell의 상태를 나열한 열거형 변수를 아래와 같이 선언한다.

public enum GridState
{
    NONE,
    ACTIVE,
    EDIT
}

 

WANIGrid.cs 내의 생성자 WANIGrid에 TextBox를 생성하기 위한 구문을 추가한다. 아래의 소스 코드 중 editBox 속성 값을 할당하는 부분과 Control Collection에 Add하는 부분을 기존 소스에 추가한다.

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();    //TextBox 생성
    editBox.BorderStyle = BorderStyle.None;   //TextBox의 테두리 제거
    editBox.Font = Font;  //TextBox 폰트 선언
    editBox.Visible = false; 
    Controls.Add(editBox);
    //마우스 우측 버튼 클릭 시 제공되는 ContextMenu 초기화
    InitializeContextMenu();
}

 

선택한 Cell의 Rectangle 값을 반환하는 메소드를 추가한다. 여기서 구해진 Rectangle을 이용해서 WANIGrid Control 내의 선택 Cell의 표시 영역을 그릴 수 있게 된다.

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++)
    {
        height = rows[i].MaxLines * rowHeight;
        if (row == i) break;
        top += height;
    }

    //선택된 Cell의 폭을 구한다.
    int left = leftHeaderWidth + 2;
    int width = 0;
    for (int i = firstVisibleCol; i <= lastVisibleCol; i++)
    {
        //보여지는 컬럼의 폭이 컨트롤의 폭 보다 클경우
        if (left + grid.GridHeaderList[i].Width > this.Width)
        {
            width = this.Width - left - 1;
        }
        else
        {
            width = grid.GridHeaderList[i].Width;
        }

        if (col == i) break;
        left += width;
    }
    //선택된 Cell의 높이와 폭을 구해서 Rectangle을 만들어서 반환
    return new Rectangle(left - 1, top + 1, width - 1, height - 1);
}

 

이렇게 구해진 Rectangle을 이용해서 직접 WANIGrid Control 내에서 사각형을 그리는 메소드를 작성한다. Active Cell을 그리는 메소드인 DrawActiveCell를 아래와 같이 만든다.

private void DrawActiveCell(Graphics g)
{
    if (ActiveCell.Row != -1 && ActiveCell.Col != -1)
    {

        //선택된 Cell 영역을 그린다
        g.DrawRectangle(new Pen(Color.FromName("HotTrack"), 1), GetSelectedCellRect(ActiveCell.Row, ActiveCell.Col));
    }
}

 

DrawActiveCell 메소드를 OnPaint 영역에 추가하도록 한다. OnPaint 메소드 영역에 추가해야 WANIGrid Control 내에서 Invalidate()메소드가 호출 될 때 바로 그려져서 사용자에게 보여지게 된다.

private void WANIGrid_Paint(object sender, PaintEventArgs e)
{
    CalcVisibleRange();
    ReCalcScrollBars();
    DrawBackground(e.Graphics, rc);
    DrawHeaders(e.Graphics, rc);
    DrawContent(e.Graphics, rc, this.ClientRectangle.Width);
    DrawActiveCell(e.Graphics); //DrawActiveCell 메소드 호출 추가 
}

이렇게 해서 Cell 이 선택될 때 선택된 Cell 영역 내의 사각형을 그리는 부분을 완성했다.

이번에는 같은 Cell을 두 번 이상 선택했을 경우, 선택 Cell 영역에 데이터를 입력할 수 있는 TextBox를 제공하기 위한 메소드인 BeginEdit를 작성한다. BeginEdit 메소드는 같은 Cell 영역을 2번 선택했을 때 TextBox를 선택한 Cell 영역 내에서 TextBox를 제공해서 사용자 입력을 받을 수 있도록 제공하는 것이다.

private void BeginEdit()
{
    if (readOnly) 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 + 5;
        editBox.Top = r.Top + 3;
        editBox.Height = r.Height;
        editBox.Width = r.Width - 7;
        editBox.Visible = true;
        editBox.Focus();                
    }
}

 

가로/세로 스크롤을 움직이거나 마우스 휠 값이 변경되었을 때, 제공된 TextBox를 제거하도록 하는 EndEdit 메소드를 살펴보자. 어떠한 이유로든 편집이 끝나면 EndEdit 메소드가 호출되어 editBox에 입력된 값을 실제 데이터 저장 공간에 반영하고 editBox의 Visible을 false로 해서 제거된 효과를 제공한다.

private void EndEdit()
{
    if (readOnly) return;
    if (ActiveCell.Col != -1 && ActiveCell.Row != -1 && editBox.Visible)
    {                

        //editBox에 입력 된 값을 실제 데이터 저장 공간에 반영
        rows[ActiveCell.Row].DataRow[grid.GridHeaderList[ActiveCell.Col].ColumnId] = editBox.Text;
    }

    editBox.Visible = false;
    editBox.Text = "";
    gridState = GridState.ACTIVE;
}

 

가로/세로 스크롤과 마우스 휠 이벤트 발생 시에 EndEdit 메소드를 호출해서 선택 Cell에 데이터를 입력하지 못하도록 처리해야 한다.

HScrollBar_Scroll, VScrollBar_Scroll, Mouse_Wheel 메소드의 첫 라인에 EndEdit(); 를 추가하도록 한다.

 

실제 Cell 선택을 체크할 수 있는 Mouse 좌측 버튼 Down 시에 발생하는 이벤트인 MouseLeftButtonClickInContents 메소드에 Cell 선택과 TextBox 처리 부분을 추가하기로 하자.

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를 선택하도록 처리
    {
        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)를 추가
            }
        }                
    }
    else //WANIGrid Control 내부의 Content 영역을 마우스 좌측 버튼으로 클릭했을 때
    {
        selectedRows.Clear();
        selectedCols.Clear();
        //선택된 row와 col이 기존의 선택되었던 ActiveCell의 row/col 값이 같은지 체크
        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;
            }

            //현재의 Row가 ActiveCell_ActiveRow와 같지 않으면 EndEdit 메소드 호출
            if (ActiveCell_ActiveRow != k) EndEdit();
            ActiveCell_ActiveRow = k;
            BeginEdit();
        }
        else
        {
            ActiveCell.Row = row;
            ActiveCell.Col = col;
            EndEdit();
        }
    }
    Invalidate();
}

 

이렇게 해서 WANIGrid Control에서 Cell 선택 시의 사각형 영역과 데이터 입력을 위한 TextBox 제공 기능까지를 구현해 보았다.

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

WANI Grid_20190730.zip
0.37MB

 

 

 

반응형

+ Recent posts