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

[소프트웨어 접근성] 초점 제어기 만들기 실습

언제나휴일 2016. 5. 4. 02:02
반응형

[소프트웨어 접근성] 초점 제어기 만들기 실습


 이번에는 SetFocus 메서드를 이용하여 원하는 자동화 요소로 초점을 옮기는 프로그램을 작성합시다.



[그림] 초점 제어기 실행화면


 초점 제어기를 만들기 전에 타겟 데모 응용 프로그램을 만들기로 합시다. 타겟 데모 응용 프로그램은 12개의 버튼이 있고 1개의 Label이 있습니다. 이 프로그램은 버튼에 초점이 오면 어느 버튼에 초점이 왔는지 Label에 표시하는 응용 프로그램입니다.

 

 먼저 Windows Forms 응용 프로젝트를 생성하세요. 그리고 Form1에 버튼 12개와 Label 1개를 배치하세요. 그리고 Label의 이름 속성을 lb_focus로 설정합니다.

 

 Fom1 FormLoad 이벤트 핸들러를 추가하여 각 버튼에 초점이 왔을 때 lb_focus Label에 표시할 텍스트 속성을 변경하게 작성합시다.

private void Form1_Load(object sender, EventArgs e)

{

    button1.GotFocus += new EventHandler(Button_GotFocus);

    button2.GotFocus += new EventHandler(Button_GotFocus);

    ...중략...

    button12.GotFocus += new EventHandler(Button_GotFocus);

}

 버튼에 초점이 왔을 때 이벤트 핸들러에서는 이벤트를 발생한 원인인 sender Button 형식으로 참조 연산한 후에 lb_focus Text 속성을 버튼의 속성을 대입합니다.

void Button_GotFocus(object sender, EventArgs e)

{

    Button btn = sender as Button;

    lb_focus.Text = btn.Text;

}


초점제어기.zip

* 사용하는 개발 도구의 버전에 맞게 변환하세요. 그리고 UI 자동화 관련 .NET 어셈블리를 참조 추가하셔야 합니다. *


using System;

using System.Windows.Forms;

 

namespace DemoTarget

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

        }

 

        private void Form1_Load(object sender, EventArgs e)

        {

            button1.GotFocus += new EventHandler(Button_GotFocus);

            button2.GotFocus += new EventHandler(Button_GotFocus);

            button3.GotFocus += new EventHandler(Button_GotFocus);

            button4.GotFocus += new EventHandler(Button_GotFocus);

            button5.GotFocus += new EventHandler(Button_GotFocus);

            button6.GotFocus += new EventHandler(Button_GotFocus);

            button7.GotFocus += new EventHandler(Button_GotFocus);

            button8.GotFocus += new EventHandler(Button_GotFocus);

            button9.GotFocus += new EventHandler(Button_GotFocus);

            button10.GotFocus += new EventHandler(Button_GotFocus);

            button11.GotFocus += new EventHandler(Button_GotFocus);

            button12.GotFocus += new EventHandler(Button_GotFocus);

        }

 

        void Button_GotFocus(object sender, EventArgs e)

        {

            Button btn = sender as Button;

            lb_focus.Text = btn.Text;

        }

    }

}

[소스] Form1.cs


 이제 초점 제어기를 작성합시다. Windows Forms 응용 프로젝트를 생성하시고 Form1의 화면 배치합니다.


[그림] 초점 제어기 Form1 화면 배치

 

번호

컨트롤 타입

컨트롤 명

설명

1

Button

btn_refresh_pro

프로세스 목록 새로고침

2

ListBox

lbox_pro

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

3

ListBox

lbox_ae

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

4

Label

lb_name

선택한 자동화 요소의 이름

5

Label

lb_cname

선택한 자동화 요소의 지역화 컨트롤 타입

6

Label

lb_rect

선택한 자동화 요소의 사각 영역

[] Form1의 컨트롤

 

 

 컨트롤 제어기의 동작 원리는 다음과 같습니다. 만약 프로세스 목록 새로고침 버튼을 누르면 프로세스 목록을 조사하여 메인 윈도우 핸들이 유효한 프로세스를 lbox_pro 목록에 추가합니다. 그리고 lbox_pro 목록의 선택 항목이 바뀔 때마다 선택한 프로세스의 메인 윈도우의 서브 트리에 있는 자동화 요소 중에 초점을 소유할 수 있는 버튼 컨트롤을 lbox_ae 목록에 추가합니다.

 

 만약 lbox_ae 목록의 선택 항목이 바뀌면 선택한 자동화 요소의 정보를 각 Label에 표시하고 초점을 이동시킵니다.

 

 이 프로그램에서는 효과적으로 프로세스와 자동화 요소를 ListBox에 추가하고 선택하였을 때 원하는 작업을 하기 위하여 WrapProcess WrapAE 클래스를 정의합시다.

 

 먼저 WrapProcess 클래스를 추가합시다.

