|
|
|
작성일 : 2013.05.08(화) |
작성자 : 윤석철 |
|
문서번호 : 130508 - 일 – SC1 | |
|
학습 내용 : UI 자동화 TrackFocus, Highlight | |
|
메인화면
응용프로그램이 실행이 되고 마우스로 아이콘을 클릭하거나 키보드로 누르게 되면 해당 아이콘의 Type와 이름이 TextBox에 뜨게 된다. SlideBar를 움직이게 되면 Highlight 사각형 박스가 천천히 Targeting된다.
| |
|
MainWindow.xaml.cs | |
|
using System; using System.Windows; using System.Drawing; using System.Windows.Automation; using System.Timers; using System.Threading; using System.Windows.Threading;
namespace TrackFocusHighlight { /// <summary> /// MainWindow.xaml에 대한 상호 작용 논리 /// </summary> public partial class MainWindow : Window { private HighlightRectangle highlight; private bool useTimer = true; System.Timers.Timer eventTimer; System.Windows.Rect focusedRect; Automati[안내]태그제한으로등록되지않습니다-xxonFocusChangedEventHandler focusHandler; AutomationElement focusedElement; double timerinterval;
AutomationElement lastTopLevelWindow;
public MainWindow() { InitializeComponent(); Highlight(); TrackFocus(); }
#region Highlight
private void Highlight() { // Create timer. timerinterval = slider1.Value; eventTimer = new System.Timers.Timer(); eventTimer.Elapsed += new ElapsedEventHandler(OnTimerTick); eventTimer.Enabled = false; eventTimer.AutoReset = false; timerinterval = slider1.Value; eventTimer.Interval = timerinterval;
//하이라이트 사각형 생성 highlight = new HighlightRectangle();
//UI 자동화 이벤트를 시작 ThreadStart threadDelegate = new ThreadStart(StartListening); Thread UIAutoThread = new Thread(threadDelegate); UIAutoThread.Start(); }
//하이라이트 사각형을 업데이트 private void OnTimerTick(object sender, EventArgs e) { UpdateHighlight(); }
private void btnClose_Click(object sender, RoutedEventArgs e) { Close(); }
//UI 자동화 등록 private void StartListening() { focusHandler = new Automati[안내]태그제한으로등록되지않습니다-xxonFocusChangedEventHandler([안내]태그제한으로등록되지않습니다-xxOnFocusChanged); Automation.AddAutomati[안내]태그제한으로등록되지않습니다-xxonFocusChangedEventHandler(focusHandler); }
//UI 자동화 Stop private void StopListening() { eventTimer.Stop(); Automation.RemoveAutomati[안내]태그제한으로등록되지않습니다-xxonFocusChangedEventHandler(focusHandler); }
//하이라이트가 업데이트 될 것이고, 또는 업데이트 강조 즉시 타이머 간격을으로 설정하면 시작 private void [안내]태그제한으로등록되지않습니다-xxOnFocusChanged(object src, Automati[안내]태그제한으로등록되지않습니다-xxonFocusChangedEventArgs e) { focusedElement = src as AutomationElement; focusedRect = focusedElement.Current.BoundingRectangle;
if (useTimer) { eventTimer.Interval = timerinterval; eventTimer.Start(); } else { UpdateHighlight(); } }
//이전 사각형을 숨기고 새로운 사각형 생성 private void UpdateHighlight() { highlight.Visible = false; highlight.Location = new Rectangle((int)focusedRect.Left, (int)focusedRect.Top, (int)focusedRect.Width, (int)focusedRect.Height); highlight.Visible = true; }
//Slider 변화에 응답 private void slider1_ValueChanged(object sender, EventArgs e) { if (slider1.Value > 0) { timerinterval = slider1.Value; useTimer = true; } else { useTimer = false; } } #endregion
#region TrackFocus
//초기화 void TrackFocus() { Automation.AddAutomati[안내]태그제한으로등록되지않습니다-xxonFocusChangedEventHandler(TrackFocusChanged); } //UI 자동화 요소의 최상위 창을 검색 private AutomationElement GetTopLevelWindow(AutomationElement element) { TreeWalker walker = TreeWalker.ControlViewWalker; AutomationElement elementParent; AutomationElement node = element;
try // { if (node == AutomationElement.RootElement) { return node; } //루트에 자식 트리로 간다 while (true) { elementParent = walker.GetParent(node); if (elementParent == null) { return null; } if (elementParent == AutomationElement.RootElement) { break; } node = elementParent; } } catch (ElementNotAvailableException) { node = null; } catch (ArgumentNullException) { node = null; } return node; }
//포커스 변경 이벤트를 처리 //초점을 받은 요소가 다른 최상위 창에 있는 경우, 그것을 알리고 그렇지 않다면 초점을 받은 요소를 알림 private void TrackFocusChanged(object src, Automati[안내]태그제한으로등록되지않습니다-xxonFocusChangedEventArgs e) { try { AutomationElement elementFocused = src as AutomationElement; AutomationElement topLevelWindow = GetTopLevelWindow(elementFocused); if (topLevelWindow == null) { return; } if (topLevelWindow != lastTopLevelWindow) { lastTopLevelWindow = topLevelWindow; OutputLine("Focus moved to top-level window." + "\n" + " " + topLevelWindow.Current.Name); } else { OutputLine("Focused element :" + "\n" + " Type : " + elementFocused.Current.LocalizedControlType + "\n" + " Name : " + elementFocused.Current.Name); } } catch (ElementNotAvailableException) { return; } }
//TextBox 출력 private void OutputLine(string outputStr) { Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate { tbOutput.Text = outputStr; })); } #endregion } } | |
|
OutputLine함수에서 tbOutput.Text = outputStr; 하게 되면 이러한 에러 메세지가 뜬다. 이것은 크로스 스레딩 문제이다. 이 오류는 메인 스레드가 아닌 스레드에서 컨트롤에 접근하려 해서 발생하는 에러이다. 이것을 해결하기 위해서는 Dispatcher.Invoke(DispatcherPriority, Delegate) 함수를 사용하면 해결 된다. | |
|
| |
|
HighlightRectangle.cs | |
|
using System.Drawing; using System.Windows.Forms;
namespace TrackFocusHighlight { class HighlightRectangle { private bool highlightShown; private int highlightLineWidth; private Rectangle highlightLocation;
private Form leftForm; private Form topForm; private Form rightForm; private Form bottomForm;
//강조표시 사격형의 각 측면 설정 public HighlightRectangle() { //사격형을 성성하고 값 설정 highlightShown = false; highlightLineWidth = 3; leftForm = new Form(); topForm = new Form(); rightForm = new Form(); bottomForm = new Form(); Form[] forms = { leftForm, topForm, rightForm, bottomForm }; foreach (Form form in forms) { form.FormBorderStyle = FormBorderStyle.None; form.ShowInTaskbar = false; form.TopMost = true; form.Visible = false; form.Left = 0; form.Top = 0; form.Width = 1; form.Height = 1; form.BackColor = Color.Aqua;
//Alt + Tab으로 표시되지 않고 도구 창을 확인 int style = NativeMethods.GetWindowLong( form.Handle, NativeMethods.GWL_EXSTYLE); NativeMethods.SetWindowLong( form.Handle, NativeMethods.GWL_EXSTYLE, (int)(style | NativeMethods.WS_EX_TOOLWINDOW)); } }
//사각형의 상태를 설정 //BeginInvoke를 사용하여 호출하고 이 메소드는 UI 스레드에 속한 모든 폼에 호출 public bool Visible { set { if (highlightShown != value) { highlightShown = value; if (highlightShown) { MethodInvoker mi = new MethodInvoker(Layout); leftForm.BeginInvoke(mi); mi = new MethodInvoker(ShowRectangle); leftForm.BeginInvoke(mi); } else { MethodInvoker mi = new MethodInvoker(HideRectangle); leftForm.BeginInvoke(mi); } } } }
//Highlight 위치 설정 public Rectangle Location { set { highlightLocation = value; MethodInvoker mi = new MethodInvoker(Layout); leftForm.BeginInvoke(mi); } } //사격형을 Show or Hide private void Show(bool show) { if (show) { NativeMethods.ShowWindow(leftForm.Handle, NativeMethods.SW_SHOWNA); NativeMethods.ShowWindow(topForm.Handle, NativeMethods.SW_SHOWNA); NativeMethods.ShowWindow(rightForm.Handle, NativeMethods.SW_SHOWNA); NativeMethods.ShowWindow(bottomForm.Handle, NativeMethods.SW_SHOWNA); } else { leftForm.Hide(); topForm.Hide(); rightForm.Hide(); bottomForm.Hide(); } }
//하이라이트 Show void ShowRectangle() { Show(true); }
//하이라이트 Hide void HideRectangle() { Show(false); }
//사각형을 구성하는 네 가지 형태의 위치와 크기를 설정 private void Layout() { NativeMethods.SetWindowPos(leftForm.Handle, NativeMethods.HWND_TOPMOST, highlightLocation.Left - highlightLineWidth, highlightLocation.Top, highlightLineWidth, highlightLocation.Height, NativeMethods.SWP_NOACTIVATE); NativeMethods.SetWindowPos(topForm.Handle, NativeMethods.HWND_TOPMOST, highlightLocation.Left - highlightLineWidth, highlightLocation.Top - highlightLineWidth, highlightLocation.Width + 2 * highlightLineWidth, highlightLineWidth, NativeMethods.SWP_NOACTIVATE); NativeMethods.SetWindowPos(rightForm.Handle, NativeMethods.HWND_TOPMOST, highlightLocation.Left + highlightLocation.Width, highlightLocation.Top, highlightLineWidth, highlightLocation.Height, NativeMethods.SWP_NOACTIVATE); NativeMethods.SetWindowPos(bottomForm.Handle, NativeMethods.HWND_TOPMOST, highlightLocation.Left - highlightLineWidth, highlightLocation.Top + highlightLocation.Height, highlightLocation.Width + 2 * highlightLineWidth, highlightLineWidth, NativeMethods.SWP_NOACTIVATE); } } } | |
|
NativeMethods.cs |
|
using System;
namespace TrackFocusHighlight { internal static class NativeMethods { [System.Runtime.InteropServices.DllImport("user32.dll")] internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[System.Runtime.InteropServices.DllImport("user32.dll")] internal static extern bool SetWindowPos( IntPtr hWnd, IntPtr hwndAfter, int x, int y, int width, int height, int flags);
[System.Runtime.InteropServices.DllImport("user32.dll")] internal static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[System.Runtime.InteropServices.DllImport("user32.dll")] internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[System.Runtime.InteropServices.DllImport("user32.dll")] internal static extern bool SetProcessDPIAware();
internal const int GWL_EXSTYLE = -20;
internal const int SW_SHOWNA = 8; internal const int WS_EX_TOOLWINDOW = 0x00000080;
internal const int SWP_NOACTIVATE = 0x0010; internal static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); } } |
참고사이트:
MSDN - http://msdn.microsoft.com/ko-kr/library/ms771315.aspx
[윤석철] - 5월 08일 UI자동화 TrckFocus, Highlight.docx