20) WindowPattern, 21) 실습: 윈도우 옵저버

20) WindowPattern

윈도우 패턴은 창 기능을 하는 컨트롤에서 지원하는 패턴입니다.

System.Object
System.Windows.Automation.BasePattern
System.Windows.Automation.WindowPattern

네임스페이스:  System.Windows.Automation
어셈블리:  UIAutomationClient(UIAutomationClient.dll)

WindowPattern의 Current 및 Cached 속성으로 WindowPatternInformation 구조체를 접근할 수 있습니다. 그리고 WindowPatternInformation 구조체에는 최대화, 최소화 가능 여부 및 모달 대화상자인지 TopMost 창인지 등을 가져오기 할 수 있는 속성을 제공합니다.

속성명설명
CanMaximize최대화 가능 여부
CanMinimize최소화 가능 여부
IsModal모달 대화상자인지 여부
IsTopMostTop Most 창인지 여부
WindowInteractionStateWindowInteraction 상태
WindowVisualStateWindowVisual 상태

[표 5.27] WindowPatternInformation 구조체의 속성

그리고 WindowPattern 클래스에서는 창의 VisualState를 설정하고 창의 프로세스가 IDLE 상태로 전이(프로세스 종료)할 때까지 대기하는 메서드를 제공합니다.

public void SetWindowVisualState( WindowVisualState state)

public bool WaitForInputIdle(int milliseconds)

또한 WindowPattern 클래스에서는 식별에 사용하는 정적 멤버 필드를 제공하고 있습니다.

멤버명설명
CanMamimizePropertyCanMamimize 속성 식별
CanMinimizePropertyCanMinimize 속성 식별
IsModalPropertyIsModal 속성 식별
IsTopmostPropertyIsTopmost 속성 식별
PatternWindowPattern 식별
WindowClosedEvent창이 닫힐 때 이벤트
WindowInteractionStatePropertyWindowInteractionState 속성 식별
WindowOpendEvent창이 열릴 때 이벤트
WindowVisualStatePropertyWindowVisualState 속성 식별

[표 5.28] WindowPattern 클래스의 정적 멤버 필드

21) 실습: 윈도우 옵저버

이번에는 컴퓨터 시스템에 윈도우가 열고 닫는 것을 관찰하는 윈도우 옵저버를 만들어 봅시다.

[그림 5.10] 실행화면

Form1에는 ListBox 컨트롤(Name: lbox_event)을 추가하였고 Dock속성을 Fill로 주었습니다.

윈도우 닫힘 자동화 이벤트에서는 닫힌 윈도우의 자동화 요소를 전달하지 않습니다. 이를 처리하기 위해 자동화 요소를 래핑한 클래스를 만들어서 내부에서 이를 처리할게요.

public class EAE

{

윈도우 닫힘 이벤트를 멤버로 추가합니다.

    public event AutomationEventHandler OnClose;

자동화 요소 이름 가져오기 속성을 제공합시다.