class WrapProcess

{

 Process 를 래핑할 것이며 이를 위해 자동 속성을 제공합시다.

    public Process Process{    get;    private set;    }

 ListBox 목록에 표시할 Title 속성을 제공합시다.

    public string Title

    {

        get

        {

            return Process.ProcessName+":"+ Process.MainWindowTitle;

        }

    }

 프로세스의 메인 윈도우에 관한 자동화 요소를 제공합시다.

    public AutomationElement RootElement

    {

        get

        {

 프로세스에 메인 윈도우가 유효하지 않으면 null을 반환합니다.

            if (Process.MainWindowHandle == IntPtr.Zero) {    return null;    }

 AutomationElement 클래스의 정적 메서드 FromHandler를 호출하여 메인 윈도우에 관한 자동화 요소를 반환합니다.

            return AutomationElement.FromHandle(Process.MainWindowHandle);

        }

    }

 

 생성자에서는 프로세스 개체를 입력받아 Process 속성에 대입합니다.

    public WrapProcess(Process process)

    {

        Process = process;

    }

 ToString 메서드를 override 하여 ListBox에 타이틀을 표시할 수 있게 정의합니다.

    public override string ToString()

    {

        return Title;

    }

}

 

using System;

using System.Diagnostics;

using System.Windows.Automation;

namespace 초점제어기

{

    class WrapProcess

    {

        public Process Process{    get;    private set;    }

        public string Title

        {

            get

            {

                return Process.ProcessName+":"+ Process.MainWindowTitle;

            }

        }

        public AutomationElement RootElement

        {

            get

            {

                if (Process.MainWindowHandle == IntPtr.Zero)

                {

                    return null;

                }

                return AutomationElement.FromHandle(Process.MainWindowHandle);

            }

        }

        public WrapProcess(Process process)

        {

            Process = process;

        }

        public override string ToString()

        {

            return Title;

        }

    }

}

[소스] WrapProcess.cs


 이번에는 AutomationElement를 래핑한 WrapAE 클래스를 정의합시다.

class WrapAE

{

 AutomationElement를 래핑할 것이며 이를 자동 속성으로 제공합시다.

    public AutomationElement AE{    get;    private set;    }

 자동화 요소의 이름과 지역화 컨트롤 타입, 사각 영역을 속성으로 제공합시다.

    public string Name

    {

        get

        {

 자동화 요소 이름은 AE. Current 속성의 Name 속성을 그대로 By Pass 합니다.

            return AE.Current.Name;

        }

    }

    public string ControlType

    {

        get

        {

 컨트롤 타입은 지역화 컨트롤 타입을 By Pass할게요.

            return AE.Current.LocalizedControlType;

        }

    }

    public string Boundary

    {

        get

        {

 사각 영역은 AE.Current BoundingRectangle 속성을 ToString 메서드를 사용하여 문자열 형태로 By Pass 합시다.

            return AE.Current.BoundingRectangle.ToString();

        }

    }

 생성자에서는 자동화 요소를 입력 인자로 받아 속성에 대입합니다.

    public WrapAE(AutomationElement ae)

    {

        AE = ae;

    }

    public override string ToString()

    {

        return Name;

    }

}

 

using System.Windows.Automation;

namespace 초점제어기

{

    class WrapAE

    {

        public AutomationElement AE{    get;    private set;    }

        public string Name

        {

            get

            {

                return AE.Current.Name;

            }

        }

        public string ControlType

        {

            get

            {

                return AE.Current.LocalizedControlType;

            }

        }

        public string Boundary

        {

            get

            {

                return AE.Current.BoundingRectangle.ToString();

            }

        }

        public WrapAE(AutomationElement ae)

        {

            AE = ae;

        }

        public override string ToString()

        {

            return Name;

        }

    }

}

[소스] WrapAE.cs


 이제 Form1을 작업할 차례입니다. 먼저 프로세스 목록 새로고침 버튼 클릭 이벤트 핸들러를 추가하세요.

private void btn_refresh_pro_Click(object sender, EventArgs e)

{

 먼저 프로세스 리스트 상자의 항목을 지웁니다.

    lbox_pro.Items.Clear();

 모든 프로세스 목록을 얻어옵니다.

    Process[] proes= Process.GetProcesses();

 얻어온 각 프로세스로 WrapProcess 개체를 생성하여 프로세스 리스트 상자에 추가합니다. 단 프로세스의 메인 윈도우 핸들이 유효하지 않는 것은 필터링 합니다.

    foreach (Process pro in proes)

    {

        if (pro.MainWindowHandle != IntPtr.Zero)

        {

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

        }

    }

}

 프로세스 리스트 상자의 선택한 인덱스 변경 이벤트 핸들러를 추가합니다.

