7) MainForm

이제 마지막으로 MainForm을 구현할 차례입니다. 프로젝트 생성할 때 디폴트로 제공한 Form1을 MainForm으로 이름을 변경하세요. 솔루션 탐색기에서 변경하면 클래스 이름을 자동으로 변경합니다.

MainForm에는 탭 컨트롤을 배치합니다. 탭 컨트롤은 두 개의 탭 페이지로 구성하고 첫번째 탭 페이지는 프로젝트 탭입니다. 프로젝트 탭은 좌측에 프로젝트 생성 버튼과 프로젝트 목록을 표시할 리스트 박스를 배치합니다. 그리고 우측에는 프로젝트 생성이나 정보를 표시할 영역을 남겨둡니다.

[그림 10.15] MainForm 배치 - 프로젝트 탭
[그림 10.15] MainForm 배치 – 프로젝트 탭
번호컨트롤 형식컨트롤 이름특이 사항
1TabControltc_maintp_project, tp_acc_evaluate 탭 추가
2Buttonbtn_new_project 
3ListBoxlbox_project 
4SplitContainersc_project2,3은 4의 왼쪽 Panel에 배치한 것임
5MenuStripms_main메인 메뉴
6FolderBrowserDialogfbdlog 

[표 10.7] MainForm의 자식 컨트롤1

접근성 평가 탭 페이지에는 선택한 평가 프로젝트의 다양한 정보를 확인할 수 있는 자식 컨트롤을 배치합니다.

[그림 10.16] MainForm 배치 - 접근성 평가 탭
[그림 10.16] MainForm 배치 – 접근성 평가 탭
번호컨트롤 형식컨트롤 이름특이 사항
7Buttonbtn_view_image 
8TreeViewtv_hierarchy 
9ListViewlv_propertych_name, ch_value 열 추가,View 속성을 Details로 지정
10ListBoxlbox_pattern 

[표 10.8] MainForm의 자식 컨트롤2

Main 폼의 메뉴를 [그림 10.17]처럼 편집하세요.

[그림 10.17] MainForm 메뉴
[그림 10.17] MainForm 메뉴
번호컨트롤 형식컨트롤 이름특이 사항
12ToolStripMenuItemms_file 
13ToolStripMenuItemms_file_new 
14ToolStripMenuItemms_file_save_ClickEnabled 속성을 False로 지정
15ToolStripMenuItemms_file_endEnabled 속성을 False로 지정
16ToolStripMenuItemms_file_exit 
17ToolStripMenuItemms_view 
18ToolStripMenuItemms_view_highlightChecked 속성을 True로 지정CheckState 속성은 Checked
19ToolStripMenuItemms_view_imageEnabled 속성을 False로 지정
20ToolStripMenuItemms_view_invokeEnabled 속성을 False로 지정
21ToolStripMenuItemms_searchEnabled 속성을 False로 지정
22ToolStripMenuItemms_search_firstEnabled 속성을 False로 지정
23ToolStripMenuItemms_search_lastEnabled 속성을 False로 지정
24ToolStripMenuItemms_search_afterEnabled 속성을 False로 지정
25ToolStripMenuItemms_search_beforeEnabled 속성을 False로 지정
26ToolStripMenuItemms_search_parentEnabled 속성을 False로 지정

[표 10.9] MainForm의 자식 컨트롤3

메인 폼에는 다른 폼을 띄우는 역할을 수행합니다. 이를 위해 멤버 필드를 선언합니다.

ImageForm image_form = null;
InvokePatternForm invokepattern_form = null;

메인 폼의 첫번째 탭 페이지의 우측에 표시할 사용자 정의 컨트롤을 위한 멤버를 선언합니다.

UserControl sc_pro_panel2_child=null;

프로그램 방식으로 하이라이트를 표시하기 위한 멤버를 선언합니다.

EHHighlight eh = null;

사용하기 편하게 EvalManager 클래스의 단일체를 가져오기 속성을 추가합니다.

EvalManager EM
{
    get{    return EvalManager.Manager;    }
}

메인 폼의 Load 이벤트 핸들러를 등록하세요.

