Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / UIAutomation / Win32Providers / MS / Internal / AutomationProxies / WindowsButton.cs / 1 / WindowsButton.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: Windows Button Proxy // // History: // 07/01/2003 : a-jeanp Created //--------------------------------------------------------------------------- using System; using System.Collections; using System.Text; using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows; using System.Runtime.InteropServices; using System.ComponentModel; using MS.Win32; namespace MS.Internal.AutomationProxies { // Windows Button proxy class WindowsButton : ProxyHwnd, IInvokeProvider, IToggleProvider, ISelectionProvider, ISelectionItemProvider { // ----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors // Contructor for Button Proxy class. // param "hwnd", Windows handle // param "parent", Proxy Parent. Null if it is a root fragment // param "type", Button / Checkbox / Radio / Group // param "style", Button Style (BS_*) also used as the Proxy ID // Made internal so that WinFormsHelper.CreateButton can use. internal WindowsButton (IntPtr hwnd, ProxyFragment parent, ButtonType type, int style, Accessible acc) : base( hwnd, parent, 0) { _type = type; _fIsKeyboardFocusable = true; _style = style; _acc = acc; // support for events _createOnEvent = new WinEventTracker.ProxyRaiseEvents (RaiseEvents); // Set ControlType based on type // Note: Do not call LocalizedName() within the constructor // since it is a virtual method. Calling a virtual method // in a constructor would have unintended consequences for // derived classes. if(type == ButtonType.PushButton) { _cControlType = ControlType.Button; _fControlHasLabel = false; } else if(type == ButtonType.CheckBox) { _cControlType = ControlType.CheckBox; // If a check box has non-empty text, it has no associated label. _fControlHasLabel = string.IsNullOrEmpty(GetLocalizedName()); } else if(type == ButtonType.RadioButton) { _cControlType = ControlType.RadioButton; // If a radio button has non-empty text, it has no associated label. _fControlHasLabel = string.IsNullOrEmpty(GetLocalizedName()); } else if (type == ButtonType.GroupBox) { _cControlType = ControlType.Group; _fIsKeyboardFocusable = false; // If a group box has non-empty text, it has no associated label. _fControlHasLabel = string.IsNullOrEmpty(GetLocalizedName()); } else { _cControlType = ControlType.Custom; } } #endregion #region Proxy Create // Static Create method called by UIAutomation to create this proxy. // returns null if unsuccessful internal static IRawElementProviderSimple Create(IntPtr hwnd, int idChild, int idObject) { return Create(hwnd, idChild); } private static IRawElementProviderSimple Create(IntPtr hwnd, int idChild) { // Something is wrong if idChild is not zero if (idChild != 0) { System.Diagnostics.Debug.Assert (idChild == 0, "Invalid Child Id, idChild != 0"); throw new ArgumentOutOfRangeException("idChild", idChild, SR.Get(SRID.ShouldBeZero)); } ButtonType type; int style; try { if (WindowsFormsHelper.IsWindowsFormsControl(hwnd)) { return WindowsFormsHelper.CreateButton(hwnd); } style = Misc.GetWindowStyle(hwnd) & NativeMethods.BS_TYPEMASK; switch (style) { case NativeMethods.BS_PUSHBUTTON: case NativeMethods.BS_DEFPUSHBUTTON: case NativeMethods.BS_OWNERDRAW: case NativeMethods.BS_SPLITBUTTON: // explore back and forward buttons type = ButtonType.PushButton; break; case NativeMethods.BS_CHECKBOX: case NativeMethods.BS_AUTOCHECKBOX: case NativeMethods.BS_3STATE: case NativeMethods.BS_AUTO3STATE: type = ButtonType.CheckBox; break; case NativeMethods.BS_RADIOBUTTON: case NativeMethods.BS_AUTORADIOBUTTON: type = ButtonType.RadioButton; break; case NativeMethods.BS_GROUPBOX: type = ButtonType.GroupBox; break; default: return null; } } catch (ElementNotAvailableException) { return null; } return new WindowsButton(hwnd, null, type, style, null); } // Static create method called by the event tracker system. // WinEvents are thrown only when a notification has been set for a // specific item. Create the item first and check for details afterward. internal static void RaiseEvents (IntPtr hwnd, int eventId, object idProp, int idObject, int idChild) { if (idObject != NativeMethods.OBJID_VSCROLL && idObject != NativeMethods.OBJID_HSCROLL) { // Can not RaiseEvents on windows that are no longer available. if (!UnsafeNativeMethods.IsWindow(hwnd)) { throw new ElementNotAvailableException(); } WindowsButton wtv = (WindowsButton)Create(hwnd, 0); // Create can return null if we don't know what kind of button this is if (wtv == null) { return; } //Only one event is generated for the winforms button so no need to check the pressed state. if (wtv._acc != null) { if (idProp == SelectionItemPattern.ElementSelectedEvent) { if (!wtv._acc.HasState(AccessibleState.Checked)) { eventId = NativeMethods.EventObjectSelectionRemove; idProp = SelectionItemPattern.ElementRemovedFromSelectionEvent; } } wtv.DispatchEvents(eventId, idProp, idObject, idChild); } else { if (idProp == InvokePattern.InvokedEvent) { // On XP, this event is triggered by the WinEvent // EventObjectStateChange (fired when a button is // pressed) since EventObjectInvoke is not available on XP. // However, this event is not fired reliably since it // is sensitive to timing issues during the button press. // The new event EventObjectInvoke, available only // on Vista, is fired reliably and corrects this. if (Environment.OSVersion.Version.Major < 6) { int state = Misc.ProxySendMessageInt(hwnd, NativeMethods.BM_GETSTATE, IntPtr.Zero, IntPtr.Zero); if (Misc.IsBitSet(state, NativeMethods.BST_PUSHED) && eventId == NativeMethods.EventObjectStateChange) { wtv.DispatchEvents(eventId, idProp, idObject, idChild); } } else if(eventId == NativeMethods.EventObjectInvoke) { // Vista or greater. To avoid duplicate firings of the InvokedEvent, // only dispatch this when the eventId is EventObjectInvoke. wtv.DispatchEvents(eventId, idProp, idObject, idChild); } } else { wtv.DispatchEvents(eventId, idProp, idObject, idChild); } } } } #endregion //------------------------------------------------------ // // Patterns Implementation // //----------------------------------------------------- #region ProxySimple Interface // Returns a pattern interface if supported. internal override object GetPatternProvider (AutomationPattern iid) { if (iid == InvokePattern.Pattern && _type == ButtonType.PushButton) { return this; } // Only WinForms RadioGroups should have this pattern. else if (iid == SelectionPattern.Pattern && _type == ButtonType.GroupBox) { return ContainsRadioButtons()? this : null; } else if (iid == SelectionItemPattern.Pattern && _type == ButtonType.RadioButton) { return this; } else if (iid == TogglePattern.Pattern && _type == ButtonType.CheckBox) { return this; } return null; } // Process all the Logical and Raw Element Properties internal override object GetElementProperty (AutomationProperty idProp) { if (idProp == AutomationElement.AccessKeyProperty) { // Special handling for forms if (!WindowsFormsHelper.IsWindowsFormsControl(_hwnd, ref _windowsForms) && IsStartButton()) { // Hard coded shortcut for the start button return ST.Get(STID.KeyCtrl) + " + " + ST.Get(STID.KeyEsc); } return Misc.AccessKey(Misc.ProxyGetText(_hwnd)); } else if (idProp == AutomationElement.IsEnabledProperty) { if (InShellTray()) { return SafeNativeMethods.IsWindowVisible(_hwnd); } } return base.GetElementProperty (idProp); } //Gets the localized name internal override string LocalizedName { get { return GetLocalizedName(); } } #endregion #region ProxyHwnd Overrides // Builds a list of Win32 WinEvents to process a UIAutomation Event. protected override WinEventTracker.EvtIdProperty[] EventToWinEvent(AutomationEvent idEvent, out int cEvent) { // For Vista, we only need register for EventObjectInvoke to handle InvokePattern.InvokedEvent. // For XP, we rely on state changes, handled in ProxyHwnd.EventToWinEvent(). if (idEvent == InvokePattern.InvokedEvent && Environment.OSVersion.Version.Major >= 6) { cEvent = 1; return new WinEventTracker.EvtIdProperty[] { new WinEventTracker.EvtIdProperty (NativeMethods.EventObjectInvoke, idEvent) }; } return base.EventToWinEvent(idEvent, out cEvent); } #endregion #region Invoke Pattern // Click the button void IInvokeProvider.Invoke () { Invoke(); } #endregion Invoke Pattern #region Selection Pattern // Returns an enumerator over the current selection. IRawElementProviderSimple[] ISelectionProvider.GetSelection() { IRawElementProviderSimple[] selection = null; Accessible accRadioButton = null; IntPtr hwndRadioButton = GetSelection(); if (hwndRadioButton == IntPtr.Zero || Accessible.AccessibleObjectFromWindow(hwndRadioButton, NativeMethods.OBJID_CLIENT, ref accRadioButton) != NativeMethods.S_OK || accRadioButton == null) { // framework will handle this one correctly return null; } else { selection = new IRawElementProviderSimple[] { new WindowsButton(hwndRadioButton, null, ButtonType.RadioButton, Misc.GetWindowStyle(hwndRadioButton) & NativeMethods.BS_TYPEMASK, accRadioButton) }; } return selection; } // Returns whether the control requires a minimum of one selected element at all times. bool ISelectionProvider.IsSelectionRequired { get { return true; } } // Returns whether the control supports multiple selection. bool ISelectionProvider.CanSelectMultiple { get { return false; } } #endregion Selection Pattern #region SelectionItem Pattern // Selects this element void ISelectionItemProvider.Select() { Invoke(); } // Adds this element to the selection void ISelectionItemProvider.AddToSelection() { throw new InvalidOperationException(SR.Get(SRID.DoesNotSupportMultipleSelection)); } // Removes this element from the selection void ISelectionItemProvider.RemoveFromSelection() { throw new InvalidOperationException(SR.Get(SRID.DoesNotSupportMultipleSelection)); } // True if this element is part of the the selection bool ISelectionItemProvider.IsSelected { get { return ToggleState == ToggleState.On ? true : false; } } // Returns the container for this element IRawElementProviderSimple ISelectionItemProvider.SelectionContainer { get { IntPtr hwndParent = Misc.GetParent(_hwnd); if (hwndParent != IntPtr.Zero && WindowsFormsHelper.IsWindowsFormsControl(hwndParent)) { Accessible accParent = null; if (Accessible.AccessibleObjectFromWindow(hwndParent, NativeMethods.OBJID_CLIENT, ref accParent) != NativeMethods.S_OK || accParent == null) { return null; } if (accParent.Role == AccessibleRole.Grouping) { return new WindowsButton(hwndParent, null, ButtonType.GroupBox, Misc.GetWindowStyle(hwndParent) & NativeMethods.BS_TYPEMASK, accParent); } } return null; } } #endregion SelectionItem Pattern #region IToggleProvider void IToggleProvider.Toggle() { // This pattern is only supported for checkboxes and radio buttons // so the invoke will never invoke a normal button. Invoke(); } ToggleState IToggleProvider.ToggleState { get { return ToggleState; } } #endregion IToggleProvider // ------------------------------------------------------ // // Internal Types // // ------------------------------------------------------ #region Internal Types // Button control types based on groupings of style constants // Made internal so that WinFormsHelper can use. internal enum ButtonType { PushButton, CheckBox, RadioButton, GroupBox }; #endregion // ----------------------------------------------------- // // Private Methods // // ------------------------------------------------------ #region Private Methods private void Invoke() { // Check that button can be clicked // This state could change anytime if (!SafeNativeMethods.IsWindowEnabled(_hwnd)) { throw new ElementNotEnabledException(); } // Moved this outside the if block because it's needed for WinForms, which uses _acc.DoDefaultAction() if (!IsShowAllProgramsButton()) { // SetFocus need here to resolve bug 1060649 Misc.SetFocus(_hwnd); } if (_acc == null) { switch (_style) { case NativeMethods.BS_PUSHBUTTON: case NativeMethods.BS_DEFPUSHBUTTON: case NativeMethods.BS_PUSHBOX: case NativeMethods.BS_OWNERDRAW: case NativeMethods.BS_USERBUTTON: case NativeMethods.BS_CHECKBOX: case NativeMethods.BS_AUTOCHECKBOX: case NativeMethods.BS_RADIOBUTTON: case NativeMethods.BS_AUTORADIOBUTTON: case NativeMethods.BS_3STATE: case NativeMethods.BS_AUTO3STATE: case NativeMethods.BS_SPLITBUTTON: // explore back and forward buttons if (IsStartButton()) { // You can't just click the start button; it won't do // anything if the tray isn't active except take focus Misc.PostMessage(_hwnd, NativeMethods.WM_SYSCOMMAND, new IntPtr(NativeMethods.SC_TASKLIST), IntPtr.Zero); break; } if (_type == ButtonType.PushButton && !IsStartButton()) { // For the Invoke event to work, there needs to be time between the OBJ_STATECHANGE // for pushing the button and the OBJ_STATECHANGE for releasing the button. // For buttons the OBJ_STATECHANGE is caused by the BM_SETSTATE message. // The BM_CLICK causes these BM_SETSTATE's to happen to fast, the OBJ_STATECHANGES // are received simultaneous. This does not give enough time to check the button pushed // state in the event handler, cause the state to be missed and the Invoke event not // being raised. Send an extra BM_SETSTATE to allow the event handler to be able to // see the state change and raise the Invoke event. Misc.ProxySendMessage(_hwnd, NativeMethods.BM_SETSTATE, new IntPtr(1), IntPtr.Zero, true); System.Threading.Thread.Sleep(1); } try { // Now cause the button click. Misc.ProxySendMessage(_hwnd, NativeMethods.BM_CLICK, IntPtr.Zero, IntPtr.Zero, true); } catch (ElementNotAvailableException) { // This is to resolve PS Bug 1074570. There is a timing issue with the SendMessage and // the Cancel button on the Log Off Dialog box. The button with be invoked but sometimes // the SendMessage will return a failure that will cause the ElementNotAvailableException // to be thrown. return; } break; } } else { _acc.DoDefaultAction(); } } private bool InShellTray() { IntPtr hwndShell = Misc.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Shell_TrayWnd", null); if (hwndShell != IntPtr.Zero) { // if the outter most window/dialog box of the button is the shell window return true. return GetRootAncestor() == hwndShell; } return false; } private bool IsStartButton() { if (!Misc.GetClassName(_hwnd).Equals("Button")) { return false; } // Vista's start button is top-level - use this check for it (leveraged from oleacc) if (Environment.OSVersion.Version.Major >= 6) { return Misc.InTheShellProcess(_hwnd) && UnsafeNativeMethods.GetProp(_hwnd, "StartButtonTag") == new IntPtr(304); } else { IntPtr hwndParent = Misc.GetParent(_hwnd); if (hwndParent != IntPtr.Zero) { if (Misc.GetClassName(hwndParent).Equals("Shell_TrayWnd")) { return true; } } return false; } } private bool IsShowAllProgramsButton() { if (!Misc.GetClassName(_hwnd).Equals("Button")) { return false; } IntPtr hwndParent = Misc.GetParent(_hwnd); if (hwndParent != IntPtr.Zero) { if (Misc.GetClassName(hwndParent).Equals("Desktop More Programs Pane")) { return true; } } return false; } // Find the outter most window/dialog box that contains the button. private IntPtr GetRootAncestor() { IntPtr hwndParent = _hwnd; IntPtr hwndRoot; do { // Have not found the outter most window/dialog box yet, so take one more step out. hwndRoot = hwndParent; if (Misc.IsBitSet(WindowStyle, NativeMethods.WS_CHILD)) { hwndParent = Misc.GetParent(hwndRoot); } else { hwndParent = Misc.GetWindow(hwndRoot, NativeMethods.GW_OWNER); } // is the parent of this root the desktop? If so root is the outter most window/dialog box. } while (hwndParent != _hwndDesktop && hwndParent != IntPtr.Zero); return hwndRoot; } private ToggleState ToggleState { get { ToggleState icsState; // Special handling for forms if (_acc != null) { AccessibleState state = _acc.State; if (Accessible.HasState(state, AccessibleState.Checked)) { icsState = ToggleState.On; } else if (Accessible.HasState(state, AccessibleState.Mixed)) { icsState = ToggleState.Indeterminate; } else { icsState = ToggleState.Off; } } else { int state = Misc.ProxySendMessageInt(_hwnd, NativeMethods.BM_GETCHECK, IntPtr.Zero, IntPtr.Zero); if (Misc.IsBitSet(state, NativeMethods.BST_CHECKED)) { icsState = ToggleState.On; } else if (Misc.IsBitSet(state, NativeMethods.BST_INDETERMINATE)) { icsState = ToggleState.Indeterminate; } else { icsState = ToggleState.Off; } } return icsState; } } unsafe private bool ContainsRadioButtons() { bool radiobuttonChildFound = false; // WinForm GroupBoxes have a parent/child relationship. Win32 GroupBoxes do not. if (WindowsFormsHelper.IsWindowsFormsControl(_hwnd, ref _windowsForms)) { Misc.EnumChildWindows(_hwnd, new NativeMethods.EnumChildrenCallbackVoid(FindRadioButtonChild), (void*)&radiobuttonChildFound); } return radiobuttonChildFound; } unsafe private bool FindRadioButtonChild(IntPtr hwnd, void* lParam) { // Only be concerned with Winforms child controls. if (!WindowsFormsHelper.IsWindowsFormsControl(hwnd)) { return true; } Accessible acc = null; if (Accessible.AccessibleObjectFromWindow(hwnd, NativeMethods.OBJID_CLIENT, ref acc) == NativeMethods.S_OK && acc != null && acc.Role == AccessibleRole.RadioButton) { *(bool*)lParam = true; return false; } return true; } // Private method to encapsulate logic used by both the constructor // and the virtual LocalizedName property. private string GetLocalizedName() { return Misc.StripMnemonic(Misc.ProxyGetText(_hwnd)); } private unsafe IntPtr GetSelection() { // WinForm GroupBoxes have a parent/child relationship. Win32 GroupBoxes do not. if (WindowsFormsHelper.IsWindowsFormsControl(_hwnd, ref _windowsForms)) { IntPtr selectedRadiobutton = new IntPtr(0); Misc.EnumChildWindows(_hwnd, new NativeMethods.EnumChildrenCallbackVoid(FindSelectedRadioButtonChild), (void*)&selectedRadiobutton); return selectedRadiobutton; } return IntPtr.Zero; } private unsafe bool FindSelectedRadioButtonChild(IntPtr hwnd, void* lParam) { // Only be concerned with Winforms child controls. if (!WindowsFormsHelper.IsWindowsFormsControl(hwnd)) { return true; } Accessible acc = null; if (Accessible.AccessibleObjectFromWindow(hwnd, NativeMethods.OBJID_CLIENT, ref acc) == NativeMethods.S_OK && acc != null && acc.Role == AccessibleRole.RadioButton && acc.HasState(AccessibleState.Checked)) { *(IntPtr*)lParam = hwnd; return false; } return true; } #endregion // ----------------------------------------------------- // // Private Fields // // ----------------------------------------------------- #region Private Fields private ButtonType _type; private int _style; private Accessible _acc; // Accessible is used for WinForms Buttons. #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: Windows Button Proxy // // History: // 07/01/2003 : a-jeanp Created //--------------------------------------------------------------------------- using System; using System.Collections; using System.Text; using System.Windows.Automation; using System.Windows.Automation.Provider; using System.Windows; using System.Runtime.InteropServices; using System.ComponentModel; using MS.Win32; namespace MS.Internal.AutomationProxies { // Windows Button proxy class WindowsButton : ProxyHwnd, IInvokeProvider, IToggleProvider, ISelectionProvider, ISelectionItemProvider { // ----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors // Contructor for Button Proxy class. // param "hwnd", Windows handle // param "parent", Proxy Parent. Null if it is a root fragment // param "type", Button / Checkbox / Radio / Group // param "style", Button Style (BS_*) also used as the Proxy ID // Made internal so that WinFormsHelper.CreateButton can use. internal WindowsButton (IntPtr hwnd, ProxyFragment parent, ButtonType type, int style, Accessible acc) : base( hwnd, parent, 0) { _type = type; _fIsKeyboardFocusable = true; _style = style; _acc = acc; // support for events _createOnEvent = new WinEventTracker.ProxyRaiseEvents (RaiseEvents); // Set ControlType based on type // Note: Do not call LocalizedName() within the constructor // since it is a virtual method. Calling a virtual method // in a constructor would have unintended consequences for // derived classes. if(type == ButtonType.PushButton) { _cControlType = ControlType.Button; _fControlHasLabel = false; } else if(type == ButtonType.CheckBox) { _cControlType = ControlType.CheckBox; // If a check box has non-empty text, it has no associated label. _fControlHasLabel = string.IsNullOrEmpty(GetLocalizedName()); } else if(type == ButtonType.RadioButton) { _cControlType = ControlType.RadioButton; // If a radio button has non-empty text, it has no associated label. _fControlHasLabel = string.IsNullOrEmpty(GetLocalizedName()); } else if (type == ButtonType.GroupBox) { _cControlType = ControlType.Group; _fIsKeyboardFocusable = false; // If a group box has non-empty text, it has no associated label. _fControlHasLabel = string.IsNullOrEmpty(GetLocalizedName()); } else { _cControlType = ControlType.Custom; } } #endregion #region Proxy Create // Static Create method called by UIAutomation to create this proxy. // returns null if unsuccessful internal static IRawElementProviderSimple Create(IntPtr hwnd, int idChild, int idObject) { return Create(hwnd, idChild); } private static IRawElementProviderSimple Create(IntPtr hwnd, int idChild) { // Something is wrong if idChild is not zero if (idChild != 0) { System.Diagnostics.Debug.Assert (idChild == 0, "Invalid Child Id, idChild != 0"); throw new ArgumentOutOfRangeException("idChild", idChild, SR.Get(SRID.ShouldBeZero)); } ButtonType type; int style; try { if (WindowsFormsHelper.IsWindowsFormsControl(hwnd)) { return WindowsFormsHelper.CreateButton(hwnd); } style = Misc.GetWindowStyle(hwnd) & NativeMethods.BS_TYPEMASK; switch (style) { case NativeMethods.BS_PUSHBUTTON: case NativeMethods.BS_DEFPUSHBUTTON: case NativeMethods.BS_OWNERDRAW: case NativeMethods.BS_SPLITBUTTON: // explore back and forward buttons type = ButtonType.PushButton; break; case NativeMethods.BS_CHECKBOX: case NativeMethods.BS_AUTOCHECKBOX: case NativeMethods.BS_3STATE: case NativeMethods.BS_AUTO3STATE: type = ButtonType.CheckBox; break; case NativeMethods.BS_RADIOBUTTON: case NativeMethods.BS_AUTORADIOBUTTON: type = ButtonType.RadioButton; break; case NativeMethods.BS_GROUPBOX: type = ButtonType.GroupBox; break; default: return null; } } catch (ElementNotAvailableException) { return null; } return new WindowsButton(hwnd, null, type, style, null); } // Static create method called by the event tracker system. // WinEvents are thrown only when a notification has been set for a // specific item. Create the item first and check for details afterward. internal static void RaiseEvents (IntPtr hwnd, int eventId, object idProp, int idObject, int idChild) { if (idObject != NativeMethods.OBJID_VSCROLL && idObject != NativeMethods.OBJID_HSCROLL) { // Can not RaiseEvents on windows that are no longer available. if (!UnsafeNativeMethods.IsWindow(hwnd)) { throw new ElementNotAvailableException(); } WindowsButton wtv = (WindowsButton)Create(hwnd, 0); // Create can return null if we don't know what kind of button this is if (wtv == null) { return; } //Only one event is generated for the winforms button so no need to check the pressed state. if (wtv._acc != null) { if (idProp == SelectionItemPattern.ElementSelectedEvent) { if (!wtv._acc.HasState(AccessibleState.Checked)) { eventId = NativeMethods.EventObjectSelectionRemove; idProp = SelectionItemPattern.ElementRemovedFromSelectionEvent; } } wtv.DispatchEvents(eventId, idProp, idObject, idChild); } else { if (idProp == InvokePattern.InvokedEvent) { // On XP, this event is triggered by the WinEvent // EventObjectStateChange (fired when a button is // pressed) since EventObjectInvoke is not available on XP. // However, this event is not fired reliably since it // is sensitive to timing issues during the button press. // The new event EventObjectInvoke, available only // on Vista, is fired reliably and corrects this. if (Environment.OSVersion.Version.Major < 6) { int state = Misc.ProxySendMessageInt(hwnd, NativeMethods.BM_GETSTATE, IntPtr.Zero, IntPtr.Zero); if (Misc.IsBitSet(state, NativeMethods.BST_PUSHED) && eventId == NativeMethods.EventObjectStateChange) { wtv.DispatchEvents(eventId, idProp, idObject, idChild); } } else if(eventId == NativeMethods.EventObjectInvoke) { // Vista or greater. To avoid duplicate firings of the InvokedEvent, // only dispatch this when the eventId is EventObjectInvoke. wtv.DispatchEvents(eventId, idProp, idObject, idChild); } } else { wtv.DispatchEvents(eventId, idProp, idObject, idChild); } } } } #endregion //------------------------------------------------------ // // Patterns Implementation // //----------------------------------------------------- #region ProxySimple Interface // Returns a pattern interface if supported. internal override object GetPatternProvider (AutomationPattern iid) { if (iid == InvokePattern.Pattern && _type == ButtonType.PushButton) { return this; } // Only WinForms RadioGroups should have this pattern. else if (iid == SelectionPattern.Pattern && _type == ButtonType.GroupBox) { return ContainsRadioButtons()? this : null; } else if (iid == SelectionItemPattern.Pattern && _type == ButtonType.RadioButton) { return this; } else if (iid == TogglePattern.Pattern && _type == ButtonType.CheckBox) { return this; } return null; } // Process all the Logical and Raw Element Properties internal override object GetElementProperty (AutomationProperty idProp) { if (idProp == AutomationElement.AccessKeyProperty) { // Special handling for forms if (!WindowsFormsHelper.IsWindowsFormsControl(_hwnd, ref _windowsForms) && IsStartButton()) { // Hard coded shortcut for the start button return ST.Get(STID.KeyCtrl) + " + " + ST.Get(STID.KeyEsc); } return Misc.AccessKey(Misc.ProxyGetText(_hwnd)); } else if (idProp == AutomationElement.IsEnabledProperty) { if (InShellTray()) { return SafeNativeMethods.IsWindowVisible(_hwnd); } } return base.GetElementProperty (idProp); } //Gets the localized name internal override string LocalizedName { get { return GetLocalizedName(); } } #endregion #region ProxyHwnd Overrides // Builds a list of Win32 WinEvents to process a UIAutomation Event. protected override WinEventTracker.EvtIdProperty[] EventToWinEvent(AutomationEvent idEvent, out int cEvent) { // For Vista, we only need register for EventObjectInvoke to handle InvokePattern.InvokedEvent. // For XP, we rely on state changes, handled in ProxyHwnd.EventToWinEvent(). if (idEvent == InvokePattern.InvokedEvent && Environment.OSVersion.Version.Major >= 6) { cEvent = 1; return new WinEventTracker.EvtIdProperty[] { new WinEventTracker.EvtIdProperty (NativeMethods.EventObjectInvoke, idEvent) }; } return base.EventToWinEvent(idEvent, out cEvent); } #endregion #region Invoke Pattern // Click the button void IInvokeProvider.Invoke () { Invoke(); } #endregion Invoke Pattern #region Selection Pattern // Returns an enumerator over the current selection. IRawElementProviderSimple[] ISelectionProvider.GetSelection() { IRawElementProviderSimple[] selection = null; Accessible accRadioButton = null; IntPtr hwndRadioButton = GetSelection(); if (hwndRadioButton == IntPtr.Zero || Accessible.AccessibleObjectFromWindow(hwndRadioButton, NativeMethods.OBJID_CLIENT, ref accRadioButton) != NativeMethods.S_OK || accRadioButton == null) { // framework will handle this one correctly return null; } else { selection = new IRawElementProviderSimple[] { new WindowsButton(hwndRadioButton, null, ButtonType.RadioButton, Misc.GetWindowStyle(hwndRadioButton) & NativeMethods.BS_TYPEMASK, accRadioButton) }; } return selection; } // Returns whether the control requires a minimum of one selected element at all times. bool ISelectionProvider.IsSelectionRequired { get { return true; } } // Returns whether the control supports multiple selection. bool ISelectionProvider.CanSelectMultiple { get { return false; } } #endregion Selection Pattern #region SelectionItem Pattern // Selects this element void ISelectionItemProvider.Select() { Invoke(); } // Adds this element to the selection void ISelectionItemProvider.AddToSelection() { throw new InvalidOperationException(SR.Get(SRID.DoesNotSupportMultipleSelection)); } // Removes this element from the selection void ISelectionItemProvider.RemoveFromSelection() { throw new InvalidOperationException(SR.Get(SRID.DoesNotSupportMultipleSelection)); } // True if this element is part of the the selection bool ISelectionItemProvider.IsSelected { get { return ToggleState == ToggleState.On ? true : false; } } // Returns the container for this element IRawElementProviderSimple ISelectionItemProvider.SelectionContainer { get { IntPtr hwndParent = Misc.GetParent(_hwnd); if (hwndParent != IntPtr.Zero && WindowsFormsHelper.IsWindowsFormsControl(hwndParent)) { Accessible accParent = null; if (Accessible.AccessibleObjectFromWindow(hwndParent, NativeMethods.OBJID_CLIENT, ref accParent) != NativeMethods.S_OK || accParent == null) { return null; } if (accParent.Role == AccessibleRole.Grouping) { return new WindowsButton(hwndParent, null, ButtonType.GroupBox, Misc.GetWindowStyle(hwndParent) & NativeMethods.BS_TYPEMASK, accParent); } } return null; } } #endregion SelectionItem Pattern #region IToggleProvider void IToggleProvider.Toggle() { // This pattern is only supported for checkboxes and radio buttons // so the invoke will never invoke a normal button. Invoke(); } ToggleState IToggleProvider.ToggleState { get { return ToggleState; } } #endregion IToggleProvider // ------------------------------------------------------ // // Internal Types // // ------------------------------------------------------ #region Internal Types // Button control types based on groupings of style constants // Made internal so that WinFormsHelper can use. internal enum ButtonType { PushButton, CheckBox, RadioButton, GroupBox }; #endregion // ----------------------------------------------------- // // Private Methods // // ------------------------------------------------------ #region Private Methods private void Invoke() { // Check that button can be clicked // This state could change anytime if (!SafeNativeMethods.IsWindowEnabled(_hwnd)) { throw new ElementNotEnabledException(); } // Moved this outside the if block because it's needed for WinForms, which uses _acc.DoDefaultAction() if (!IsShowAllProgramsButton()) { // SetFocus need here to resolve bug 1060649 Misc.SetFocus(_hwnd); } if (_acc == null) { switch (_style) { case NativeMethods.BS_PUSHBUTTON: case NativeMethods.BS_DEFPUSHBUTTON: case NativeMethods.BS_PUSHBOX: case NativeMethods.BS_OWNERDRAW: case NativeMethods.BS_USERBUTTON: case NativeMethods.BS_CHECKBOX: case NativeMethods.BS_AUTOCHECKBOX: case NativeMethods.BS_RADIOBUTTON: case NativeMethods.BS_AUTORADIOBUTTON: case NativeMethods.BS_3STATE: case NativeMethods.BS_AUTO3STATE: case NativeMethods.BS_SPLITBUTTON: // explore back and forward buttons if (IsStartButton()) { // You can't just click the start button; it won't do // anything if the tray isn't active except take focus Misc.PostMessage(_hwnd, NativeMethods.WM_SYSCOMMAND, new IntPtr(NativeMethods.SC_TASKLIST), IntPtr.Zero); break; } if (_type == ButtonType.PushButton && !IsStartButton()) { // For the Invoke event to work, there needs to be time between the OBJ_STATECHANGE // for pushing the button and the OBJ_STATECHANGE for releasing the button. // For buttons the OBJ_STATECHANGE is caused by the BM_SETSTATE message. // The BM_CLICK causes these BM_SETSTATE's to happen to fast, the OBJ_STATECHANGES // are received simultaneous. This does not give enough time to check the button pushed // state in the event handler, cause the state to be missed and the Invoke event not // being raised. Send an extra BM_SETSTATE to allow the event handler to be able to // see the state change and raise the Invoke event. Misc.ProxySendMessage(_hwnd, NativeMethods.BM_SETSTATE, new IntPtr(1), IntPtr.Zero, true); System.Threading.Thread.Sleep(1); } try { // Now cause the button click. Misc.ProxySendMessage(_hwnd, NativeMethods.BM_CLICK, IntPtr.Zero, IntPtr.Zero, true); } catch (ElementNotAvailableException) { // This is to resolve PS Bug 1074570. There is a timing issue with the SendMessage and // the Cancel button on the Log Off Dialog box. The button with be invoked but sometimes // the SendMessage will return a failure that will cause the ElementNotAvailableException // to be thrown. return; } break; } } else { _acc.DoDefaultAction(); } } private bool InShellTray() { IntPtr hwndShell = Misc.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Shell_TrayWnd", null); if (hwndShell != IntPtr.Zero) { // if the outter most window/dialog box of the button is the shell window return true. return GetRootAncestor() == hwndShell; } return false; } private bool IsStartButton() { if (!Misc.GetClassName(_hwnd).Equals("Button")) { return false; } // Vista's start button is top-level - use this check for it (leveraged from oleacc) if (Environment.OSVersion.Version.Major >= 6) { return Misc.InTheShellProcess(_hwnd) && UnsafeNativeMethods.GetProp(_hwnd, "StartButtonTag") == new IntPtr(304); } else { IntPtr hwndParent = Misc.GetParent(_hwnd); if (hwndParent != IntPtr.Zero) { if (Misc.GetClassName(hwndParent).Equals("Shell_TrayWnd")) { return true; } } return false; } } private bool IsShowAllProgramsButton() { if (!Misc.GetClassName(_hwnd).Equals("Button")) { return false; } IntPtr hwndParent = Misc.GetParent(_hwnd); if (hwndParent != IntPtr.Zero) { if (Misc.GetClassName(hwndParent).Equals("Desktop More Programs Pane")) { return true; } } return false; } // Find the outter most window/dialog box that contains the button. private IntPtr GetRootAncestor() { IntPtr hwndParent = _hwnd; IntPtr hwndRoot; do { // Have not found the outter most window/dialog box yet, so take one more step out. hwndRoot = hwndParent; if (Misc.IsBitSet(WindowStyle, NativeMethods.WS_CHILD)) { hwndParent = Misc.GetParent(hwndRoot); } else { hwndParent = Misc.GetWindow(hwndRoot, NativeMethods.GW_OWNER); } // is the parent of this root the desktop? If so root is the outter most window/dialog box. } while (hwndParent != _hwndDesktop && hwndParent != IntPtr.Zero); return hwndRoot; } private ToggleState ToggleState { get { ToggleState icsState; // Special handling for forms if (_acc != null) { AccessibleState state = _acc.State; if (Accessible.HasState(state, AccessibleState.Checked)) { icsState = ToggleState.On; } else if (Accessible.HasState(state, AccessibleState.Mixed)) { icsState = ToggleState.Indeterminate; } else { icsState = ToggleState.Off; } } else { int state = Misc.ProxySendMessageInt(_hwnd, NativeMethods.BM_GETCHECK, IntPtr.Zero, IntPtr.Zero); if (Misc.IsBitSet(state, NativeMethods.BST_CHECKED)) { icsState = ToggleState.On; } else if (Misc.IsBitSet(state, NativeMethods.BST_INDETERMINATE)) { icsState = ToggleState.Indeterminate; } else { icsState = ToggleState.Off; } } return icsState; } } unsafe private bool ContainsRadioButtons() { bool radiobuttonChildFound = false; // WinForm GroupBoxes have a parent/child relationship. Win32 GroupBoxes do not. if (WindowsFormsHelper.IsWindowsFormsControl(_hwnd, ref _windowsForms)) { Misc.EnumChildWindows(_hwnd, new NativeMethods.EnumChildrenCallbackVoid(FindRadioButtonChild), (void*)&radiobuttonChildFound); } return radiobuttonChildFound; } unsafe private bool FindRadioButtonChild(IntPtr hwnd, void* lParam) { // Only be concerned with Winforms child controls. if (!WindowsFormsHelper.IsWindowsFormsControl(hwnd)) { return true; } Accessible acc = null; if (Accessible.AccessibleObjectFromWindow(hwnd, NativeMethods.OBJID_CLIENT, ref acc) == NativeMethods.S_OK && acc != null && acc.Role == AccessibleRole.RadioButton) { *(bool*)lParam = true; return false; } return true; } // Private method to encapsulate logic used by both the constructor // and the virtual LocalizedName property. private string GetLocalizedName() { return Misc.StripMnemonic(Misc.ProxyGetText(_hwnd)); } private unsafe IntPtr GetSelection() { // WinForm GroupBoxes have a parent/child relationship. Win32 GroupBoxes do not. if (WindowsFormsHelper.IsWindowsFormsControl(_hwnd, ref _windowsForms)) { IntPtr selectedRadiobutton = new IntPtr(0); Misc.EnumChildWindows(_hwnd, new NativeMethods.EnumChildrenCallbackVoid(FindSelectedRadioButtonChild), (void*)&selectedRadiobutton); return selectedRadiobutton; } return IntPtr.Zero; } private unsafe bool FindSelectedRadioButtonChild(IntPtr hwnd, void* lParam) { // Only be concerned with Winforms child controls. if (!WindowsFormsHelper.IsWindowsFormsControl(hwnd)) { return true; } Accessible acc = null; if (Accessible.AccessibleObjectFromWindow(hwnd, NativeMethods.OBJID_CLIENT, ref acc) == NativeMethods.S_OK && acc != null && acc.Role == AccessibleRole.RadioButton && acc.HasState(AccessibleState.Checked)) { *(IntPtr*)lParam = hwnd; return false; } return true; } #endregion // ----------------------------------------------------- // // Private Fields // // ----------------------------------------------------- #region Private Fields private ButtonType _type; private int _style; private Accessible _acc; // Accessible is used for WinForms Buttons. #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- TextBoxView.cs
- GridSplitter.cs
- ProxyHelper.cs
- Formatter.cs
- CollectionDataContractAttribute.cs
- ZipIOLocalFileBlock.cs
- SqlAggregateChecker.cs
- CfgSemanticTag.cs
- ConfigXmlAttribute.cs
- ClientConfigurationSystem.cs
- AnnouncementEventArgs.cs
- _DomainName.cs
- Int64.cs
- DecimalStorage.cs
- HwndSourceParameters.cs
- FontFamily.cs
- TableRow.cs
- ObjectHandle.cs
- BooleanFunctions.cs
- AudioDeviceOut.cs
- ReachPageContentSerializer.cs
- Form.cs
- HMACRIPEMD160.cs
- TextRenderer.cs
- TextPattern.cs
- ResourceDescriptionAttribute.cs
- XPathQilFactory.cs
- WebContext.cs
- serverconfig.cs
- DocumentViewerHelper.cs
- ProfileSettings.cs
- IRCollection.cs
- BufferedGraphics.cs
- CodeTypeDeclarationCollection.cs
- Registry.cs
- VisualTarget.cs
- SqlClientFactory.cs
- DotExpr.cs
- ObjectFullSpanRewriter.cs
- GrabHandleGlyph.cs
- Stroke2.cs
- DataControlExtensions.cs
- DesignerDataSourceView.cs
- SQLResource.cs
- AppSettingsExpressionBuilder.cs
- XmlUtf8RawTextWriter.cs
- AddInController.cs
- XmlSchemaInfo.cs
- ObjectRef.cs
- GeneralTransform3DGroup.cs
- QuinticEase.cs
- HttpClientCertificate.cs
- OleDbFactory.cs
- PersonalizableTypeEntry.cs
- Geometry.cs
- TypeGeneratedEventArgs.cs
- ThemeDictionaryExtension.cs
- CallSite.cs
- LassoHelper.cs
- RealProxy.cs
- ScaleTransform3D.cs
- ScalarConstant.cs
- DoubleIndependentAnimationStorage.cs
- LineInfo.cs
- shaper.cs
- StringResourceManager.cs
- SamlEvidence.cs
- SafePEFileHandle.cs
- PermissionToken.cs
- DiscoveryServerProtocol.cs
- FileDialogCustomPlace.cs
- ExtendedTransformFactory.cs
- EndOfStreamException.cs
- InputManager.cs
- ProxyWebPart.cs
- BitmapPalette.cs
- FlowDocumentReader.cs
- MailWriter.cs
- ProfileEventArgs.cs
- PowerModeChangedEventArgs.cs
- VariableExpressionConverter.cs
- DaylightTime.cs
- ContourSegment.cs
- DivideByZeroException.cs
- ToolStripDropDownClosedEventArgs.cs
- WebPartConnectionsEventArgs.cs
- ModelVisual3D.cs
- CodeMemberProperty.cs
- SignedXml.cs
- DbSetClause.cs
- LogRestartAreaEnumerator.cs
- ViewValidator.cs
- sqlnorm.cs
- UnauthorizedAccessException.cs
- ParserOptions.cs
- TextServicesProperty.cs
- ColumnHeaderConverter.cs
- FocusChangedEventArgs.cs
- MatrixCamera.cs
- DataGridSortCommandEventArgs.cs