Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / UIAutomation / Win32Providers / MS / Internal / AutomationProxies / WindowsButton.cs / 1305600 / 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 [....] 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 [....] 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 [....], 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 [....] 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 [....] 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 [....] 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
- AttachedPropertyMethodSelector.cs
- TextLineBreak.cs
- TextEffect.cs
- Helpers.cs
- KeyInterop.cs
- MouseEvent.cs
- Roles.cs
- externdll.cs
- DataView.cs
- SQLDateTime.cs
- ObjectSelectorEditor.cs
- WebZoneDesigner.cs
- PostBackOptions.cs
- JoinElimination.cs
- CodeAttributeDeclaration.cs
- DataServiceProviderMethods.cs
- CodeCommentStatementCollection.cs
- InputLanguageEventArgs.cs
- CacheOutputQuery.cs
- IgnoreFlushAndCloseStream.cs
- EventLogPermissionEntryCollection.cs
- RequiredAttributeAttribute.cs
- Metafile.cs
- ILGenerator.cs
- CodeVariableReferenceExpression.cs
- QueryStatement.cs
- CompilerResults.cs
- TempFiles.cs
- DecoderReplacementFallback.cs
- QueryableDataSourceHelper.cs
- SwitchAttribute.cs
- ExceptionUtil.cs
- JobInputBins.cs
- LexicalChunk.cs
- ExtentJoinTreeNode.cs
- FixedLineResult.cs
- WindowCollection.cs
- ReferenceService.cs
- ObjectTypeMapping.cs
- AliasGenerator.cs
- Tokenizer.cs
- XmlCharType.cs
- ToolStripItemClickedEventArgs.cs
- HandlerBase.cs
- GenerateTemporaryAssemblyTask.cs
- InvariantComparer.cs
- OleDbConnectionInternal.cs
- SafeBitVector32.cs
- WindowsButton.cs
- TriggerBase.cs
- KeyConverter.cs
- AddressAccessDeniedException.cs
- Keyboard.cs
- DbConnectionPoolOptions.cs
- XmlAnyElementAttribute.cs
- CodeMethodReturnStatement.cs
- SafeBitVector32.cs
- QueryCacheKey.cs
- VScrollBar.cs
- LocalizableAttribute.cs
- SchemaNames.cs
- SqlDataSourceView.cs
- DBCSCodePageEncoding.cs
- ECDiffieHellmanPublicKey.cs
- LicFileLicenseProvider.cs
- DrawingContextWalker.cs
- BindingGroup.cs
- DoubleLink.cs
- XmlHelper.cs
- XamlStackWriter.cs
- DescendantQuery.cs
- PersonalizationProviderHelper.cs
- TextTreeNode.cs
- IntegerFacetDescriptionElement.cs
- EmbeddedObject.cs
- HelpEvent.cs
- FillErrorEventArgs.cs
- WebPartConnectionsCloseVerb.cs
- StringBlob.cs
- AxisAngleRotation3D.cs
- securitycriticaldataformultiplegetandset.cs
- ProfileSettingsCollection.cs
- ProgressBarBrushConverter.cs
- SystemBrushes.cs
- MessageBox.cs
- TokenBasedSet.cs
- ObjectView.cs
- ClientSettingsSection.cs
- ToolBar.cs
- StreamAsIStream.cs
- SchemaTableOptionalColumn.cs
- ImageClickEventArgs.cs
- StaticDataManager.cs
- WindowsServiceElement.cs
- DbDataSourceEnumerator.cs
- XpsException.cs
- TextEditorThreadLocalStore.cs
- CommonDialog.cs
- PixelFormats.cs
- ApplicationInterop.cs