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

[S/W 접근성] InvokePattern 요소 제어하기 실습(응용 만들기)

언제나휴일 2016. 4. 19. 14:43
반응형

InvokePattern 요소 제어하기 실습(응용 만들기)


Invoke요소 제어기 솔루션(Visual Studio 2010).zip


 작성한 클래스 라이브러리를 참조 추가할 때는 솔루션 창의 프로젝트의 참조에서 컨텍스트 메뉴를 통해 참조 추가할 수 있습니다.


어셈블리 참조 추가

[그림] 어셈블리 참조 추가

 

 이제 Invoke 요소 제어기를 작성합니다. 먼저 프로젝트 추가를 통해 Windows Forms 응용 프로그램을 선택하세요.

 

 물론 WrapLib를 참조 추가 및 UI 자동화 기술에 관한 어셈블리도 참조 추가해야 합니다.

 

 이제 메인 폼의 컨트롤을 배치합시다.



Main 폼 컨트롤 배치

[그림] Main 컨트롤 배치

 

 Main 폼에는 버튼 1개와 ListBox 3개를 배치합니다.

 

번호

컨트롤 타입

컨트롤 명

설명

1

Button

btn_refresh

프로세스 목록 새로고침

2

ListBox

lbox_process

프로세스 목록을 보여주는 리스트 상자

3

ListBox

lbox_inv_elem

선택한 프로세스의 InvokePattern 자동화 요소 목록을 보여주는 리스트 상자

4

ListBox

lbox_history

선택한 프로세스의 InvokeEvent를 보여주는 리스트 상자

[] Form1의 컨트롤

 

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

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)

{

 InvokePattern 리스트 상자의 항목을 지웁니다.

    lbox_inv_elem.Items.Clear();

 프로세스 리스트 상자의 선택 항목에서 래팽한 InvokePattern 자동화 요소 개체를 얻어옵니다. 이 부분은 별도의 메서드로 정의합시다.

    WrapInvoke wae = SelectedWrapAE();

 반환 값이 null이면 이벤트 핸들러를 종료합니다.

    if (wae == null)

    {

        return;

    }

 Automation 클래스의 정적 메서드 AddAutomationEventHandler를 호출하여 InvokePattern Invoke 이벤트가 발생할 때 처리할 이벤트 핸드러를 등록합니다.

    Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent,

                     wae.AE, TreeScope.Subtree,

                     MyEventHandler = new AutomationEventHandler(OnUIAutomationEvent));

 그리고 InvokePattern인 개체를 찾기 위한 Condition 개체를 생성합니다.

    Condition cond = new PropertyCondition(

                     AutomationElement.IsInvokePatternAvailableProperty, true);

 조건 개체와 탐색할 조건을 서브 트리인 인자를 전달하여 조건에 만족하는 자동화 요소 컬렉션을 구합니다.

    AutomationElementCollection aec = wae.AE.FindAll(TreeScope.Subtree, cond);

 자동화 요소 컬렉션에 있는 자동화 요소 개체를 WrapInvoke 개체로 감싼 후에 InvokePattern 개체를 표시할 리스트 상자 항목에 추가합니다.

    foreach (AutomationElement ae in aec)

    {

        lbox_inv_elem.Items.Add(new WrapInvoke(ae));

    }

}

 

 프로세스 리스트 상자에서 선택한 항목을 WrapInvoke 개체로 래핑하여 반환하는 메서드를 정의합시다.

private WrapInvoke SelectedWrapAE()

{

 만약 선택항목이 없으면 null을 반환합니다.

    if (lbox_process.SelectedIndex == -1)

    {

        return null;

    }

 

 선택항목을 WrapProcess 형식으로 참조합니다.

    WrapProcess wp = lbox_process.SelectedItem as WrapProcess;

 프로세스의 Root 자동화 요소를 얻어와서 WrapInvoke 개체를 생성하여 반환합니다.

    AutomationElement ae = wp.RootElement;

    return new WrapInvoke(ae);

}


 Invoke 관련 이벤트를 감지하였을 때 처리할 이벤트 핸들러를 정의합시다.

private void OnUIAutomationEvent(object src, AutomationEventArgs e)

