6) 실습: 초점 제어기

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

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

 초점 제어기를 만들기 전에 타겟 데모 응용 프로그램을 만들기로 합시다. 타겟 데모 응용 프로그램은 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;
}

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;
        }
    }
}

[소스 3.4] Form1.cs

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

[그림 3.6] 초점 제어기 Form1 화면 배치
번호컨트롤 타입컨트롤 명설명
1Buttonbtn_refresh_pro프로세스 목록 새로고침
2ListBoxlbox_pro프로세스 목록을 보여주는 리스트 상자
3ListBoxlbox_ae선택한 프로세스의 자동화 요소 목록을 보여주는 상자
4Labellb_name선택한 자동화 요소의 이름
5Labellb_cname선택한 자동화 요소의 지역화 컨트롤 타입
6Labellb_rect선택한 자동화 요소의 사각 영역

[표 3.5] 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;
        }
    }
}

[소스 3.5] 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;
        }
    }
}

[소스 3.6] 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();
        }
    }
}

[소스 3.7] Form1.cs