Course D-1

자주 사용되는 컨트롤 실습

목표

  • 기존 프로젝트를 복사하여 새로운 폼을 생성해보고 XtraTabControl을 사용한 코드 작성 패턴, TreeList와 ChartControl에 데이터 바인딩을 실습합니다.

  • 학습 인정 시간: 4 시간

개발 화면

첫 번째 탭
두 번째 탭
세 번째 탭

1. 폼 생성

1.1) 기존 프로젝트 파일 복사

  • 이전 과정에서 개발했던 "공통 코드 관리(DEV002_xxx)"의 프로젝트 폴더로 이동하여 bin, obj 폴더를 제거합니다.

  • 해당 프로젝트 폴더와 Visual Studio Solution(.sln) 파일이 포함 되어있는 폴더를 복사합니다.

  • 복사된 폴더의 이름을 "DEV004_xxx"로 변경하고, 해당 폴더 내 솔루션 파일(.sln)과 ".git" 폴더를 제거한 다음 마찬가지로 프로젝트 폴더의 이름도 변경합니다.

  • 프로젝트 폴더 내 Visual C# Project File(.csproj)의 이름을 "DEV004_xxx.csproj"로 변경하고 실행하여 [VisualStudio→파일→모두 저장(Ctrl+Shift+S)]으로 "프로젝트 폴더"와 동일한 위치에 솔루션 파일(.sln)을 저장합니다.

  • 솔루션 탐색기에서 클래스 파일 이름을 "DEV004_xxx.cs"로 변경하면 해당 클래스의 모든 참조 이름을 변경하겠냐는 알림 창이 뜨고, 확인 버튼을 클릭합니다.

  • 마지막으로 [프로젝트 속성] 창에서 어셈블리 이름을 변경합니다.

1.2) 저장 프로시저 복사

  • DEV002_xxx 프로젝트의 조회, 저장용 프로시저를 열어 CREATE 문으로 수정 후 각각 "P_DEV004_xxx_Q", "P_DEV004_xxx_S"를 새로 생성합니다.

1.3) 폼 정보 등록 [GPM→개발→폼 정보]

  • [GPM Tools]로 프로젝트 파일을 생성한게 아니기 때문에 폼 정보를 다음과 같이 수동으로 등록합니다.

  • 저장 후 [VisualStudio→도구→GPM Tools]를 실행하여 등록했던 폼 정보가 정상적으로 불러와지는지 확인합니다.

2. XtraTabControl (GST.Platform.Client.Controls.TabEx)

2.1) 화면 디자인 수정

  • 디자인 탭에서 도구 상자를 통해 TabEx 컨트롤을 폼에 추가하고 아래 사진과 같이 디자인 정보를 수정합니다.

2.2) 활성화 된 탭에 따른 코드 처리 패턴 적용

  • 폼에 탭 컨트롤을 이용하여 화면을 구분하였고, GSTBrowser의 공통 버튼 기능에 대하여 활성화 된 탭에 따라 기능을 다르게 처리해야 하므로 코드를 수정합니다. ( ※ 자세한 설명은 [링크]의 내용과 Example 코드 확인 )

  • OnLoad 메서드에서 탭 페이지별 버튼의 상태를 초기화합니다.

  • 생성자에서 텝 페이지가 변경되었을 때 GSTBrowser에 버튼 상태를 변경되기 전 페이지(e.PrevPage)의 Tag 속성에 입력하고, 변경된 페이지(e.Page)의 Tag 속성에 등록된 버튼 상태 정보를 확인하여 업데이트하는 이벤트 핸들러를 등록합니다.

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);

    // 탭페이지별 버튼 상태 초기화
    tpgEdit.Tag = ProcessButtonState.Retrieve | ProcessButtonState.New | ProcessButtonState.Delete;
    tpgTreeView.Tag = ProcessButtonState.Retrieve;

    // 탭페이지(tpgProcessLayout)의 버튼 상태 적용
    UpdateProcessButtonState((ProcessButtonState)tpgEdit.Tag);

    ReadOnlyControl(txtGroup_code1, true);

    ResetParameters();
}
public DEV004_xxx()
{
    InitializeComponent();

    ...
    
    tabMain.SelectedPageChanged += TabMain_SelectedPageChanged;
}

...

