접근성 평가 프로젝트의 자동화 요소를 조사하고 관리하는 AccEvalProject 클래스를 정의합시다. 이 부분이 전체 프로그램의 핵심적인 역할을 하는 부분입니다.
자동화 요소를 발견할 때 발견한 개수를 이벤트로 전달하기 위한 간단한 대리자를 정의합시다.
public delegate void AddFindElementDele(int cnt); public class AccEvalProject {
AccEvalProject에서는 자동화 요소의 계층 구조를 파악할 수 있어야 합니다. 이를 위해 사전 컬렉션을 멤버로 선언합시다.
Dictionary<string, TreeNode> node_dic = null;
평가가 끝났을 때 이를 구동하는 이벤트 핸들러를 등록하기 위한 이벤트를 선언합니다.
public event EventHandler EndEvalProject = null;
자동화 속성이 이동할 때 프로그램 방식의 하이라이트도 이동해야 합니다. 이를 위한 이벤트를 선언합니다.
public event AutomationPropertyChangedEventHandler AEMoved = null;
자동화 속성 변경 이벤트 핸들러와 닫힐 때 처리를 위한 이벤트 핸들러 멤버를 선언합니다.
AutomationPropertyChangedEventHandler apceh = null; AutomationEventHandler close_eventHandler = null;
최상위 자동화 요소를 기억할 멤버를 선언합시다.
AutomationElement mae = null;
평가 프로젝트 제목을 속성으로 제공합시다.
public string Title{ get; private set; }
자동화 요소 개수를 속성으로 제공합시다.
public int UICount { get{ return Table.Rows.Count; } }
평가 테이블을 속성으로 제공합시다. 평가 테이블은 평가 내용을 XML로 저장하기 위한 개체로 XML로 기록하는 메서드를 제공하는 ADO.NET 기술의 클래스 DataTable 형식을 사용합시다.
public DataTable Table{ get; private set; }
평가 대상 프로세스도 속성으로 제공합니다.
public EHProcess EHProcess{ get; private set; }
평가 대상 프로세스의 메인 창의 윈도우 핸들도 속성으로 제공합시다.
public IntPtr MainWndHandle { get{ return EHProcess.MainHandle; } }
계층화 구조의 루트 노드를 속성으로 제공합시다.
public TreeNode Root{ get; private set; }
대상 프로세스의 메인 창의 이미지도 속성으로 제공합시다.
public Image Image{ get; private set; }
사전 개체에서 키로 트리 노드를 가져오기 할 수 있게 속성을 제공합시다.
public TreeNode this[string key] { get{ return node_dic[key]; } }
생성자 메서드에서는 프로젝트 이름, 데이터 테이블, 평가 대상 프로세스 개체를 입력 인자로 받습니다.
internal AccEvalProject(string title, DataTable dt, EHProcess ehprocess) {
생성자에서는 입력 인자로 속성을 설정합니다.
Title = title; Table = dt; EHProcess = ehprocess;
그리고 계층화 정보에 사용할 트리 노드 개체를 기억할 사전 개체를 생성합니다.
node_dic = new Dictionary<string, TreeNode>(); }
프로세스 모드를 초기화하는 메서드를 제공합시다. 이번 프로젝트는 프로세스 모드만 제공하지만 보다 효과적인 평가 도구로 만들려면 포커스 모드와 창 모드 등을 제공하는 것이 좋습니다.
public void InitProcessMode(AddFindElementDele find_dele) {
프로세스의 메인 창의 자동화 요소를 찾습니다.
mae = AutomationElement.FromHandle(MainWndHandle);
속성 변경 이벤트 핸들러를 생성합니다.
apceh = new AutomationPropertyChangedEventHandler(OnPropertyChange);
메인 창의 사각 영역 속성이 변하는 것을 알기 위해 이벤트 핸들러를 등록합니다. 이를 통해 창의 위치가 바뀌어도 프로그램 방식의 하이라이트가 유효한 위치에 표시할 수 있습니다.
Automation.AddAutomationPropertyChangedEventHandler( mae, TreeScope.Element, apceh, AutomationElement.BoundingRectangleProperty);
창의 닫히는 시점을 알기 위해 이벤트 핸들러를 등록합시다. 여기에서는 평가 대상 프로젝트의 메인 창이 닫히면 프로젝트를 끝낼 것인지 메시지 창을 띄워 확인하는 구조로 만들 것입니다.
close_eventHandler = new AutomationEventHandler(OnWindowClose); Automation.AddAutomationEventHandler( WindowPattern.WindowClosedEvent, mae, TreeScope.Element, close_eventHandler);
그리고 메인 창의 하위 자동화 요소들을 검색합니다. 이 부분은 별도의 메서드로 구현합시다.
FindAETree(mae, find_dele); }
private void OnPropertyChange(object src, AutomationPropertyChangedEventArgs e) {
첫번째 입력 인자로 받은 개체를 자동화 요소 형식으로 참조합니다.
AutomationElement sourceElement = src as AutomationElement; if (e.Property == AutomationElement.BoundingRectangleProperty) {
이벤트 발생 이유가 사각 영역의 속성 변경이면 구독자에게 By Pass합니다.
if (AEMoved != null){ AEMoved(this, e); } } }
private void OnWindowClose(object src, AutomationEventArgs e) { try{AutomationElement ae = src as AutomationElement;}catch{ return; }
윈도우 창 닫힘 이벤트 핸들러에서는 평가 프로젝트를 닫습니다.
End(); }
계층 정보와 함께 자동화 요소를 검색하는 메서드를 구현합시다.
private void FindAETree(AutomationElement ae, AddFindElementDele find_dele) { try {
입력 인자로 받은 자동화 요소에 포커스를 부여하고 약간의 시간을 지연합니다. 시간을 지연하는 이유는 SetFocus 메서드를 호출하면 바로 포커스를 소유하는 것이 아니기 때문입니다.
ae.SetFocus(); Thread.Sleep(100); }
catch { try {
하위 트리에서 키보드로 초점을 받을 수 있는 자동화 요소를 탐색합니다.
AutomationElement sae=ae.FindFirst(TreeScope.Subtree, new PropertyCondition ( AutomationElement.IsKeyboardFocusableProperty, true));
그리고 탐색한 개체에 포커스를 부여하고 지연합니다. 위에서 한 작업을 예외가 발생할 때 다시 하는 이유는 인터넷 익스플로어나 일부 응용 프로그램은 메인 창이 초점을 갖지 못할 때가 있습니다. 이러한 응용들을 위해 예외처리를 한 것입니다. 물론 이 책은 자동화 기술을 소개하는 것이 주 목적이며 상용 프로그램을 만드는 것이 아니어서 예외 처리를 최소화한 것입니다.
sae.SetFocus(); Thread.Sleep(100); } catch { MessageBox.Show("선택한 윈도우에 초점을 가질 수 있는 요소가 없습니다."); } }
try {
ImageCapture 정적 클래스의 CaptureFromRect 메서드를 이용하여 메인 창 이미지를 비트맵 개체로 만들어 속성에 설정합니다.
Image = ImageCapture.CaptureFormRect(ae.Current.BoundingRectangle); } catch { }
만약 탐색 대리자를 입력 인자로 받았다면 첫번째 항목을 발견하였음을 전달합니다.
if (find_dele != null){ find_dele(1); }
그리고 EHAutoElem 개체를 생성합니다.
EHAutoElem eae = new EHAutoElem(ae);
계층화 정보를 만들어 최상위 노드를 설정합니다. 계층화 정보를 만드는 부분은 별도의 메서드로 구현합시다.
Root = MakeUIHierarchy(eae, find_dele); }
TreeNode MakeUIHierarchy(EHAutoElem eae, AddFindElementDele find_dele) {
입력 인자로 받은 요소의 ToString 메서드의 반환 문자열로 트리 노드를 생성합니다.
TreeNode sr = new TreeNode(eae.ToString());
구분하기 위한 ID를 구합니다.
string ehid = eae.GetEHID(); if (node_dic.ContainsKey(ehid) == false) {
만약 사전 개체에 없다면 사전 개체에 등록합니다.
node_dic[ehid] = sr;
트리 노드의 Tag 속성에 EHAutoElem 개체를 설정하고 반대로 EHAutoElem 개체의 Tag 속성에 트리 노드를 설정합니다. 이를 통해 트리 뷰의 트리 노드를 선택하면 태그에 기억한 정보로 대응하는 자동화 요소를 확인하거나 그 역을 수행할 수 있습니다.
sr.Tag = eae; eae.Tag = sr; FindedAutoElement(null, new FindAutoElemEventArgs(eae)); }
이미 사전 개체에 있다면 더 이상 진행하지 않고 메서드를 종료합니다.
else{ return null; }
이제 자식 요소를 탐색합니다.
Condition con = TreeWalker.RawViewWalker.Condition; AutomationElement ae = eae.AE; AutomationElementCollection aec = ae.FindAll(TreeScope.Children, con);
탐색 대리자가 존재하면 탐색한 요소 개수를 인자로 전달합니다.
if (find_dele != null){ find_dele(aec.Count); }
탐색한 자동화 요소 개체 항목마다 재귀적으로 MakeUIHierarchy 메서드를 호출합니다.
foreach (AutomationElement sae in aec) { TreeNode tr = null; if ((tr = MakeUIHierarchy(new EHAutoElem(sae), find_dele)) != null) { sr.Nodes.Add(tr); } } return sr; }
void FindedAutoElement(object send, FindAutoElemEventArgs e) {
자동화 요소를 발견하였때 인자로 전달받은 EHAutoElem 개체를 참조합니다.
EHAutoElem eae = e.EAE;
만약 테이블이 없으면 메서드를 종료합니다.
if (Table == null){ return; }
테이블의 NewRow 메서드를 호출하여 행 개체를 생성합니다.
DataRow dr = Table.NewRow();
자동화 속성 정보를 구하여 행 개체의 쉘에 설정합니다.
for (ENUM_UIProperty uip = 0; uip < ENUM_UIProperty.MAX_UIPROPERTY; uip++) { dr[uip.ToString()] = eae.GetAEProperty(uip); }
지원하는 컨트롤 패턴 정보도 마찬가지로 행 개체의 쉘에 설정합니다.
for (ENUM_CONTROL ctrl = 0; ctrl < ENUM_CONTROL.MAX_CONTROL; ctrl++) { dr[ctrl.ToString()] = eae.GetPattern(ctrl); }
어느 자동화 요소 개체의 정보인지 설정합니다.
dr["EHAutoElem"] = eae;
이렇게 설정한 행 개체를 테이블의 행 항목에 추가합니다.
Table.Rows.Add(dr); }
public void End() {
평가 프로젝트가 종료하였는지 구독자가 있다면 이벤트를 게시합니다.
if (EndEvalProject != null){ EndEvalProject(this, new EventArgs()); }
프로젝트 메인 창의 변화를 감지하기 위해 등록한 자동화 이벤트 핸들러를 해제합니다.
Automation.RemoveAutomationPropertyChangedEventHandler(mae, apceh); Automation.RemoveAutomationEventHandler(WindowPattern.WindowClosedEvent, mae, close_eventHandler); }
ToString 메서드를 재정의하여 평가 프로젝트 제목을 반환합니다.
public override string ToString() { return Title; }
저장하기 메서드를 제공합시다.
public void Save(string path) {
입력 인자로 받은 경로에 테이블 이름으로 폴더를 만들 것입니다.
string dirname = path + "/" + Table.TableName; if (Directory.Exists(dirname)) {
만약 같은 디렉토리명이 존재하면 일련 번호를 부여하여 존재하지 않는 디렉토리 이름이 나올 때까지 반복합니다. 이와 같은 기능은 하나의 프로세스를 평가하기 위해 생성한 프로젝트를 같은 폴더에 번호를 부여하여 보관할 수 있게 하는 기능입니다.
int i = 1; dirname = path + "/" + Table.TableName + "_" + i.ToString(); while (Directory.Exists(dirname)) { i++; dirname = path + "/" + Table.TableName +"_" + i.ToString(); } }
디렉토리를 생성합니다.
Directory.CreateDirectory(dirname);
그리고 평가 결과를 저장하고 모든 이미지를 저장합니다.
SaveReport(dirname); SaveImageAll(dirname); }
public void SaveReport(string dir) {
현재 디렉토리를 구하여 기억해 둡니다.
string cdir = Directory.GetCurrentDirectory();
편의성을 위해 입력 인자로 들어온 경로를 현재 디렉토리로 설정합니다.
Directory.SetCurrentDirectory(dir);
XML로 기록하기 위한 설정 개체를 생성합니다.
XmlWriterSettings settings = new XmlWriterSettings();
XML 선언부를 생략하지 않게 설정합니다.
settings.OmitXmlDeclaration = false;
문자 검사를 하지 않게 설정합니다.
settings.CheckCharacters = false;
자동 들여쓰기로 설정합니다.
settings.Indent = true;
인코딩은 디폴트로 설정할게요.
settings.Encoding = System.Text.Encoding.Default;
레포트를 기록할 XmlWriter 개체를 생성합니다.
XmlWriter writer = XmlWriter.Create(dir + "/report.xml",settings);
“Report”가 XML 문서의 루트 요소로 할 것입니다. 자식 요소로 결과를 기록합니다.
writer.WriteStartElement("Report"); writer.WriteElementString("Title", Title); writer.WriteElementString("전체_개수", UICount.ToString()); writer.WriteElementString("이름없는_UI_요소_개수", NonameCount.ToString()); WriteControlCount(writer); WriteAcceleratorKey(writer); WriteAccessKey(writer); WriteUIElement(writer); writer.WriteEndElement();
모든 정보를 기록하였으면 XmlWriter 개체를 닫고 기억해 두었던 원래 디렉토리를 현재 디레토리로 설정합니다.
writer.Close(); Directory.SetCurrentDirectory(cdir); }
이름이 없는 자동화 요소의 개수를 가져오기 할 수 있는 속성을 제공합시다. 접근성 평가에서 가장 기본적인 절차가 UI 요소를 구분할 수 있는 이름이 적절한지 확인하는 것입니다.
public int NonameCount { get { int count = 0;
테이블의 행 목록에서 이름 정보가 빈 것을 카운팅하여 반환합니다.
foreach (DataRow dr in Table.Rows) { if (dr[(int)ENUM_UIProperty.NAME].ToString() == string.Empty){ count++; } } return count; } }
Access 키 정보를 기록하는 메서드입니다.
private void WriteAccessKey(XmlWriter writer) { writer.WriteStartElement("Access키"); List<string[]> keylist = new List<string[]>();
먼저 Access 키를 갖고 있는 목록을 구합니다. 이 부분은 GetAccessKeyItemList 메서드로 구현합시다. GetAccessItemList 메서드에서는 키와 쌍을 원소로 하는 문자열 배열을 목록화한 컬렉션을 반환하게 구현할 것입니다.
keylist = GetAccessKeyItemList(); foreach (string[] key_value in keylist) {
각 목록의 키와 값을 기록합니다. 주의할 점은 요소 이름으로 (나 )와 같은 기호들은 사용할 수 없습니다. 따라서 문자열을 필터링해야 하는데 이 부분도 ConvertString 메서드를 별도로 정의하기로 할게요.
writer.WriteElementString(ConvertString(key_value[0]), ConvertString(key_value[1])); } writer.WriteEndElement(); }
public List<string[]> GetAccessKeyItemList() {
먼저 반환할 키와 값을 원소로 하는 문자열 배열 개체을 목록화할 컬렉션 개체를 생성합니다.
List<string[]> item_list = new List<string[]>();
테이블에 기록한 정보를 이용할 것입니다.
DataTable dt = Table; EHAutoElem eae = null;
테이블의 행 항목마다 반복적인 작업을 수행합니다.
foreach (DataRow dr in dt.Rows) {
EHAutoElem 쉘의 개체를 참조합니다.
eae = dr["EHAutoElem"] as EHAutoElem; if (eae.AE.Current.AccessKey != string.Empty) {
만약 개체의 AccessKey가 빈 문자열이 아니면 키와 값을 문자열 배열에 설정하여 컬렉션에 추가합니다.
string[] name_key = new string[2]; name_key[0] = eae.ToString(); name_key[1] = eae.AE.Current.AccessKey; item_list.Add(name_key); } } return item_list; }
특수 기호를 필터링하는 메서드를 구현합시다.
private string ConvertString(string str) {
먼저 문자열 길이를 구합니다.
int max = str.Length; for(int i = 0; i<max;i++) { if(char.IsLetterOrDigit(str[i])==false) {
반복해서 알파벳과 숫자 문자가 아닌 것을 발견하면 해당 요소를 제거합니다.
str = str.Remove(i,1); max = str.Length; i--; } }
변환한 문자열을 반환합니다.
return str; }
private void WriteAcceleratorKey(XmlWriter writer) {
Accelerator 키의 정보를 기록하는 메서드도 Access 키를 기록하는 메서드와 원리가 같습니다.
writer.WriteStartElement("Accelerator키"); List<string[]> keylist = new List<string[]>(); keylist = GetAcceleratorKeyItemList(); foreach (string[] key_value in keylist) { writer.WriteElementString(ConvertString(key_value[0]), key_value[1]); } writer.WriteEndElement(); }
internal List<string[]> GetAcceleratorKeyItemList() {
Accelerator 키 항목을 구하는 메서드도 Access 키 항목을 구하는 메서드와 논리가 같습니다.
List<string[]> item_list = new List<string[]>(); ...중략... return item_list; }
private void WriteControlCount(XmlWriter writer) {
컨트롤 유형 별 개수를 기록하는 메서드에서는 컨트롤 패턴 별로 개수를 구한 후에 이를 기록합니다.
writer.WriteStartElement("컨트롤_유형_별_개수"); for (ENUM_CONTROL ctrl = 0; ctrl < ENUM_CONTROL.MAX_CONTROL; ctrl++) { int count = GetControlPatternCount(ctrl); writer.WriteElementString(ctrl.ToString(), count.ToString()); } writer.WriteEndElement(); }
public int GetControlPatternCount(ENUM_CONTROL ctrl) {
컨트롤 패턴 별 개수를 세기위한 변수를 선언합니다.
int count = 0;
테이블의 행에서 NULL이 아닌 개체의 수를 구하여 반환합니다.
DataTable dt = Table; foreach (DataRow dr in dt.Rows) { if (dr[ctrl.ToString()] != DBNull.Value){ count++; } } return count; }
private void WriteUIElement(XmlWriter writer) {
모든 UI 요소 정보를 기록하는 메서드에서는 테이블의 행 의 각 UI 요소 정보를 기록합니다.
writer.WriteStartElement("UI_요소_목록"); foreach (DataRow dr in Table.Rows) {
UI 요소 정보를 기록하는 메서드는 별도로 작성합시다.
WriteUIInfo(writer, dr); } writer.WriteEndElement(); }
private void WriteUIInfo(XmlWriter writer, DataRow dr) {
속성 열거형의 마지막 열거값까지 반복하여 UI 요소의 속성 정보를 기록합니다. 앞에서 열거형을 정의한 이유가 이와 같은 기능을 간결하게 표현하기 위함입니다.
writer.WriteStartElement("UI요소"); for (ENUM_UIProperty eup = 0; eup < ENUM_UIProperty.MAX_UIPROPERTY; eup++) { string pname = eup.ToString(); string value = ConvertString(dr[eup.ToString()].ToString()); writer.WriteElementString(pname,value); } writer.WriteEndElement(); }
public void SaveImageAll(string dir) {
모든 이미지를 저장하는 메서드에서는 원본 이미지들을 저장하는 메서드와 흑백 이미지들을 저장하는 메서드를 호출합니다.
SaveImage(dir); SaveGrayImage(dir); }
public void SaveImage(string dir) {
현재 디렉토리를 기억해 두고 입력 인자로 받은 디렉토리로 현재 디렉토리를 설정합니다.
string cdir = Directory.GetCurrentDirectory(); Directory.SetCurrentDirectory(dir);
이미지 디렉토리를 생성합시다.
Directory.CreateDirectory("이미지");
이미지 파일 이름은 일련 번호를 부여합시다.
int fname_index = 1; foreach (TreeNode tn in node_dic.Values) {
사전 개체의 각 트리 노드마다 다음을 반복합니다. 트리 노드의 Tag에는 EHAutoElem 개체를 설정해 두었습니다.
EHAutoElem eae = tn.Tag as EHAutoElem; if (eae.Image != null) {
EHAutoElem 개체의 이미지를 저장합니다.
eae.Image.Save("이미지/"+fname_index.ToString() + ".bmp"); } fname_index++; }
기억해 두었던 디렉토리로 현재 디렉토리를 설정합니다.
Directory.SetCurrentDirectory(cdir); }
public void SaveGrayImage(string dir) {
흑백 이미지를 저장하는 원리도 같습니다.
string cdir = Directory.GetCurrentDirectory(); Directory.SetCurrentDirectory(dir); Directory.CreateDirectory("흑백이미지"); ...중략... } }
using System; using System.Data; using System.Windows.Automation; using System.Collections.Generic; using System.Windows.Forms; using System.Threading; using System.Drawing; using System.IO; using System.Xml; namespace 접근성_평가_도우미 { public delegate void AddFindElementDele(int cnt); public class AccEvalProject { Dictionary<string, TreeNode> node_dic = null; public event EventHandler EndEvalProject = null; public event AutomationPropertyChangedEventHandler AEMoved = null; AutomationPropertyChangedEventHandler apceh = null; AutomationEventHandler close_eventHandler = null; AutomationElement mae = null; public string Title{ get; private set; } public int UICount { get { return Table.Rows.Count; } } public DataTable Table{ get; private set; } public EHProcess EHProcess{ get; private set; } public IntPtr MainWndHandle { get{ return EHProcess.MainHandle; } } public TreeNode Root{ get; private set; } public Image Image{ get; private set; } public TreeNode this[string key] { get { return node_dic[key]; } } internal AccEvalProject(string title, DataTable dt, EHProcess ehprocess) { Title = title; Table = dt; EHProcess = ehprocess; node_dic = new Dictionary<string, TreeNode>(); } public void InitProcessMode(AddFindElementDele find_dele) { mae = AutomationElement.FromHandle(MainWndHandle); apceh = new AutomationPropertyChangedEventHandler(OnPropertyChange); Automation.AddAutomationPropertyChangedEventHandler(mae, TreeScope.Element, apceh, AutomationElement.BoundingRectangleProperty); close_eventHandler = new AutomationEventHandler(OnWindowClose); Automation.AddAutomationEventHandler(WindowPattern.WindowClosedEvent, mae, TreeScope.Element, close_eventHandler); FindAETree(mae, find_dele); } private void OnPropertyChange(object src, AutomationPropertyChangedEventArgs e) { AutomationElement sourceElement = src as AutomationElement; if (e.Property == AutomationElement.BoundingRectangleProperty) { if (AEMoved != null) { AEMoved(this, e); } } } private void OnWindowClose(object src, AutomationEventArgs e) { try { AutomationElement ae = src as AutomationElement; } catch{ return; } End(); } private void FindAETree(AutomationElement ae, AddFindElementDele find_dele) { try { ae.SetFocus(); Thread.Sleep(100); } catch { try { AutomationElement sae=ae.FindFirst(TreeScope.Subtree, new PropertyCondition (AutomationElement.IsKeyboardFocusableProperty, true)); sae.SetFocus(); Thread.Sleep(100); } catch { MessageBox.Show( "선택한 윈도우에 초점을 가질 수 있는 요소가 없습니다."); } } try { Image = ImageCapture.CaptureFormRect(ae.Current.BoundingRectangle); } catch { } if (find_dele != null) { find_dele(1); } EHAutoElem eae = new EHAutoElem(ae); Root = MakeUIHierarchy(eae, find_dele); } TreeNode MakeUIHierarchy(EHAutoElem eae, AddFindElementDele find_dele) { TreeNode sr = new TreeNode(eae.ToString()); string ehid = eae.GetEHID(); if (node_dic.ContainsKey(ehid) == false) { node_dic[ehid] = sr; sr.Tag = eae; eae.Tag = sr; FindedAutoElement(null, new FindAutoElemEventArgs(eae)); } else{ return null; } Condition con = TreeWalker.RawViewWalker.Condition; AutomationElement ae = eae.AE; AutomationElementCollection aec = ae.FindAll(TreeScope.Children, con); if (find_dele != null) { find_dele(aec.Count); } foreach (AutomationElement sae in aec) { TreeNode tr = null; if ((tr = MakeUIHierarchy(new EHAutoElem(sae), find_dele)) != null) { sr.Nodes.Add(tr); } } return sr; } void FindedAutoElement(object send, FindAutoElemEventArgs e) { EHAutoElem eae = e.EAE; if (Table == null){ return; } DataRow dr = Table.NewRow(); for (ENUM_UIProperty uip = 0; uip < ENUM_UIProperty.MAX_UIPROPERTY; uip++) { dr[uip.ToString()] = eae.GetAEProperty(uip); } for (ENUM_CONTROL ctrl = 0; ctrl < ENUM_CONTROL.MAX_CONTROL; ctrl++) { dr[ctrl.ToString()] = eae.GetPattern(ctrl); } dr["EHAutoElem"] = eae; Table.Rows.Add(dr); } public void End() { if (EndEvalProject != null){ EndEvalProject(this, new EventArgs()); } Automation.RemoveAutomationPropertyChangedEventHandler(mae, apceh); Automation.RemoveAutomationEventHandler( WindowPattern.WindowClosedEvent, mae, close_eventHandler); } public override string ToString(){ return Title; } public void Save(string path) { string dirname = path + "/" + Table.TableName; if (Directory.Exists(dirname)) { int i = 1; dirname = path + "/" + Table.TableName + "_" + i.ToString(); while (Directory.Exists(dirname)) { i++; dirname = path + "/" + Table.TableName +"_" + i.ToString(); } } Directory.CreateDirectory(dirname); SaveReport(dirname); SaveImageAll(dirname); } public void SaveReport(string dir) { string cdir = Directory.GetCurrentDirectory(); Directory.SetCurrentDirectory(dir); XmlWriterSettings settings = new XmlWriterSettings(); settings.OmitXmlDeclaration = false; settings.CheckCharacters = false; settings.Indent = true; settings.Encoding = System.Text.Encoding.Default; XmlWriter writer = XmlWriter.Create(dir + "/report.xml",settings); writer.WriteStartElement("Report"); writer.WriteElementString("Title", Title); writer.WriteElementString("전체_개수", UICount.ToString()); writer.WriteElementString("이름없는_UI_요소_개수", NonameCount.ToString()); WriteControlCount(writer); WriteAcceleratorKey(writer); WriteAccessKey(writer); WriteUIElement(writer); writer.WriteEndElement(); writer.Close(); Directory.SetCurrentDirectory(cdir); } public int NonameCount { get { int count = 0; foreach (DataRow dr in Table.Rows) { if (dr[(int)ENUM_UIProperty.NAME].ToString() == string.Empty){ count++; } } return count; } } private void WriteAccessKey(XmlWriter writer) { writer.WriteStartElement("Access키"); List<string[]> keylist = new List<string[]>(); keylist = GetAccessKeyItemList(); foreach (string[] key_value in keylist) { writer.WriteElementString(ConvertString(key_value[0]), ConvertString(key_value[1])); } writer.WriteEndElement(); } public List<string[]> GetAccessKeyItemList() { List<string[]> item_list = new List<string[]>(); DataTable dt = Table; EHAutoElem eae = null; foreach (DataRow dr in dt.Rows) { eae = dr["EHAutoElem"] as EHAutoElem; if (eae.AE.Current.AccessKey != string.Empty) { string[] name_key = new string[2]; name_key[0] = eae.ToString(); name_key[1] = eae.AE.Current.AccessKey; item_list.Add(name_key); } } return item_list; } private string ConvertString(string str) { int max = str.Length; for (int i = 0; i < max; i++) { if (char.IsLetterOrDigit(str[i]) == false) { str = str.Remove(i, 1); max = str.Length; i--; } } return str; } private void WriteAcceleratorKey(XmlWriter writer) { writer.WriteStartElement("Accelerator키"); List<string[]> keylist = new List<string[]>(); keylist = GetAcceleratorKeyItemList(); foreach (string[] key_value in keylist) { writer.WriteElementString(ConvertString(key_value[0]), key_value[1]); } writer.WriteEndElement(); } internal List<string[]> GetAcceleratorKeyItemList() { List<string[]> item_list = new List<string[]>(); DataTable dt = Table; EHAutoElem eae = null; foreach (DataRow dr in dt.Rows) { eae = dr["EHAutoElem"] as EHAutoElem; if (eae.AE.Current.AcceleratorKey != string.Empty) { string[] name_key = new string[2]; name_key[0] = eae.ToString(); name_key[1] = eae.AE.Current.AcceleratorKey; item_list.Add(name_key); } } return item_list; } private void WriteControlCount(XmlWriter writer) { writer.WriteStartElement("컨트롤_유형_별_개수"); for (ENUM_CONTROL ctrl = 0; ctrl < ENUM_CONTROL.MAX_CONTROL; ctrl++) { int count = GetControlPatternCount(ctrl); writer.WriteElementString(ctrl.ToString(), count.ToString()); } writer.WriteEndElement(); } public int GetControlPatternCount(ENUM_CONTROL ctrl) { int count = 0; DataTable dt = Table; foreach (DataRow dr in dt.Rows) { if (dr[ctrl.ToString()] != DBNull.Value) { count++; } } return count; } private void WriteUIElement(XmlWriter writer) { writer.WriteStartElement("UI_요소_목록"); foreach (DataRow dr in Table.Rows) { WriteUIInfo(writer, dr); } writer.WriteEndElement(); } private void WriteUIInfo(XmlWriter writer, DataRow dr) { writer.WriteStartElement("UI요소"); for (ENUM_UIProperty eup = 0; eup < ENUM_UIProperty.MAX_UIPROPERTY; eup++) { string pname = eup.ToString(); string value = ConvertString(dr[eup.ToString()].ToString()); writer.WriteElementString(pname,value); } writer.WriteEndElement(); } public void SaveImageAll(string dir) { SaveImage(dir); SaveGrayImage(dir); } public void SaveImage(string dir) { string cdir = Directory.GetCurrentDirectory(); Directory.SetCurrentDirectory(dir); Directory.CreateDirectory("이미지"); int fname_index = 1; foreach (TreeNode tn in node_dic.Values) { EHAutoElem eae = tn.Tag as EHAutoElem; if (eae.Image != null) { eae.Image.Save("이미지/"+fname_index.ToString() + ".bmp"); } fname_index++; } Directory.SetCurrentDirectory(cdir); } public void SaveGrayImage(string dir) { string cdir = Directory.GetCurrentDirectory(); Directory.SetCurrentDirectory(dir); Directory.CreateDirectory("흑백이미지"); int fname_index = 1; foreach (TreeNode tn in node_dic.Values) { EHAutoElem eae = tn.Tag as EHAutoElem; if (eae.Image != null) { eae.GrayImage.Save("흑백이미지/" + fname_index.ToString() + ".bmp"); } fname_index++; } Directory.SetCurrentDirectory(cdir); } } }
[소스 10.7] AccEvalProject.cs