8) RangeValuePattern, 9) 실습: Range 값 제어하기

8) RangeValuePattern

RangeValuePattern은 특정 범위 내에서 값을 설정할 수 있는 컨트롤의 패턴입니다.

System.Object
System.Windows.Automation.BasePattern
System.Windows.Automation.RangeValuePattern

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

RangeValuePattern의 Current 및 Cached 속성으로 RangeValuePatternInformation 구조체를 접근할 수 있습니다. 그리고 RangeValuePatternInformation 구조체에는 범위의 최소 및 최대 값과 현재 값 등을 가져오기 할 수 있는 속성을 제공합니다.

속성명설명
IsReadOnly읽기 전용인지 여부
LargeChange더하거나 뺄 때 사용하는 큰 변경 값
Maximum범위의 최대 값
Minimum범위의 최소 값
SmallChange더하거나 뺄 때 사용하는 작은 변경 값
Value현재 값

[표 5.9] RangeValuePatternInformation 구조체의 속성

그리고 RangeValuePattern 클래스에서는 SetValue 메서드를 제공하여 프로그램 방식으로 제어할 수 있습니다.

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

멤버명설명
IsReadOnlyPropertyIsReadOnly 속성 식별
LargeChangePropertyLargeChange 속성 식별
MaximumPropertyMaximum 속성 식별
MinimumPropertyMinimum 속성 식별
PatternRangeValuePattern 식별
SmallChangePropertySmallChange 속성 식별
ValuePropertyValue 속성 식별

[표 5.10] RangeValuePattern 클래스의 정적 멤버 필드

9) 실습: Range 값 제어하기

RangeValuePattern 개체를 이용하여 다른 프로세스를 제어하는 프로그램을 만들어 봅시다. 여기에서는 간단하게 데모 타겟 프로그램을 만든 후에 제어하는 프로그램을 만듭시다.

[그림 5.5] 실행화면
[그림 5.5] 실행화면

먼저 데모 타겟은 단순히 메인 Form에 TrackBar 컨트롤을 배치하는 것이 끝입니다. 별도의 소스 코드를 편집하거나 설정할 필요는 없습니다.

이제 Range 값 제어기를 만들기 위해 Windows Forms 응용 프로그램 프로젝트를 추가한 후 자식 컨트롤을 배치하세요.

[그림 5.6] 컨트롤 배치
[그림 5.6] 컨트롤 배치
번호컨트롤 타입컨트롤 명설명
1Buttonbtn_target_start타켓 프로그램 실행 버튼
2Labellb_msg메시지 표시
3Labellb_countdown대기 시간 표시
4TrackBartbar_control제어에 사용할 TrackBar
5Timertimer1카운트 다운에 사용

[표 5.11] Form1의 컨트롤 배치

멤버 필드로 데모 타겟 프로그램의 실행 파일명을 설정합니다.

string target = "Demo Range 값 제어대상.exe";

데모 타겟 프로그램이 실행하여 Main 윈도우가 뜨고 난 후에 자동화 요소를 수집할 것입니다. 차후에는 자동화 이벤트를 이용하지만 여기에서는 의도적으로 10초간 대기하기로 할게요. 이를 위해 카운트 다운하기 위한 멤버 필드를 선언하고 10으로 초기화합니다.

int cd_value=10;

타겟 프로그램의 프로세스 개체를 참조할 멤버 필드를 선언합니다.

Process process=null;

제어할 컨트롤의 RangeValuePattern 개체를 참조할 멤버 필드를 선언합니다.

RangeValuePattern rvp=null;

데모 타겟 프로그램 실행 버튼의 클릭 이벤트 핸들러를 추가합시다.

private void btn_target_start_Click(object sender, EventArgs e)
{
 타겟 프로세스를 시작합니다.
    process = Process.Start(target);
 그리고 카운트 다운 멤버 필드 값을 10으로 설정한 후 타이머를 가동합니다.
    cd_value = 10;
    timer1.Start();
 카운트 다운 시간과 대기 메시지를 설정하고 버튼을 비활성화합니다.
    lb_countdown.Text = cd_value.ToString();
    lb_msg.Text = "잠시 대기해 주세요.";
    btn_target_start.Enabled = false;
}

타이머 이벤트 핸드러를 등록합니다.

private void timer1_Tick(object sender, EventArgs e)
{
 타이머 이벤트 핸들러에서는 카운트 다운을 감소하고 이를 표시하는 작업을 수행합니다.
    cd_value--;
    lb_countdown.Text = cd_value.ToString();
    if (cd_value == 0)
    {
 만약 카운트 다운 타밍이 만료하면 타이머를 멈추고 메시지를 빈 문자열로 설정합니다. 그리고 RangeValuePattern 의 자동화 요소를 탐색합니다. 탐색 부분은 메서드로 정의하여 사용합시다.
        timer1.Stop();
        lb_msg.Text = string.Empty;
        lb_countdown.Text = string.Empty;
        SearchRangeValuePattern();
    }
}

RangeValuePattern을 탐색하는 메서드를 정의합시다.