{

 src로 전달받은 개체를 자동화 요소 개체로 참조합니다.

    AutomationElement ae = src as AutomationElement;

 이벤트를 리스트 상자에 추가하는 메서드를 호출합시다.

    AddEvent(ae);

}

 

 리스트 상자에 추가하는 메서드를 정의합시다. 그런데 Invoke 관련 이벤트를 감지하여 전달하는 스레드와 리스트 상자를 소유하는 스레드가 다르기 때문에 크로스 스레드 문제가 발생합니다. 이를 위해 AddEvent 메서드와 반환 형식과 입력 인자 리스트가 같은 대리자를 정의합시다.

delegate void MyDele(AutomationElement ae);

private void AddEvent(AutomationElement ae)

{

 만약 리스트 상자의 InvokeRequired 속성이 참이면 현재 수행하는 스레드와 리스트 상자의 스레드가 서로 다름을 의미합니다. 이 때는 인자를 object 배열 개체로 생성하고 대리자 개체를 생성하여 리스트 상자의 Invoke 메서드를 호출합니다. 이는 Windows Forms 응용 프로그램의 어떠한 컨트롤에서도 같은 방법으로 크로스 스레드 문제를 해결할 수 있습니다.

    if (lbox_history.InvokeRequired)

    {

        object[] objs = new object[1] { ae };

        lbox_history.Invoke(new MyDele(AddEvent), objs);

    }

    else

    {

        lbox_history.Items.Add(ae.Current.Name+" 클릭");

    }

}

 

 

 InvokePattern 개체를 항목으로 표시한 리스트 상자의 선택 변경 이벤트 핸들러를 추가하세요.

private void lbox_inv_elem_SelectedIndexChanged(object sender, EventArgs e)

{

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

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

 선택항목을 WrapInvoke 형식으로 참조하여 Invoke 메서드를 호출합니다.

    WrapInvoke winvoke = lbox_inv_elem.SelectedItem as WrapInvoke;

    winvoke.Invoke();

}

 

 이제 프로그램을 빌드하여 테스트를 해 보세요.

 

Invoke요소 제어기.zip


using System;

using System.Windows.Forms;

using System.Diagnostics;

using System.Windows.Automation;

using WrapLib;

 

namespace Invoke요소_제어기

{

    public partial class MainForm : Form

    {

        AutomationEventHandler MyEventHandler;

 

        public MainForm()

        {

            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)

        {

            lbox_inv_elem.Items.Clear();

            WrapInvoke wae = SelectedWrapAE();

            if (wae == null)

            {

                return;

            }           

            Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent,

                     wae.AE, TreeScope.Subtree,

                     MyEventHandler = new AutomationEventHandler(OnUIAutomationEvent));

 

            Condition cond = new PropertyCondition(

                       AutomationElement.IsInvokePatternAvailableProperty, true);

            AutomationElementCollection aec = wae.AE.FindAll(TreeScope.Subtree, cond);

            foreach (AutomationElement ae in aec)

            {

                lbox_inv_elem.Items.Add(new WrapInvoke(ae));

            }

        }

        private WrapInvoke SelectedWrapAE()

        {

            if (lbox_process.SelectedIndex == -1)

            {

                return null;

            }

 

            WrapProcess wp = lbox_process.SelectedItem as WrapProcess;

            AutomationElement ae = wp.RootElement;

            return new WrapInvoke(ae);

        }

        private void OnUIAutomationEvent(object src, AutomationEventArgs e)

        {

            AutomationElement ae = src as AutomationElement;

            AddEvent(ae);

        }

 

        delegate void MyDele(AutomationElement ae);

        private void AddEvent(AutomationElement ae)

        {

            if (lbox_history.InvokeRequired)

            {

                object[] objs = new object[1] { ae };

                lbox_history.Invoke(new MyDele(AddEvent), objs);

            }

            else

            {

                lbox_history.Items.Add(ae.Current.Name+" 클릭");

            }

        }

 

        private void lbox_inv_elem_SelectedIndexChanged(object sender, EventArgs e)

        {

            if (lbox_inv_elem.SelectedIndex == -1)

            {

                return;

            }

            WrapInvoke winvoke = lbox_inv_elem.SelectedItem as WrapInvoke;

            winvoke.Invoke();

        }

    }

}

[소스] Form1.cs


[S/W 접근성] InvokePattern

[S/W 접근성] InvokePattern 요소 제어하기 실습 (시나리오)

[S/W 접근성] InvokePattern 요소 제어하기 실습 (WrapLib 클래스 라이브러리 만들기)


반응형