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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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)
23 // Moonlight Team (moonlight-list@lists.ximian.com)
27 using System
.Collections
.Generic
;
29 using System
.Windows
.Automation
;
30 using System
.Windows
.Controls
;
31 using System
.Windows
.Markup
;
32 using System
.Windows
.Media
;
35 namespace System
.Windows
.Automation
.Peers
{
37 public class FrameworkElementAutomationPeer
: AutomationPeer
{
39 private FrameworkElement owner
;
41 public FrameworkElementAutomationPeer (FrameworkElement owner
)
44 throw new NullReferenceException ("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
;
57 control
.IsEnabledChanged
+= (o
, e
) => {
58 RaisePropertyChangedEvent (AutomationElementIdentifiers
.IsEnabledProperty
,
63 // SWA.AutomationProperties events
64 owner
.AcceleratorKeyChanged
+= (o
, e
) => {
65 RaisePropertyChangedEvent (AutomationElementIdentifiers
.AcceleratorKeyProperty
,
69 owner
.AccessKeyChanged
+= (o
, e
) => {
70 RaisePropertyChangedEvent (AutomationElementIdentifiers
.AccessKeyProperty
,
74 owner
.AutomationIdChanged
+= (o
, e
) => {
75 RaisePropertyChangedEvent (AutomationElementIdentifiers
.AutomationIdProperty
,
79 owner
.HelpTextChanged
+= (o
, e
) => {
80 RaisePropertyChangedEvent (AutomationElementIdentifiers
.HelpTextProperty
,
84 owner
.IsRequiredForFormChanged
+= (o
, e
) => {
85 RaisePropertyChangedEvent (AutomationElementIdentifiers
.IsRequiredForFormProperty
,
89 owner
.ItemStatusChanged
+= (o
, e
) => {
90 RaisePropertyChangedEvent (AutomationElementIdentifiers
.ItemStatusProperty
,
94 owner
.ItemTypeChanged
+= (o
, e
) => {
95 RaisePropertyChangedEvent (AutomationElementIdentifiers
.ItemTypeProperty
,
99 // LabeledBy and Name properties are "special" because they somehow depend on each other.
100 owner
.LabeledByChanged
+= (o
, e
) => {
101 RaisePropertyChangedEvent (AutomationElementIdentifiers
.LabeledByProperty
,
105 UIElement labeledByOld
= e
.OldValue
as UIElement
;
106 if (labeledByOld
!= null) {
107 FrameworkElementAutomationPeer peer
108 = FrameworkElementAutomationPeer
.CreatePeerForElement (labeledByOld
) as FrameworkElementAutomationPeer
;
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
;
117 peer
.NameChanged
+= LabeledBy_NameChanged
;
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
);
147 protected override List
<AutomationPeer
> GetChildrenCore ()
152 public override object GetPattern (PatternInterface pattern
)
157 public static AutomationPeer
FromElement (UIElement element
)
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 ()
236 protected override bool IsControlElementCore ()
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
251 protected override bool IsKeyboardFocusableCore ()
256 protected override bool IsOffscreenCore ()
261 protected override bool IsPasswordCore ()
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
)
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.
306 FrameworkElement parent
= VisualTreeHelper
.GetParent (element
) as FrameworkElement
;
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
);
316 return GetParentPeer (parent
);
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
{
332 internal virtual string ClassNameCore
{
336 internal virtual List
<AutomationPeer
> ChildrenCore
{
337 get { return GetChildrenRecursively (owner); }
340 internal virtual bool PasswordCore
{
341 get { return false; }
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
;
359 = FrameworkElementAutomationPeer
.CreatePeerForElement (element
);
363 List
<AutomationPeer
> returnedChildren
364 = GetChildrenRecursively (element
);
365 if (returnedChildren
!= null)
366 children
.AddRange (returnedChildren
);
370 if (children
.Count
== 0)
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);
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
) { }
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
))
400 if (double.IsNaN (height
))
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.
407 height
= (double) owner
.GetValue (FrameworkElement
.ActualHeightProperty
);
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
)
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
,
432 if (NameChanged
!= null)
433 NameChanged (this, EventArgs
.Empty
);