private void MainForm_Load(object sender, EventArgs e)
{

프로그램 방식으로 하이라이트를 표시할 개체를 생성합니다.

    eh = new EHHighlight();
    eh.Visible = true;

프로젝트를 생성하였을 때와 선택 변경하였을 때의 이벤트를 구독하기 위해 이벤트 핸들러를 등록합니다.

    EM.MakedProject += new MakeProjectEventHandler(EM_MakedProject);
    EM.ChangedProject += new MakeProjectEventHandler(EM_ChangedProject);
}
void EM_MakedProject(object sender, MakeProjectEventArgs e)
{

프로젝트 생성 이벤트를 통보 받았을 때 이벤트 핸들러는 크로스 문제가 발생할 수 있습니다. 따라서 이를 해결합니다.

    if (this.InvokeRequired)
    {
        this.Invoke(new MakeProjectEventHandler(EM_MakedProject), new object[] { sender, e });
    }

평가 프로젝트 목록에 추가하고 생성한 평가 프로젝트를 선택 프로젝트로 선택합니다. 그리고 평가 대상 프로젝트의 메인 창이 움직일 때의 이벤트를 구독하는 이벤트 핸들러를 등록합니다.

    else
    {
        lbox_project.Items.Add(e.AccEvalProject);
        lbox_project.SelectedItem = e.AccEvalProject;
        e.AccEvalProject.AEMoved +=
                 new AutomationPropertyChangedEventHandler(AccEvalProject_AEMoved);

프로젝트를 평가 시작하는 메서드를 호출합니다. 이 부분은 별도로 만들어서 사용합니다.

        EvaluationStart(e.AccEvalProject);

메뉴의 활성화를 설정하는 메서드를 호출합니다. 이 부분도 별도로 만들어서 사용합니다.

        SetEnableMenu(true);
    }
}

파일의 메뉴 아이템의 Enabled 속성을 설정합니다.

private void SetEnableMenu(bool flag)
{
    ms_file_save.Enabled = flag;
    ms_file_end.Enabled = flag;
    ms_view_image.Enabled = flag;
    ms_view_invoke.Enabled = flag;
    btn_view_image.Enabled = flag;
}

시작할 프로젝트 개체가 없으면 프로젝트 목록에서 선택 항목을 제거합니다.

private void EvaluationStart(AccEvalProject aeproject)
{
    if (aeproject == null)
    {
        lbox_project.Items.Remove(lbox_project.SelectedItem);
        return;
    }

선택한 프로젝트의 정보를 표시할 컨트롤을 생성하여 배치합니다.

    ProjectInfoControl pictrl = new ProjectInfoControl(aeproject);
    ChangeProjectPanel2Child(pictrl);

선택한 평가 프로젝트 개체의 래핑한 프로세스 개체를 참조하고 계층화 트리의 항목도 선택한 평가 대상 프로세스의 루트 노드로 설정합니다.

    EHProcess ehprocess = aeproject.EHProcess;
    tv_hierarchy.Nodes.Clear();
    tv_hierarchy.Nodes.Add(aeproject.Root);
    tv_hierarchy.ExpandAll();
}

현재 자식 컨트롤이 존재하면 먼저 Dispose 메서드를 호출하여 해제합니다.

private void ChangeProjectPanel2Child(UserControl ctrl)
{
    if (sc_pro_panel2_child != null)
    {
        sc_pro_panel2_child.Dispose();
    }

입력 인자로 들어온 사용자 정의 컨트롤을 자식으로 배정합니다.

    sc_pro_panel2_child = ctrl;
    if (ctrl != null)
    {
        ctrl.Parent = sc_project.Panel2;
        ctrl.Dock = DockStyle.Fill;
    }
}

크로스 스레드 문제가 발생할 수 있으니 이를 해결합니다.

void AccEvalProject_AEMoved(object sender, AutomationPropertyChangedEventArgs e)
{
    if (this.InvokeRequired)
    {
        this.Invoke(new AutomationPropertyChangedEventHandler(AccEvalProject_AEMoved),
                   new object[] { sender, e });
        return;
    }

선택한 노드를 확인해 사각 영역을 구합니다.

    TreeNode tn = tv_hierarchy.SelectedNode;
    if (tn != null)
    {
        EHAutoElem eae = tn.Tag as EHAutoElem;
        eh.Rect = eae.GetBoundaryRect();
    }
}

크로스 스레드 문제가 발생할 수 있으니 이를 해결합니다.

