프로그래밍 기술/소프트웨어 접근성, UI 자동화

[소프트웨어 접근성] UI 자동화 요소 탐색기 만들기 실습 - 메인 폼 구현

언제나휴일 2016. 5. 5. 18:51
반응형

[소프트웨어 접근성] UI 자동화 요소 탐색기 만들기 실습 - 메인 폼 구현


[그림] 작성할 UI 자동화 요소 탐색기


*Visual Studio 2010으로 작성한 솔루션입니다. 자신의 개발 도구 버전에 맞게 변환하세요. *

UsingTreeWalker.zip


 이제 폼을 구현할 차례입니다.

 

 먼저 프로세스 새로고침 버튼에 클릭 이벤트 핸들러를 추가합니다.

private void btn_refresh_Click(object sender, EventArgs e)

{

 프로세스 ListBox의 항목을 지웁니다.

    lbox_process.Items.Clear();

 프로세스 목록을 구합니다.

    Process[] processes = Process.GetProcesses();

 프로세스 목록의 각 프로세스마다 다음을 반복합니다.

    foreach (Process pro in processes)

    {

 만약 프로세스의 MainWindowHandle이 있으면 프로세스를 래핑한 개체를 생성하여 프로세스 ListBox의 항목에 추가합니다.

        if (pro.MainWindowHandle != IntPtr.Zero)

        {

            lbox_process.Items.Add(new WrapProcess(pro));

        }

    }

}

 

 프로세스 ListBox의 항목 변경 이벤트 핸들러를 추가합니다.

private void lbox_process_SelectedIndexChanged(object sender, EventArgs e)

{

 자동화 요소 TreeView의 노드를 제거하고 선택한 항목의 개체를 구합니다.

    tv_ae.Nodes.Clear();

    WrapAE wae = SelectedWrapAE();

 만약 선택한 항목의 개체가 없다면 기능을 종료합니다.

    if (wae == null){    return;    }

 선택한 항목의 개체를 인자로 UISearcher 개체를 생성합니다.

    UISearcher uis = new UISearcher(wae);

 생성한 개체의 FindEnableControl 메서드를 호출합니다. FindEnalbeControl 메서드에서는 활성화 상태의 컨트롤들로 구성한 TreeNode를 만들어 반환합니다. 그리고 반환받은 개체를 입력 인자로 TreeView의 루트 노드를 변경합니다.

    ChangeRootNode(uis.FindEnalbleControl());

    uis = null;

}

private void ChangeRootNode(TreeNode rootnode)

{

 ChageRootNode 메서드에서는 트리 뷰의 Node 컬렉션에 root 노드를 추가한 후 노드를 펼치게 합니다. 그리고 스크롤 위치를 맨 위로 위치하도록 선택 노드를 rootnode로 설정합니다.

    tv_ae.Nodes.Add(rootnode);

    tv_ae.ExpandAll();

    tv_ae.SelectedNode = rootnode;

}

 전체 찾기 버튼을 클릭했을때 이벤트 핸들러를 추가하세요.

private void btn_find_raw_Click(object sender, EventArgs e)

{

 트리 뷰의 노드 컬렉션을 비워줍니다.

    tv_ae.Nodes.Clear();

 선택 항목의 개체를 구합니다.

    WrapAE wae = SelectedWrapAE();

 선택 항목이 없으면 기능을 종료합니다.

    if (wae == null)

    {

        return;

    }

 

 선택 개체를 입력 인자로 UISearcher 개체를 생성합니다.

    UISearcher uis = new UISearcher(wae);

 생성한 개체의 FindRawTree 메서드를 이용하여 전체 목록을 포함한 TreeNode를 구하여 루트 노드를 변경합니다.

    ChangeRootNode(uis.FindRawTree());

 탐색에만 사용하는 UISearcher 개체는 더 이상 필요없으니 uis 변수에 null을 설정합니다.

    uis = null;

}

 

 컨트롤 계층 보기 버튼 클릭 이벤트 핸들러를 추가하세요. 내부 기능은 전체 보기 버튼 클릭 이벤트 핸들러와 비슷합니다. 차이가 있는 부분은 UISearch 개체의 FindControlTree 메서드를 호출한다는 점 뿐입니다.

private void btn_find_contrl_Click(object sender, EventArgs e)

