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의 디자이너 옵션 팝업에서 KeyFieldName과 ParentFieldName 속성에 각각
key_id
,parent_key_id
값을 설정하고 [GPM Tools→용어 정보]에서 용어 정보를 세팅합니다.



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?