void EM_ChangedProject(object sender, MakeProjectEventArgs e)
{
    if (this.InvokeRequired)
    {
        this.Invoke(new MakeProjectEventHandler(EM_ChangedProject), new object[] { sender, e });
    }

평가 프로젝트의 정보를 설정하는 EvaluationStart 메서드를 호출합니다.

    else
    {
        EvaluationStart(e.AccEvalProject);

만약 이벤트 인자로 전달받은 프로젝트 개체가 없으면 평가를 끝냅니다.

        if (e.AccEvalProject == null)
        {
            MessageBox.Show("평가를 끝냅니다.");
            EndProject();
        }
    }
}
void EndProject()
{

탭 페이지 우측 패널에 자식 컨트롤을 null로 설정합니다.

    ChangeProjectPanel2Child(null);

서브 폼들이 떠 있으면 닫아주고 현재 프로젝트도 닫습니다.

    if (invokepattern_form != null){    invokepattern_form.Close();    }
    if (image_form != null){    image_form.Close();    }
    AccEvalProject aeproject = EM.CurrentProject;
    if (aeproject != null){    aeproject.End();    }
    tv_hierarchy.Nodes.Clear();
    lv_property.Items.Clear();
    lbox_pattern.Items.Clear();
}

프로젝트 생성 버튼의 클릭 이벤트 핸들러를 등록하세요.

private void btn_new_project_Click(object sender, EventArgs e)
{

프로젝트 생성에 필요한 사용자 정의 컨트롤을 생성하여 탭 페이지 우측 패널에 배치합니다.

    ProjectMakerControl pmctrl = new ProjectMakerControl();
    ChangeProjectPanel2Child(pmctrl);
}

트리 뷰의 AfterSelect 이벤트 핸들러를 등록합니다.

private void tv_hierarchy_AfterSelect(object sender, TreeViewEventArgs e)
{

선택 노드가 없으면 이벤트 핸들러를 종료합니다.

    if (tv_hierarchy.SelectedNode == null){    return;    }

선택한 노드로 UI 속성 트리큐와 계층 관계, 패턴 등을 변경합니다. 이들은 모두 별도의 메서드로 작성합시다.

    TreeNode tn = tv_hierarchy.SelectedNode;
    EHAutoElem eae = tn.Tag as EHAutoElem;
    ChangeUIPropertyTreeView(eae);
    ChangeFamily(tn);
    ChangePattern(eae);

사각 영역을 가져옵니다.

    eh.Rect = eae.GetBoundaryRect();
}
private void ChangeUIPropertyTreeView(EHAutoElem eae)
{

속성 리스트 뷰의 항목을 모두 지웁니다.

    lv_property.Items.Clear();
    string[] vals = new string[2];

자동화 속성의 목록을 리스트 뷰의 항목에 추가합니다.

    for (ENUM_UIProperty i = 0; i < ENUM_UIProperty.MAX_UIPROPERTY; i++)
    {
        vals[0] = i.ToString();
        vals[1] = eae.GetAEPropertyByIndex((int)i);
        lv_property.Items.Add(new ListViewItem(vals));
    }
}
private void ChangeFamily(TreeNode tn)
{

부모 자식 관계에 따른 메뉴 스트립의 Enabled 속성을 설정합니다.

    ms_search_parent.Enabled = (tn.Parent != null);
    ms_search_after.Enabled = (tn.NextNode != null);
    ms_search_before.Enabled = (tn.PrevNode != null);
    ms_search_first.Enabled = (tn.FirstNode != null);
    ms_search_last.Enabled = (tn.LastNode != null);
}
private void ChangePattern(EHAutoElem eae)
{
    object obj = null;

패턴 리스트 박스의 모든 항목을 지웁니다.

    lbox_pattern.Items.Clear();

패턴 항목을 조사하여 리스트 박스의 항목에 추가합니다.

    for (ENUM_CONTROL dt = 0; dt < ENUM_CONTROL.MAX_CONTROL; dt++)
    {
        obj = eae.GetPattern(dt);
        if (obj != null){    lbox_pattern.Items.Add(obj);    }
    }
}

평가 프로젝트 리스트 박스의 SelecctedIndexChanged 이벤트 핸들러를 등록하세요.

private void lbox_project_SelectedIndexChanged(object sender, EventArgs e)
{

선택한 항목이 없으면 이벤트 핸들러를 종료합니다.

    if (lbox_project.SelectedIndex == -1){    return;    }

선택한 평가 프로젝트를 현재 프로젝트로 설정합니다.

    AccEvalProject aeproject = lbox_project.SelectedItem as AccEvalProject;
    EM.SetCurrentProject(aeproject.ToString());
}

하이라이트 보이기 메뉴 스트립의 클릭 이벤트 핸드러를 등록하여 시각화 상태를 토글합니다.

private void ms_view_highlight_Click(object sender, EventArgs e)
{
    ms_view_highlight.Checked ^= true;
    eh.Visible = ms_view_highlight.Checked;
}