{

    tv_ae.Nodes.Clear();

    WrapAE wae = SelectedWrapAE();

    if (wae == null){    return;    }

    UISearcher uis = new UISearcher(wae);

    ChangeRootNode(uis.FindControlTree());

    uis = null;

}

 

 컨텐츠 계층 보기 버튼 클릭 이벤트 핸들러를 추가하세요. 내부 기능은 전체 보기 버튼 클릭 이벤트 핸들러와 비슷합니다. 차이가 있는 부분은 UISearch 개체의 FindContentTree 메서드를 호출한다는 뿐입니다.

private void btn_find_content_Click(object sender, EventArgs e)

{

    tv_ae.Nodes.Clear();

    WrapAE wae = SelectedWrapAE();

    if (wae == null){    return;    }

    UISearcher uis = new UISearcher(wae);

    ChangeRootNode(uis.FindContentTree());

    uis = null;

}


 이번에는 탐색 기능들을 구현합시다.

 

 먼저 트리 뷰의 선택 항목을 변경했을 때의 이벤트 핸들러를 추가하세요.

private void tv_ae_AfterSelect(object sender, TreeViewEventArgs e)

{

 선택한 노드를 구합니다.

    TreeNode tn = e.Node;

 선택한 노드가 없으면 기능을 종료합니다.

    if (tn == null)

    {

        return;

    }

 선택한 노드의 tag에 설정한 개체를 참조합니다.

    WrapAE wae = tn.Tag as WrapAE;

 

 참조한 개체의 ToString 메서드를 호출하여 자동화 요소 레이블의 Text 속성을 설정합니다.

    lb_ae_info.Text = wae.ToString();

 레이블의 Tag 속성에 참조한 개체를 설정합니다.

    lb_ae_info.Tag = wae;

}

 

 레이블 테그에 설정한 자동화 요소와 관련 요소를 탐색하는 기능들을 구현합시다. 먼저 첫번째 자식 찾기 클릭 이벤트 핸들러를 추가합니다.

private void btn_first_child_Click(object sender, EventArgs e)

{

 자동화 요소 레이블의 Tag 속성에 설정한 요소를 참조합니다.

    WrapAE wae = lb_ae_info.Tag as WrapAE;

 

 참조한 개체가 없으면 기능을 종료합니다.

    if (wae == null)

    {

        return;

    }

 UISearcher의 정적 메서드 FindFirstChild 메서드를 이용하여 첫번째 자식 요소를 탐색합니다.

    wae = UISearcher.FindFirstChild(wae);

 

 탐색한 요소의 ToString 메서드로 자동화 요소 정보 레이블의 Text 속성을 설정합니다.

    lb_ae_info.Text = wae.ToString();

 레이블의 Tag 속성도 탐색한 요소로 설정합니다.

    lb_ae_info.Tag = wae;

} 

 

 나머지 탐색 버튼의 클릭 이벤트 핸들러도 추가합니다. 그리고 탐색 목적에 맞는 UISearcher의 정적 메서드로 탐색후에 자동화 요소 정보 레이블의 속성을 변경합니다. 구현 방법은 첫번째 자식 탐색 버튼 클릭 이벤트 핸들러와 비슷합니다.

 

private void btn_next_Click(object sender, EventArgs e)

{

    WrapAE wae = lb_ae_info.Tag as WrapAE;

    if (wae == null)

    {

        return;

    }

    wae = UISearcher.FindNext(wae);

    lb_ae_info.Text = wae.ToString();

    lb_ae_info.Tag = wae;

}

private void btn_prev_Click(object sender, EventArgs e)

{

    WrapAE wae = lb_ae_info.Tag as WrapAE;

    if (wae == null){    return;    }

    wae = UISearcher.FindPrev(wae);

    lb_ae_info.Text = wae.ToString();

    lb_ae_info.Tag = wae;

}

 

private void btn_last_child_Click(object sender, EventArgs e)

{

    WrapAE wae = lb_ae_info.Tag as WrapAE;

    if (wae == null)

    {

        return;

    }

    wae = UISearcher.FindLastChild(wae);

    lb_ae_info.Text = wae.ToString();

    lb_ae_info.Tag = wae;

}

private void btn_parent_Click(object sender, EventArgs e)

{

    WrapAE wae = lb_ae_info.Tag as WrapAE;

    if (wae == null)

    {

        return;

    }

    wae = UISearcher.FindParent(wae);

    lb_ae_info.Text = wae.ToString();

    lb_ae_info.Tag = wae;

}

 

 

using System;

using System.Windows.Forms;

using System.Diagnostics;

using System.Windows.Automation;