    public string Name
    {
        get;
        private set;
    }

생성자에서는 자동화 요소 개체를 입력 인자로 받습니다.

public EAE( AutomationElement ae)
{
 윈도우 창 닫힘 이벤트 핸들러를 생성합니다.
    AutomationEventHandler eh_close = new AutomationEventHandler(OnWindowClose);
 생성한 이벤트 핸들러를 등록합니다.
 이벤트는 WindowPattern의 WindowClosedEvent 속성을 사용하며 TreeScope는 자기 자신인 Element로 전달합니다.
    Automation.AddAutomationEventHandler(
            WindowPattern.WindowClosedEvent, ae, TreeScope.Element, eh_close);
 자동화 요소 개체의 현재 이름을 Name 속성에 설정합니다.
    Name = ae.Current.Name;
}

창 닫힘 이벤트 핸들러를 작성합시다.

private void OnWindowClose(object src, AutomationEventArgs e)
{
 만약 OnClose 이벤트 핸들러가 null이 아니면 이벤트 인자를 전달해 줍니다.
 이 때 첫번째 인자로 Name 속성을 전달하기로 할게요.
    if (OnClose != null)
    {
        OnClose(Name, e);
    }
}

Form1의 Laod 이벤트 핸드러를 등록하세요.

private void Form1_Load(object sender, EventArgs e)
{
 AutomationElement 클래스이 정적 메서드로 RootElement를 참조합니다.
    AutomationElement ae = AutomationElement.RootElement;
 윈도우 창이 열릴 때 자동화 이벤트가 발생하면 처리할 자동화 이벤트 개체를 하나 생성합니다.
    AutomationEventHandler eh_open = new AutomationEventHandler(OnWindowOpen);
 그리고 이벤트 핸들러를 등록합니다.
 이벤트 핸들러를 등록할 때 이벤트 종류는 WindowPattern 클래스의 정적 속성 WindowOpenedEvent이며 범위는 Subtree를 전달합니다.
    Automation.AddAutomationEventHandler(
        WindowPattern.WindowOpenedEvent, ae, TreeScope.Subtree, eh_open);
}

OnWindowOpen 이벤트 핸들러를 구현합시다.

private void OnWindowOpen(object src, AutomationEventArgs e)
{
 이벤트 핸들러의 첫번째 인자를 자동화 요소로 참조합니다.
    AutomationElement se = src as AutomationElement;
 윈도우 닫힘 이벤트 처리를 위해 래핑한 개체를 생성합니다.
    EAE eae = new EAE(se);
 래핑한 개체의 OnClose 이벤트 핸들러를 등록하세요.
    eae.OnClose += new AutomationEventHandler(OnWindowClose);
 리스트 상자에 메시지를 추가합니다. 이 부분은 별도의 메서드로 작성할게요.
    AddMsg(se.Current.Name+"열림");
}

OnWindowClose 이벤트 핸드러를 등록합시다.

private void OnWindowClose(object src, AutomationEventArgs e)
{
 리스트 상자에 메시지를 추가하는 AddMsg 메서드를 호출합니다.
    string name = src.ToString();
    AddMsg(name+"닫힘");
}

리스트 상자에 메시지를 추가하는 부분에서 크로스 스레드 문제가 발생합니다. 리스트 상자를 생성한 스레드와 윈도우 창이 닫힘을 전달하는 스레드가 달라서 발생하는 문제입니다. 이를 해결하기 위해 AddMsg와 시놉시스가 같은 대리자를 정의합니다.

delegate void AddMsgDele(string msg);

AddMsg 메서드를 정의합시다.

private void AddMsg(string msg)
{
 만약 lbox_event 개체의 InvokeRequired 속성이 참이면 현재 스레드는 크로스 스레드입니다.
    if (lbox_event.InvokeRequired)
    {
        lbox_event.Invoke(new AddMsgDele(AddMsg), new object[] { msg });
    }
    else{    lbox_event.Items.Add(msg);    }
}
using System;
using System.Windows.Forms;
using System.Windows.Automation;
namespace 윈도우_옵저버
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            AutomationElement ae = AutomationElement.RootElement;
            AutomationEventHandler eh_open = new AutomationEventHandler(OnWindowOpen);
            Automation.AddAutomationEventHandler(
                WindowPattern.WindowOpenedEvent, ae, TreeScope.Subtree, eh_open);            
        }
        private void OnWindowOpen(object src, AutomationEventArgs e)
        {
            AutomationElement se;
            se = src as AutomationElement;
            EAE eae = new EAE(se);
            eae.OnClose += new AutomationEventHandler(OnWindowClose);
            AddMsg(se.Current.Name+"열림");
        }
        private void OnWindowClose(object src, AutomationEventArgs e)
        {
            string name = src.ToString();
            AddMsg(name+"닫힘");
        }
        delegate void AddMsgDele(string msg);
        private void AddMsg(string msg)
        {
            if (lbox_event.InvokeRequired)
            {
                lbox_event.Invoke(new AddMsgDele(AddMsg), new object[] { msg });
            }
            else
            {
                lbox_event.Items.Add(msg);
            }
        }
    }
    public class EAE
    {
        public event AutomationEventHandler OnClose; 
        public string Name
        {
            get;
            private set;
        }
        public EAE( AutomationElement ae)
        {            
            AutomationEventHandler eh_close = new AutomationEventHandler(OnWindowClose);
            Automation.AddAutomationEventHandler(
                WindowPattern.WindowClosedEvent, ae, TreeScope.Element, eh_close);
            Name = ae.Current.Name;
        }
        private void OnWindowClose(object src, AutomationEventArgs e)
        {
            if (OnClose != null)
            {
                OnClose(Name, e);
            }
        }
    }
}

[소스 5.7] Form1.cs