이미지 보기 메뉴 스트립의 클릭 이벤트 핸들러를 등록하세요.

private void ms_view_image_Click(object sender, EventArgs e)
{

이미지 폼이 떠 있지 않다면 이미지 폼을 띄웁니다.

    if (image_form == null)
    {
        image_form = new ImageForm();

이미지 폼을 두 번 띄우지 않기 위해 image_form 멤버에 개체를 기억하게 한 것입니다. 여기에서는 이미지 폼이 닫히는 이벤트를 구독하기 위한 이벤트 핸들러도 등록합니다.

        image_form.FormClosed += new FormClosedEventHandler(image_form_FormClosed);
        image_form.Show();
    }
}
void image_form_FormClosed(object sender, FormClosedEventArgs e)
{

이미지 폼이 닫혔다는 이벤트를 통보받으면 image_form을 null로 설정합니다. 이 작업을 하지 않으면 한 번 이미지 폼이 뜨고 난 이후에는 이미지 폼을 닫아도 다시 뜨지 않습니다.

    image_form = null;
}

Invoke 가능하 요소 보기 메뉴 스트립의 Click 이벤트 핸들러를 등록하세요.

private void ms_view_invoke_Click(object sender, EventArgs e)
{

InvokePattern 폼이 떠 있지 않거나 현재 선택 프로젝트가 있을 때만 작업을 수행합니다.

    if ((invokepattern_form == null) && (EM.CurrentProject != null))

현재 선택 프로젝트의 Root 노드를 가져오고 매핑하는 EHAutoElem 개체를 참조합니다.

        TreeNode root = EM.CurrentProject.Root;
        EHAutoElem eae = root.Tag as EHAutoElem;

InvokePatternForm 개체를 생성하면서 EHAutoElem 개체를 전달합니다.

        invokepattern_form = new InvokePatternForm(eae);

InvokePatternForm 이 닫히는지 알 수 있게 이벤트 핸들러를 등록합니다.

        invokepattern_form.FormClosed += new FormClosedEventHandler(ip_form_FormClosed);
        invokepattern_form.Show();
    }
}
void ip_form_FormClosed(object sender, FormClosedEventArgs e)
{
    invokepattern_form = null;
}

저장 메뉴 스트립의 Click 이벤트 핸들러를 등록합니다.

private void ms_file_save_Click(object sender, EventArgs e)
{

폴더 브라우저 대화상자를 띄워서 폴더를 선택하면 평가 프로젝트 개체의 Save 메서드를 호출합니다.

    if (fbdlog.ShowDialog() == DialogResult.OK)
    {
        AccEvalProject aeproject = EM.CurrentProject;
        aeproject.Save(fbdlog.SelectedPath);
    }
}

프로젝트 끝내기 메뉴 스트립의 Click 이벤트 핸들러를 등록하여 EndProject를 호출합니다.

private void ms_file_end_Click(object sender, EventArgs e)
{
    EndProject();
}

종료 메뉴 스트립의 Click 이벤트 핸들러를 등록하여 Close 메서드를 호출합니다.

private void ms_file_exit_Click(object sender, EventArgs e)
{
    Close();
}

탐색의 각 메뉴 스트립의 Click 이벤트 핸들러도 등록하여 선택 항목의 계층 구조에 맞게 설정하세요.

private void ms_search_first_Click(object sender, EventArgs e)
{
    TreeNode tr = tv_hierarchy.SelectedNode;
    tv_hierarchy.SelectedNode = tr.FirstNode;
}

private void ms_search_last_Click(object sender, EventArgs e)
{
    TreeNode tr = tv_hierarchy.SelectedNode;
    tv_hierarchy.SelectedNode = tr.LastNode;
}

private void ms_search_after_Click(object sender, EventArgs e)
{
    TreeNode tr = tv_hierarchy.SelectedNode;
    tv_hierarchy.SelectedNode = tr.NextNode;
}

private void ms_search_before_Click(object sender, EventArgs e)
{
    TreeNode tr = tv_hierarchy.SelectedNode;
    tv_hierarchy.SelectedNode = tr.PrevNode;
}

private void ms_search_parent_Click(object sender, EventArgs e)
{
    TreeNode tr = tv_hierarchy.SelectedNode;
    tv_hierarchy.SelectedNode = tr.Parent;
}
using System;
using System.Windows.Forms;
using System.Windows.Automation;
using System.IO;