private void SearchRangeValuePattern()
{
 만약 프로세스 개체가 null이거나 Main 창의 Handle이 Zero일 때는 제대로 프로세스를 동작하지 않거나 아직 메인 창이 뜨지 않을 때입니다. 이 때는 오류 메시지를 메시지 창을 통해 사용자에게 알려줍시다.
    if ( (process == null)||(process.MainWindowHandle == IntPtr.Zero) )
    {
        MessageBox.Show("죄송합니다. 다시 실행해 주세요.");
        btn_target_start.Enabled =false;
        return;
    }
 이제 프로세스의 Main 창 Handle을 인다로 자동화 요소를 참조합니다. 이를 위해 자동화 기술에서는 AutomationElement 형식에 정적 메서드 FromHandle을 제공하고 있습니다.
    AutomationElement ae = AutomationElement.FromHandle(process.MainWindowHandle);
 탐색 조건 개체를 생성합니다. 여기에서는 RangeValuePattern을 찾아야 합니다. 자동화 기술에서는 AutomationElement의 정적 속성 IsRangeValuePatternAvailableProperty를 제공하여 조건 개체를 만들 때 인자로 사용할 수 있습니다.
    Condition cond = new PropertyCondition(
              AutomationElement.IsRangeValuePatternAvailableProperty, true);
 자동화 요소의 FindAll 멤버 메서드에 탐색 범위를 서브 트리로 지정하고 앞에서 생성한 조건 개체를 전달합니다. FindAll 멤버 메서드에서는 조건에 맞는 개체들을 찾아 컬렉션을 반환합니다.
     AutomationElementCollection aec = ae.FindAll(TreeScope.Subtree, cond);
    try
    {
 이번 실습에서는 타겟 프로그램에 의도적으로 RangeValuePattern인 컨트롤을 하나 추가하였습니다.
 컬렉션에 있는 개체를 전수 조사하지 않고 인덱스 0에 있는 개체에 직접 접근하기로 할게요.
 탐색 결과로 받은 자동화 요소 컬렉션의 인덱스 0에 있는 개체가 RangePattern으로 참조합니다.
 이를 위해 자동화 요소 개체에서는 GetCurrentPattern 메서드를 통해 컨트롤 패턴 개체를 참조할 수 있게 기능을 제공합니다.
 이 때 원하는 패턴을 접근하기 위해 RangeValuePattern의 정적 속성인 Pattern을 입력 인자로 사용합니다.
 그리고 실제 자동화 요소 개체가 요청한 컨트롤 패턴이 아닐 수 있기 때문에 as 연산을 통해 참조합니다.
        rvp = aec[0].GetCurrentPattern(RangeValuePattern.Pattern) as RangeValuePattern;
 그리고 트랙바 컨트롤을 활성화 상태로 설정하고 자동화 요소의 Current 속성의 Minimum 속성과 Maximum 속성과 Value 속성을 참조하여 트랙바의 속성을 설정합니다.
        tbar_control.Enabled = true;
        tbar_control.Minimum = (int)rvp.Current.Minimum;
        tbar_control.Maximum = (int)rvp.Current.Maximum;
        tbar_control.Value = (int)rvp.Current.Value;
    }
    catch { }
}

트랙바의 스크롤 이벤트 핸들러를 등록하여 Value 속성으로 RanageValuePattern 개체의 값을 설정합니다.

private void tbar_control_Scroll(object sender, EventArgs e)
{
    rvp.SetValue(tbar_control.Value);
}

빌드한 후 타겟 응용 프로그램을 제어기 실행 파일과 같은 위치로 옮긴 후 테스트 하세요.

using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.Windows.Automation;

namespace Range_값_제어하기
{
    public partial class Form1 : Form
    {
        string target = "Demo Range 값 제어대상.exe";
        int cd_value=10;
        Process process=null;
        RangeValuePattern rvp=null;
        public Form1()
        {
            InitializeComponent();
        }
        private void btn_target_start_Click(object sender, EventArgs e)
        {
            process = Process.Start(target);
            cd_value = 10;
            timer1.Start();
            lb_countdown.Text = cd_value.ToString();
            lb_msg.Text = "잠시 대기해 주세요.";
            btn_target_start.Enabled = false;
        }
        private void timer1_Tick(object sender, EventArgs e)
        {
            cd_value--;
            lb_countdown.Text = cd_value.ToString();
            if (cd_value == 0)
            {
                timer1.Stop();
                lb_msg.Text = string.Empty;
                lb_countdown.Text = string.Empty;
                SearchRangeValuePattern();
            }
        }

        private void SearchRangeValuePattern()
        {
            if ( (process == null)||(process.MainWindowHandle == IntPtr.Zero) )
            {
                MessageBox.Show("죄송합니다. 다시 실행해 주세요.");
                btn_target_start.Enabled =false;
                return;
            }
            AutomationElement ae = AutomationElement.FromHandle(
                                        process.MainWindowHandle);

            Condition cond = new PropertyCondition(
                     AutomationElement.IsRangeValuePatternAvailableProperty, true);
            AutomationElementCollection aec = ae.FindAll(TreeScope.Subtree, cond);

            try
            {
                rvp = aec[0].GetCurrentPattern(RangeValuePattern.Pattern)
                          as RangeValuePattern;
                tbar_control.Enabled = true;
                tbar_control.Minimum = (int)rvp.Current.Minimum;
                tbar_control.Maximum = (int)rvp.Current.Maximum;
                tbar_control.Value = (int)rvp.Current.Value;
                
            }
            catch { }
        }
        private void tbar_control_Scroll(object sender, EventArgs e)
        {
            rvp.SetValue(tbar_control.Value);
        }
    }
}

[소스 5.4] Form1.cs