private void TabMain_SelectedPageChanged(object sender, DevExpress.XtraTab.TabPageChangedEventArgs e)
{
    // 이전 페이지에 버튼 상태 정보를 저장
    if (e?.PrevPage != null)
        e.PrevPage.Tag = CurrentProcessButtonState;

    // 전환된 페이지의 Tag에 ProcessButtonState가 존재하지 않으면 초기화
    if (!Enum.TryParse(e.Page?.Tag?.ToString() ?? string.Empty, out ProcessButtonState buttonState))
    {
        UpdateProcessButtonState(ProcessButtonState.None);
        return;
    }

    // 전환된 페이지가 가지고 있던 버튼 상태로 변경
    UpdateProcessButtonState(buttonState);
}

  • 각 버튼별 클릭 메서드(ClickRetrieveButton, ClickNewButton ...)에 현재 선택된 탭 페이지를 비교하는 코드를 작성합니다.

// 조회
public override void ClickRetrieveButton()
{
    // 처리영역 탭
    if (tabMain.SelectedTabPage.Equals(tpgEdit))
    {
        int beforeRowHandle = gvwList.FocusedRowHandle;
        int findIndex = 0;

        if (Func_P_DEV004_xxx_Q("list"))
        {
            if (!string.IsNullOrEmpty(_FindRowValue))
            {
                findIndex = gvwList.LocateByValue("group_code", _FindRowValue);
                _FindRowValue = null; // findIndex를 찾고 초기화

                gvwList.FocusedRowHandle = findIndex;
            }
        }

        if (gvwList.FocusedRowHandle < 0
            || (beforeRowHandle <= 0 && gvwList.FocusedRowHandle == 0)
            || findIndex < 0)
        {
            FocusedRowChanged(gvwList);
        }
    }
    // 조회영역 탭
    else if (tabMain.SelectedTabPage.Equals(tpgTreeView))
    {
    }
}
// 신규
public override void ClickNewButton()
{
    // 처리영역 탭
    if (tabMain.SelectedTabPage.Equals(tpgEdit))
    {
        InitControls(grpMaster);
        InitControls(grdDetail);

        ReadOnlyControl(txtGroup_code1, false);
    }
}
// 저장
public override void ClickSaveButton()
{
    // 처리영역 탭
    if (tabMain.SelectedTabPage.Equals(tpgEdit))
    {
        if (Func_P_DEV004_xxx_S(txtGroup_code1.ReadOnly ? "U" : "N"))
        {
            ClickRetrieveButton();
        }
    }
}
// 삭제
public override void ClickDeleteButton()
{
    // 처리영역 탭
    if (tabMain.SelectedTabPage.Equals(tpgEdit))
    {
        if (gvwList.FocusedRowHandle < 0)
        {
            ShowMessageBox(GetFormMessage("PROJECTBASE_012", "선택된 자료가 없습니다."));
            return;
        }
        else if (ShowMessageBox(GetFormMessage("PROJECTBASE_014", "정말 삭제하시겠습니까? 삭제하시려면 Yes를 클릭하세요."), "", MessageFormButtons.YesNo, MessageFormIcon.Question)
            != DialogResult.Yes)
        {
            return;
        }

        if (Func_P_DEV004_xxx_S("D"))
        {
            ClickRetrieveButton();
        }
    }
}

3. TreeList (GST.Platform.Client.Controls.TreeListEx)

3.1) 조회용 프로시저(P_DEV004_xxx_Q)에 쿼리 추가

  • TreeList 컨트롤에 바인딩 할 데이터를 조회하기 위해 새로운 작업 타입(@p_work_type='tree_view')의 쿼리를 등록합니다. (※ parent_key_id, key_id 필드에 대해서는 아래에서 설명)

3.2) 디자인 수정

  • 두 번째 텝 페이지에 GroupControlEx와 TreeListEx 컨트롤을 추가하고, GPM Tools를 통해 조회용 프로시저 결과 테이블을 사용하여 트리의 컬럼에 생성합니다.

  • 이후 TreeList의 디자이너 옵션 팝업에서 KeyFieldNameParentFieldName 속성에 각각 key_id, parent_key_id 값을 설정하고 [GPM Tools→용어 정보]에서 용어 정보를 세팅합니다.

TreeList에 데이터 바인딩 시 KeyFieldName, ParentFieldName 속성에 지정된 값을 확인하여 TreeNode의 레벨을 자동으로 구성합니다. 이때 KeyFieldName 속성으로 지정된 필드의 DataSource에는 중복 값이 존재하면 안됩니다. ( [DevExpress - Tree Generation Algorithm in the Tree List] 참고 )

3.3) 조회 기능 수정

  • 조회용 프로시저 실행 메서드(Func_P_DEV004_xxx_Q)에 작업 타입이 "tree_view"일때 트리 컨트롤에 데이터가 바인딩 되도록 수정합니다.

  • ClickRetrieveButton 메서드에서 두 번째 탭 페이지가 활성화 되어있을 때 작업 타입 "tree_view"로 Func_P_DEV004_xxx_Q 메서드를 호출합니다.