namespace 접근성_평가_도우미
{
    public partial class MainForm : Form
    {
        ImageForm image_form = null;
        InvokePatternForm invokepattern_form = null;
        UserControl sc_pro_panel2_child=null;
        EHHighlight eh = null;
        EvalManager EM
        {
            get
            {
                return EvalManager.Manager;
            }
        }
        public MainForm()
        {
            InitializeComponent();
        }
        private void MainForm_Load(object sender, EventArgs e)
        {
            eh = new EHHighlight();
            eh.Visible = true;
            EM.MakedProject += new MakeProjectEventHandler(EM_MakedProject);            
            EM.ChangedProject += new MakeProjectEventHandler(EM_ChangedProject);
        }
        void EM_MakedProject(object sender, MakeProjectEventArgs e)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new MakeProjectEventHandler(EM_MakedProject),new object[]{sender,e });
            }
            else
            {
                lbox_project.Items.Add(e.AccEvalProject);
                lbox_project.SelectedItem = e.AccEvalProject;
                e.AccEvalProject.AEMoved += 
                      new AutomationPropertyChangedEventHandler(AccEvalProject_AEMoved);
                EvaluationStart(e.AccEvalProject);
                SetEnableMenu(true);
            }
        }
        private void SetEnableMenu(bool flag)
        {
            ms_file_save.Enabled = flag;
            ms_file_end.Enabled = flag;
            ms_view_image.Enabled = flag;
            ms_view_invoke.Enabled = flag;
            btn_view_image.Enabled = flag;
        }
        private void EvaluationStart(AccEvalProject aeproject)
        {
            if (aeproject == null)
            {
                lbox_project.Items.Remove(lbox_project.SelectedItem);
                return;
            }
            ProjectInfoControl pictrl = new ProjectInfoControl(aeproject);
            ChangeProjectPanel2Child(pictrl);
            EHProcess ehprocess = aeproject.EHProcess;
            tv_hierarchy.Nodes.Clear();
            tv_hierarchy.Nodes.Add(aeproject.Root);
            tv_hierarchy.ExpandAll();
        }
        private void ChangeProjectPanel2Child(UserControl ctrl)
        {
            if (sc_pro_panel2_child != null)
            {
                sc_pro_panel2_child.Dispose();
            }
            sc_pro_panel2_child = ctrl;
            if (ctrl != null)
            {
                ctrl.Parent = sc_project.Panel2;
                ctrl.Dock = DockStyle.Fill;
            }
        }
        void AccEvalProject_AEMoved(object sender, AutomationPropertyChangedEventArgs e)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new AutomationPropertyChangedEventHandler(
                           AccEvalProject_AEMoved), new object[] { sender, e });
                return;
            }
            TreeNode tn = tv_hierarchy.SelectedNode;
            if (tn != null)
            {
                EHAutoElem eae = tn.Tag as EHAutoElem;
                eh.Rect = eae.GetBoundaryRect();
            }
        }
        void EM_ChangedProject(object sender, MakeProjectEventArgs e)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new MakeProjectEventHandler(EM_ChangedProject),new object[]{sender,e});
            }
            else
            {
                EvaluationStart(e.AccEvalProject);
                if (e.AccEvalProject == null)
                {
                    MessageBox.Show("평가를 끝냅니다.");
                    EndProject();
                }
            }
        }
        void EndProject()
        {
            ChangeProjectPanel2Child(null);
            if (invokepattern_form != null)
            {
                invokepattern_form.Close();
            }
            if (image_form != null)
            {
                image_form.Close();
            }
            AccEvalProject aeproject = EM.CurrentProject;
            if (aeproject != null)
            {
                aeproject.End();
            }
            tv_hierarchy.Nodes.Clear();
            lv_property.Items.Clear();
            lbox_pattern.Items.Clear();
        }
        private void btn_new_project_Click(object sender, EventArgs e)
        {
            ProjectMakerControl pmctrl = new ProjectMakerControl();
            ChangeProjectPanel2Child(pmctrl);
        }    
        private void tv_hierarchy_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (tv_hierarchy.SelectedNode == null){    return;    }
            TreeNode tn = tv_hierarchy.SelectedNode;
            EHAutoElem eae = tn.Tag as EHAutoElem;
            ChangeUIPropertyTreeView(eae);
            ChangeFamily(tn);
            ChangePattern(eae);
            eh.Rect = eae.GetBoundaryRect();
        }     
        private void ChangeUIPropertyTreeView(EHAutoElem eae)
        {
            lv_property.Items.Clear();
            string[] vals = new string[2];
            for (ENUM_UIProperty i = 0; i < ENUM_UIProperty.MAX_UIPROPERTY; i++)
            {
                vals[0] = i.ToString();
                vals[1] = eae.GetAEPropertyByIndex((int)i);
                lv_property.Items.Add(new ListViewItem(vals));
            }
        }
        private void ChangeFamily(TreeNode tn)
        {
            ms_search_parent.Enabled = (tn.Parent != null);
            ms_search_after.Enabled = (tn.NextNode != null);
            ms_search_before.Enabled = (tn.PrevNode != null);
            ms_search_first.Enabled = (tn.FirstNode != null);
            ms_search_last.Enabled = (tn.LastNode != null);
        }
        private void ChangePattern(EHAutoElem eae)
        {
            object obj = null;
            lbox_pattern.Items.Clear();
            for (ENUM_CONTROL dt = 0; dt < ENUM_CONTROL.MAX_CONTROL; dt++)
            {
                obj = eae.GetPattern(dt);

                if (obj != null)
                {
                    lbox_pattern.Items.Add(obj);
                }
            }
        }
        private void lbox_project_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lbox_project.SelectedIndex == -1)
            {
                return;
            }
            AccEvalProject aeproject = lbox_project.SelectedItem as AccEvalProject;
            EM.SetCurrentProject(aeproject.ToString());
        }
        private void ms_view_highlight_Click(object sender, EventArgs e)
        {
            ms_view_highlight.Checked ^= true;
            eh.Visible = ms_view_highlight.Checked;
        }
        private void ms_view_image_Click(object sender, EventArgs e)
        {
            if (image_form == null)
            {
                image_form = new ImageForm();
                image_form.FormClosed += new FormClosedEventHandler(image_form_FormClosed);
                image_form.Show();
            }
        }
        void image_form_FormClosed(object sender, FormClosedEventArgs e)
        {
            image_form = null;
        }
        private void ms_view_invoke_Click(object sender, EventArgs e)
        {
            if ((invokepattern_form == null) && (EM.CurrentProject != null))
            {
                TreeNode root = EM.CurrentProject.Root;
                EHAutoElem eae = root.Tag as EHAutoElem;
                invokepattern_form = new InvokePatternForm(eae);
                invokepattern_form.FormClosed += 
                                      new FormClosedEventHandler(ip_form_FormClosed);
                invokepattern_form.Show();
            }
        }
        void ip_form_FormClosed(object sender, FormClosedEventArgs e)
        {
            invokepattern_form = null;
        }
        private void ms_file_save_Click(object sender, EventArgs e)
        {
            if (fbdlog.ShowDialog() == DialogResult.OK)
            {
                AccEvalProject aeproject = EM.CurrentProject;
                aeproject.Save(fbdlog.SelectedPath);
            }
        }
        private void ms_file_end_Click(object sender, EventArgs e)
        {
            EndProject();
        }
        private void ms_file_exit_Click(object sender, EventArgs e)
        {
            Close();
        }
        private void ms_search_first_Click(object sender, EventArgs e)
        {
            TreeNode tr = tv_hierarchy.SelectedNode;
            tv_hierarchy.SelectedNode = tr.FirstNode;
        }
        private void ms_search_last_Click(object sender, EventArgs e)
        {
            TreeNode tr = tv_hierarchy.SelectedNode;
            tv_hierarchy.SelectedNode = tr.LastNode;
        }
        private void ms_search_after_Click(object sender, EventArgs e)
        {
            TreeNode tr = tv_hierarchy.SelectedNode;
            tv_hierarchy.SelectedNode = tr.NextNode;
        }
        private void ms_search_before_Click(object sender, EventArgs e)
        {
            TreeNode tr = tv_hierarchy.SelectedNode;
            tv_hierarchy.SelectedNode = tr.PrevNode;
        }
        private void ms_search_parent_Click(object sender, EventArgs e)
        {
            TreeNode tr = tv_hierarchy.SelectedNode;
            tv_hierarchy.SelectedNode = tr.Parent;
        }
    }
}

[소스 10.15] MainForm.cs

이상으로 UI 자동화 기술에 관한 소개를 마칠게요. 교재의 예제 코드들은 상용화를 목적으로 만든 것이 아니라 UI 자동화 기술을 소개하기 위함입니다. 충분한 테스트를 거친 것이 아니므로 버그가 발생할 수 있습니다.  수고하셨습니다.

모두가 행복한 사회를 만드는데 동참하길 기원하며 글을 마칠게요.