namespace UsingTreeWalker

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

        }

        private void btn_refresh_Click(object sender, EventArgs e)

        {

            lbox_process.Items.Clear();

            Process[] processes = Process.GetProcesses();

            foreach (Process pro in processes)

            {

                if (pro.MainWindowHandle != IntPtr.Zero)

                {

                    lbox_process.Items.Add(new WrapProcess(pro));

                }

            }

        }

 

        private void lbox_process_SelectedIndexChanged(object sender, EventArgs e)

        {

            tv_ae.Nodes.Clear();

            WrapAE wae = SelectedWrapAE();

            if (wae == null)

            {

                return;

            }

            UISearcher uis = new UISearcher(wae);

            ChangeRootNode(uis.FindEnalbleControl());

            uis = null;

        }

 

        private void btn_find_raw_Click(object sender, EventArgs e)

        {

            tv_ae.Nodes.Clear();

            WrapAE wae = SelectedWrapAE();

            if (wae == null)

            {

                return;

            }

            UISearcher uis = new UISearcher(wae);

            ChangeRootNode(uis.FindRawTree());

            uis = null;

        }

        private void btn_find_contrl_Click(object sender, EventArgs e)

        {

            tv_ae.Nodes.Clear();

            WrapAE wae = SelectedWrapAE();

            if (wae == null)

            {

                return;

            }

            UISearcher uis = new UISearcher(wae);

            ChangeRootNode(uis.FindControlTree());

            uis = null;

        }

 

        private void btn_find_content_Click(object sender, EventArgs e)

        {

            tv_ae.Nodes.Clear();

            WrapAE wae = SelectedWrapAE();

            if (wae == null)

            {

                return;

            }

 

            UISearcher uis = new UISearcher(wae);

            ChangeRootNode(uis.FindContentTree());

            uis = null;

        }

        private void ChangeRootNode(TreeNode rootnode)

        {

            tv_ae.Nodes.Add(rootnode);

            tv_ae.ExpandAll();

            tv_ae.SelectedNode = rootnode;

        }

        WrapAE SelectedWrapAE()

        {           

            if (lbox_process.SelectedIndex == -1)

            {

                return null;

            }

            WrapProcess wp = lbox_process.SelectedItem as WrapProcess;

            AutomationElement ae = wp.RootElement;

            return new WrapAE(ae);

        }

        private void tv_ae_AfterSelect(object sender, TreeViewEventArgs e)

        {

            TreeNode tn = e.Node;

            if (tn == null)

            {

                return;

            }

            WrapAE wae = tn.Tag as WrapAE;

            lb_ae_info.Text = wae.ToString();

            lb_ae_info.Tag = wae;

        }

        private void btn_first_child_Click(object sender, EventArgs e)

        {

            WrapAE wae = lb_ae_info.Tag as WrapAE;

            if (wae == null)

            {

                return;

            }           

            wae = UISearcher.FindFirstChild(wae);

            lb_ae_info.Text = wae.ToString();

            lb_ae_info.Tag = wae;

        }

        private void btn_next_Click(object sender, EventArgs e)

        {

            WrapAE wae = lb_ae_info.Tag as WrapAE;

            if (wae == null){    return;    }

            wae = UISearcher.FindNext(wae);

            lb_ae_info.Text = wae.ToString();

            lb_ae_info.Tag = wae;

        }

        private void btn_prev_Click(object sender, EventArgs e)

        {

            WrapAE wae = lb_ae_info.Tag as WrapAE;

            if (wae == null){    return;    }

            wae = UISearcher.FindPrev(wae);

            lb_ae_info.Text = wae.ToString();

            lb_ae_info.Tag = wae;

        }

        private void btn_last_child_Click(object sender, EventArgs e)

        {

            WrapAE wae = lb_ae_info.Tag as WrapAE;

            if (wae == null){    return;    }

            wae = UISearcher.FindLastChild(wae);

            lb_ae_info.Text = wae.ToString();

            lb_ae_info.Tag = wae;

        }

        private void btn_parent_Click(object sender, EventArgs e)

        {

            WrapAE wae = lb_ae_info.Tag as WrapAE;

            if (wae == null){    return;    }

            wae = UISearcher.FindParent(wae);

            lb_ae_info.Text = wae.ToString();

            lb_ae_info.Tag = wae;

        }

    }

}



[소프트웨어 접근성] UI 자동화 요소 탐색기 만들기 실습 - 컨트롤 배치


[소프트웨어 접근성] UI 자동화 요소 탐색기 만들기 실습- 프로세스, 자동화 요소 래퍼


[소프트웨어 접근성] UI 자동화 요소 탐색기 만들기 실습 - 핵심 클래스 UISearcher 구현


반응형