private bool Func_P_DEV004_xxx_Q(string workType)
{
    ...
    
    try
    {
        P_DEV004_xxx_Q procInfo = new P_DEV004_xxx_Q();

        procInfo.AddParamData(
            workType,
            groupCode,
            groupName,
            groupCategory);

        ResultSet result = ExecuteProcedure(procInfo);
        
        if (workType.Equals("list"))
        {
            SetData(grdList, result?[0]);
        }
        else if (workType.Equals("detail"))
        {
            SetData(grdDetail, result?[0]);
        }
        else if (workType.Equals("tree_view"))
        {
            SetData(treeList, result?[0]);
            treeList.ExpandAll();
        }

        return result?.IsSuccess ?? false;
    }
    catch (Exception ex)
    {
        ShowErrorMessageBox(ex);
        return false;
    }
}
public override void ClickRetrieveButton()
{
    // 처리영역 탭
    if (tabMain.SelectedTabPage.Equals(tpgEdit))
    {
        ...
    }
    // 조회영역 탭
    else if (tabMain.SelectedTabPage.Equals(tpgTreeView))
    {
        Func_P_DEV004_xxx_Q("tree_view");
    }
}

3.4) TreeList 요약 정보

4. ChartControl

4.1) 조회용 프로시저(P_DEV004_xxx_Q)에 쿼리 추가

  • ChartControl에 바인딩 할 데이터를 조회하기 위해 새로운 작업 타입(@p_work_type='chart')의 쿼리를 등록합니다.

4.2) 디자인 수정

  • 세 번째 탭 페이지를 추가하여 해당 페이지에 GroupControlEx와 ChartControl을 배치하고 용어 정보를 수정합니다.

4.3) 탭 페이지 버튼 상태 초기화

  • 새로 추가한 탭 페이지에 대한 초기 버튼 상태를 등록합니다.

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);

    // 탭페이지별 버튼 상태 초기화
    ...
    tpgChart.Tag = ProcessButtonState.Retrieve;

    ...
}

4.4) 조회 기능 수정

  • 조회용 프로시저 실행 메서드(Func_P_DEV004_xxx_Q)에 작업 타입이 "chart"일때 차트 컨트롤에 데이터가 바인딩 되도록 수정합니다.

  • ClickRetrieveButton 메서드에서 세 번째 탭 페이지가 활성화 되어있을 때 작업 타입 "chart"로 Func_P_DEV004_xxx_Q 메서드를 호출합니다.

  • ※ 차트에 데이터를 바인딩하는 패턴에는 쿼리 결과에 따라 여러 케이스가 있으며 실습 과정에선 가장 일반적인 패턴을 사용합니다.

private bool Func_P_DEV004_xxx_Q(string workType)
{
    ...
    
    try
    {
        P_DEV004_xxx_Q procInfo = new P_DEV004_xxx_Q();

        procInfo.AddParamData(
            workType,
            groupCode,
            groupName,
            groupCategory);

        ResultSet result = ExecuteProcedure(procInfo);
        ...
        
        // 차트
        else if (workType.Equals("chart"))
        {
            chartControl.Series.Clear();

            chartControl.SeriesDataMember = "member";
            chartControl.SeriesTemplate.ValueDataMembers.AddRange(new string[] { "value" });
            chartControl.SeriesTemplate.ArgumentDataMember = "argument";
            chartControl.DataSource = result?[0];
            chartControl.Refresh();

            // 두 번째 시리즈의 타입 변경
            Series series = chartControl.Series.ElementAtOrDefault(1) as Series;
            if (series != null)
            {
                series.ChangeView(ViewType.Line);
            }
        }

        return result?.IsSuccess ?? false;
    }
    catch (Exception ex)
    {
        ShowErrorMessageBox(ex);
        return false;
    }
}
public override void ClickRetrieveButton()
{
    ...
    // 차트 탭
    else if (tabMain.SelectedTabPage.Equals(tpgChart)) 
    {
        Func_P_DEV004_xxx_Q("chart");
    }
}

5. 체크 리스트

  • 각 탭 페이지별로 GSTBrowser의 공통 버튼 상태가 제대로 변경되는지 확인합니다.

  • [처리영역] 탭 페이지에서 검색 / 신규→저장 / 수정→저장 / 삭제 기능이 올바르게 동작하는지 확인합니다.

  • [조회영역] 탭 페이지에서 트리의 레벨과 전체 노드의 개수가 Footer영역에 정상적으로 표현되는지 확인합니다.

  • [차트] 탭 페이지에서 그래프가 정상적으로 표현되는지 확인합니다.

Last updated

Was this helpful?