private void lbox_pro_SelectedIndexChanged(object sender, EventArgs e)

{

 선택한 항목이 -1이면 선택 항목이 없는 것이므로 리턴합니다.

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

 

 자동화 요소 항목을 지웁니다.

    lbox_ae.Items.Clear();

 프로세스 리스트 상자의 선택한 항목을 WrapProcess로 참조합니다.

    WrapProcess wp = lbox_pro.SelectedItem as WrapProcess;

 선택한 프로세스의 최상위 루트 요소를 참조합니다.

    AutomationElement ae = wp.RootElement;

 여기에서는 초점 제어기이므로 초점을 소유할 수 있는 자동화 요소만 탐색할 것입니다. 이를 위한 컨디션 개체를 생성합니다.

     Condition con_kf = new PropertyCondition(

             AutomationElement.IsKeyboardFocusableProperty,true);

 버튼 컨트롤만 탐색하기 위한 컨트롤 개체도 생성합니다.

    Condition con_but = new PropertyCondition(AutomationElement.ControlTypeProperty,

            ControlType.Button);

 그리고 생성한 두 컨트롤 개체를 입력 인자로 AndCondition 개체를 생성합니다.

    Condition con = new AndCondition(con_kf,con_but);

 루트 요소의 서브 트리에서 컨디션 개체를 전달하여 자동화 요소를 탐색합니다.

    AutomationElementCollection aec = ae.FindAll(TreeScope.Subtree, con);

 탐색한 자동화 요소 개체를 WrapAE 개체로 래핑하여 리스트 상자 항목에 추가합니다.

    foreach (AutomationElement sae in aec)

    {

        lbox_ae.Items.Add(new WrapAE(sae));

    }

}

 

 자동화 요소 리스트 상자의 선택 인덱스 변경 이벤트 핸들러를 추가합니다.

private void lbox_ae_SelectedIndexChanged(object sender, EventArgs e)

{

 만약 선택 항목이 없으면 리턴합니다.

    if (lbox_ae.SelectedIndex == -1)

    {

        return;

    }

 

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

    WrapAE wae = lbox_ae.SelectedItem as WrapAE;

 

 선택한 요소의 속성으로 레이블의 Text 속성에 대입합니다.

    lb_name.Text = wae.Name;

    lb_cname.Text = wae.ControlType;

    lb_rect.Text = wae.Boundary;

 

 선택한 자동화 요소의 SetFocus 메서드를 호출하여 초점을 제어합니다.

    wae.AE.SetFocus();

}

 

using System;

using System.Windows.Forms;

using System.Diagnostics;

using System.Windows.Automation;

 

namespace 초점제어기

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

        }

 

        private void btn_refresh_pro_Click(object sender, EventArgs e)

        {

            lbox_pro.Items.Clear();

            Process[] proes= Process.GetProcesses();

            foreach (Process pro in proes)

            {

                if (pro.MainWindowHandle != IntPtr.Zero)

                {

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

                }

            }

        }

 

        private void lbox_pro_SelectedIndexChanged(object sender, EventArgs e)

        {

            if (lbox_pro.SelectedIndex == -1)

            {

                return;

            }

 

            lbox_ae.Items.Clear();

            WrapProcess wp = lbox_pro.SelectedItem as WrapProcess;

            AutomationElement ae = wp.RootElement;

 

            Condition con_kf = new PropertyCondition(

                          AutomationElement.IsKeyboardFocusableProperty,true);

            Condition con_but = new PropertyCondition(

                       AutomationElement.ControlTypeProperty, ControlType.Button);

            Condition con = new AndCondition(con_kf,con_but);

 

            AutomationElementCollection aec = ae.FindAll(TreeScope.Subtree, con);

            foreach (AutomationElement sae in aec)

            {

                lbox_ae.Items.Add(new WrapAE(sae));

            } 

        }

 

        private void lbox_ae_SelectedIndexChanged(object sender, EventArgs e)

        {

            if (lbox_ae.SelectedIndex == -1)

            {

                return;

            }

            WrapAE wae = lbox_ae.SelectedItem as WrapAE;

            lb_name.Text = wae.Name;

            lb_cname.Text = wae.ControlType;

            lb_rect.Text = wae.Boundary;

            wae.AE.SetFocus();

        }

    }

}

[소스] Form1.cs


너와 나의 연결고리 "공감"

반응형