2009-11-17 Jeffrey Stedfast <fejj@novell.com>
[moon.git] / class / System.Windows / System.Windows.Automation.Peers / FrameworkElementAutomationPeer.cs
blob83156817822b1710f8fdfd6cbf333082a376494e
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 // Copyright (c) 2008 Novell, Inc. (http://www.novell.com)
22 // Contact:
23 // Moonlight Team (moonlight-list@lists.ximian.com)
26 using System;
27 using System.Collections.Generic;
28 using System.Windows;
29 using System.Windows.Automation;
30 using System.Windows.Controls;
31 using System.Windows.Markup;
32 using System.Windows.Media;
33 using System.Linq;
35 namespace System.Windows.Automation.Peers {
37 public class FrameworkElementAutomationPeer : AutomationPeer {
39 private FrameworkElement owner;
41 public FrameworkElementAutomationPeer (FrameworkElement owner)
43 if (owner == null)
44 throw new NullReferenceException ("owner");
45 this.owner = owner;
47 // Default Automation events
48 owner.SizeChanged += (o, s) => {
49 Point location = GetLocation (owner);
50 RaisePropertyChangedEvent (AutomationElementIdentifiers.BoundingRectangleProperty,
51 new Rect (0, 0, s.PreviousSize.Width, s.PreviousSize.Height),
52 new Rect (location.X, location.Y, s.NewSize.Width, s.NewSize.Height));
55 Control control = owner as Control;
56 if (control != null)
57 control.IsEnabledChanged += (o, e) => {
58 RaisePropertyChangedEvent (AutomationElementIdentifiers.IsEnabledProperty,
59 e.OldValue,
60 e.NewValue);
63 // SWA.AutomationProperties events
64 owner.AcceleratorKeyChanged += (o, e) => {
65 RaisePropertyChangedEvent (AutomationElementIdentifiers.AcceleratorKeyProperty,
66 e.OldValue,
67 e.NewValue);
69 owner.AccessKeyChanged += (o, e) => {
70 RaisePropertyChangedEvent (AutomationElementIdentifiers.AccessKeyProperty,
71 e.OldValue,
72 e.NewValue);
74 owner.AutomationIdChanged += (o, e) => {
75 RaisePropertyChangedEvent (AutomationElementIdentifiers.AutomationIdProperty,
76 e.OldValue,
77 e.NewValue);
79 owner.HelpTextChanged += (o, e) => {
80 RaisePropertyChangedEvent (AutomationElementIdentifiers.HelpTextProperty,
81 e.OldValue,
82 e.NewValue);
84 owner.IsRequiredForFormChanged += (o, e) => {
85 RaisePropertyChangedEvent (AutomationElementIdentifiers.IsRequiredForFormProperty,
86 e.OldValue,
87 e.NewValue);
89 owner.ItemStatusChanged += (o, e) => {
90 RaisePropertyChangedEvent (AutomationElementIdentifiers.ItemStatusProperty,
91 e.OldValue,
92 e.NewValue);
94 owner.ItemTypeChanged += (o, e) => {
95 RaisePropertyChangedEvent (AutomationElementIdentifiers.ItemTypeProperty,
96 e.OldValue,
97 e.NewValue);
99 // LabeledBy and Name properties are "special" because they somehow depend on each other.
100 owner.LabeledByChanged += (o, e) => {
101 RaisePropertyChangedEvent (AutomationElementIdentifiers.LabeledByProperty,
102 e.OldValue,
103 e.NewValue);
104 // Name property
105 UIElement labeledByOld = e.OldValue as UIElement;
106 if (labeledByOld != null) {
107 FrameworkElementAutomationPeer peer
108 = FrameworkElementAutomationPeer.CreatePeerForElement (labeledByOld) as FrameworkElementAutomationPeer;
109 if (peer != null)
110 peer.NameChanged -= LabeledBy_NameChanged;
112 UIElement labeledByNew = e.NewValue as UIElement;
113 if (labeledByNew != null) {
114 FrameworkElementAutomationPeer peer
115 = FrameworkElementAutomationPeer.CreatePeerForElement (labeledByNew) as FrameworkElementAutomationPeer;
116 if (peer != null)
117 peer.NameChanged += LabeledBy_NameChanged;
119 RaiseNameChanged ();
121 owner.NameChanged += (o, e) => RaiseNameChanged ();
124 public UIElement Owner {
125 get { return owner; }
128 protected override string GetNameCore ()
130 return owner.GetValue (AutomationProperties.NameProperty) as string ?? string.Empty;
133 protected override string GetItemTypeCore ()
135 return owner.GetValue (AutomationProperties.ItemTypeProperty) as string ?? string.Empty;
138 protected override AutomationPeer GetLabeledByCore ()
140 UIElement labeledBy = owner.GetValue (AutomationProperties.LabeledByProperty) as UIElement;
141 if (labeledBy != null)
142 return FrameworkElementAutomationPeer.CreatePeerForElement (labeledBy);
143 else
144 return null;
147 protected override List<AutomationPeer> GetChildrenCore ()
149 return ChildrenCore;
152 public override object GetPattern (PatternInterface pattern)
154 return null;
157 public static AutomationPeer FromElement (UIElement element)
159 if (element == null)
160 throw new ArgumentNullException ("element");
161 return element.AutomationPeer;
164 protected override string GetAcceleratorKeyCore ()
166 return owner.GetValue (AutomationProperties.AcceleratorKeyProperty) as string ?? string.Empty;
169 protected override string GetAccessKeyCore ()
171 return owner.GetValue (AutomationProperties.AccessKeyProperty) as string ?? string.Empty;
174 protected override AutomationControlType GetAutomationControlTypeCore ()
176 // Some Peers don't override GetAutomationControlTypeCore and return something different to Custom
177 // for example: TextBoxAutomationPeer
178 return AutomationControlTypeCore.HasValue ? AutomationControlTypeCore.Value : AutomationControlType.Custom;
181 protected override string GetAutomationIdCore ()
183 return owner.GetValue (AutomationProperties.AutomationIdProperty) as string ?? string.Empty;
186 protected override Rect GetBoundingRectangleCore ()
188 if (VisualTreeHelper.GetParent (owner) == null)
189 return new Rect (0, 0, 0, 0);
191 return GetBoundingRectangleFrom (owner);
194 protected override string GetClassNameCore ()
196 return ClassNameCore ?? string.Empty;
199 protected override Point GetClickablePointCore ()
201 return new Point (0, 0);
204 protected override string GetHelpTextCore ()
206 return owner.GetValue (AutomationProperties.HelpTextProperty) as string ?? string.Empty;
209 protected override string GetItemStatusCore ()
211 return owner.GetValue (AutomationProperties.ItemStatusProperty) as string ?? string.Empty;
214 protected override string GetLocalizedControlTypeCore ()
216 // LAMESPEC: http://msdn.microsoft.com/en-us/library/ms743581.aspx
217 // "CamelCase" literal values should be "camel case", not "camelcase"
218 return GetAutomationControlType ().ToString ().ToLower ();
221 protected override AutomationOrientation GetOrientationCore ()
223 return AutomationOrientation.None;
226 protected override bool HasKeyboardFocusCore ()
228 return owner == System.Windows.Input.FocusManager.GetFocusedElement ();
231 protected override bool IsContentElementCore ()
233 return true;
236 protected override bool IsControlElementCore ()
238 return true;
241 protected override bool IsEnabledCore ()
243 Control ownerAsControl = owner as Control;
244 if (ownerAsControl != null)
245 return ownerAsControl.IsEnabled;
247 // Fall back to default value
248 return true;
251 protected override bool IsKeyboardFocusableCore ()
253 return false;
256 protected override bool IsOffscreenCore ()
258 return false;
261 protected override bool IsPasswordCore ()
263 return PasswordCore;
266 protected override bool IsRequiredForFormCore ()
268 bool? isRequired = (bool?) owner.GetValue (AutomationProperties.IsRequiredForFormProperty);
269 return isRequired.HasValue ? isRequired.Value : false;
272 protected override void SetFocusCore ()
274 Control ownerAsControl = owner as Control;
275 if (ownerAsControl != null)
276 ownerAsControl.Focus ();
279 public static AutomationPeer CreatePeerForElement (UIElement element)
281 if (element == null)
282 throw new ArgumentNullException ("element");
283 if (element.AutomationPeer == null) {
284 element.AutomationPeer = element.CreateAutomationPeer ();
285 // We need to cache old values to raise PropertyChanged events
286 // when calling AutomationPeer.InvalidatePeer()
287 if (element.AutomationPeer != null)
288 element.AutomationPeer.CacheMainProperties ();
290 return element.AutomationPeer;
293 internal override AutomationPeer GetParentCore ()
295 return GetParentPeer (owner);
298 #region Private Methods
300 private AutomationPeer GetParentPeer (FrameworkElement element)
302 // We are returning parents of children already instantiated.
303 if (element == null)
304 return null;
306 FrameworkElement parent = VisualTreeHelper.GetParent (element) as FrameworkElement;
307 if (parent == null)
308 return null;
310 // Some parents don't return an Automation Peer (for example: Border or Panel subclasses)
311 // We need to create the Peer because some Peers return children that don't
312 // necesarily return this peer when calling GetParent
313 // (for example: ListBox when Template is not null)
314 AutomationPeer peer = FrameworkElementAutomationPeer.CreatePeerForElement (parent);
315 if (peer == null)
316 return GetParentPeer (parent);
317 else
318 return peer;
321 #endregion
323 #region Internal properties
325 // Internal properties used by Peers that don't override XXXXCore and return something else
326 // than FrameworkElementAutomationPeer's default value.
328 internal virtual AutomationControlType? AutomationControlTypeCore {
329 get { return null; }
332 internal virtual string ClassNameCore {
333 get { return null; }
336 internal virtual List<AutomationPeer> ChildrenCore {
337 get { return GetChildrenRecursively (owner); }
340 internal virtual bool PasswordCore {
341 get { return false; }
344 #endregion
346 #region Internal methods
348 internal static List<AutomationPeer> GetChildrenRecursively (UIElement uielement)
350 List<AutomationPeer> children = new List<AutomationPeer> ();
351 int childrenCount = VisualTreeHelper.GetChildrenCount (uielement);
353 for (int child = 0; child < childrenCount; child++) {
354 UIElement element = VisualTreeHelper.GetChild (uielement, child) as UIElement;
355 if (element == null)
356 continue;
358 AutomationPeer peer
359 = FrameworkElementAutomationPeer.CreatePeerForElement (element);
360 if (peer != null)
361 children.Add (peer);
362 else {
363 List<AutomationPeer> returnedChildren
364 = GetChildrenRecursively (element);
365 if (returnedChildren != null)
366 children.AddRange (returnedChildren);
370 if (children.Count == 0)
371 return null;
373 return children;
376 internal Point GetLocation (FrameworkElement owner)
378 if (VisualTreeHelper.GetParent (owner) == null)
379 return new Point (0, 0);
381 Point point = new Point (0, 0);
382 try {
383 // This happens when an item is not visible yet but exists, for
384 // example ListBoxItems in ComboBox when Template is not null
385 point = owner.TransformToVisual (Application.Current.RootVisual).Transform (new Point ());
386 } catch (ArgumentException) { }
388 return point;
391 internal Rect GetBoundingRectangleFrom (FrameworkElement owner)
393 Point location = GetLocation (owner);
395 double width = (double) owner.GetValue (FrameworkElement.WidthProperty);
396 double height = (double) owner.GetValue (FrameworkElement.HeightProperty);
398 if (double.IsNaN (width))
399 width = 0;
400 if (double.IsNaN (height))
401 height = 0;
403 // Some Controls may not be honoring the specified Height or Width and would
404 // use a different value, that's why we need to test these properties too,
405 // Examples of those Controls are TextBlock and Image.
406 if (height == 0)
407 height = (double) owner.GetValue (FrameworkElement.ActualHeightProperty);
408 if (width == 0)
409 width = (double) owner.GetValue (FrameworkElement.ActualWidthProperty);
411 return new Rect (location.X, location.Y, width, height);
414 internal event EventHandler NameChanged;
416 private void LabeledBy_NameChanged (object sender, EventArgs args)
418 RaiseNameChanged ();
421 // Raises UIA Event (NameProperty) and internal event (NameChanged)
422 // NOTE: This method MUST BE called by AutomationPeers overriding GetNameCore().
423 internal override void RaiseNameChanged ()
425 IAutomationCacheProperty cachedProperty
426 = GetCachedProperty (AutomationElementIdentifiers.NameProperty);
427 string newValue = GetName ();
428 if (!object.Equals (newValue, cachedProperty.OldValue)) {
429 RaisePropertyChangedEvent (AutomationElementIdentifiers.NameProperty,
430 cachedProperty.OldValue,
431 newValue);
432 if (NameChanged != null)
433 NameChanged (this, EventArgs.Empty);
438 #endregion