[corlib] Avoid unnecessary ephemeron array resizes
[mono.git] / mcs / class / referencesource / System.Activities.Presentation / System.Activities.Presentation / System / Activities / Presentation / Base / Core / Internal / PropertyEditing / Selection / PropertySelection.cs
1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Internal.PropertyEditing.Selection
5 {
6     using System;
7     using System.ComponentModel;
8     using System.Diagnostics;
9     using System.Diagnostics.CodeAnalysis;
10     using System.Windows;
11     using System.Windows.Input;
12     using System.Windows.Media;
13
14     using System.Runtime;
15     using System.Activities.Presentation.Internal.PropertyEditing.Selection;
16     using System.Activities.Presentation;
17
18     // <summary>
19     // This is a container for attached properties used by PropertyInspector to track and manage
20     // property selection.  It is public because WPF requires that attached properties used in XAML
21     // be declared by public classes.
22     // </summary>
23     [EditorBrowsable(EditorBrowsableState.Never)]
24     static class PropertySelection
25     {
26
27         private static readonly DependencyPropertyKey IsSelectedPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
28             "IsSelected",
29             typeof(bool),
30             typeof(PropertySelection),
31             new PropertyMetadata(false));
32
33         // <summary>
34         // Attached, ReadOnly DP that we use to mark objects as selected.  If they care, they can then render
35         // themselves differently.
36         // </summary>
37         internal static readonly DependencyProperty IsSelectedProperty = IsSelectedPropertyKey.DependencyProperty;
38
39         // <summary>
40         // Attached DP that we use in XAML to mark elements that can be selected.
41         // </summary>
42         internal static readonly DependencyProperty SelectionStopProperty = DependencyProperty.RegisterAttached(
43             "SelectionStop",
44             typeof(ISelectionStop),
45             typeof(PropertySelection),
46             new PropertyMetadata(null));
47
48         // <summary>
49         // Attached DP used in conjunction with SelectionStop DP.  It specifies the FrameworkElement to hook into
50         // in order to handle double-click events to control the expanded / collapsed state of its parent SelectionStop.
51         // </summary>
52         internal static readonly DependencyProperty IsSelectionStopDoubleClickTargetProperty = DependencyProperty.RegisterAttached(
53             "IsSelectionStopDoubleClickTarget",
54             typeof(bool),
55             typeof(PropertySelection),
56             new PropertyMetadata(false, new PropertyChangedCallback(OnIsSelectionStopDoubleClickTargetChanged)));
57
58         // <summary>
59         // Attached DP that we use in XAML to mark elements as selection scopes - meaning selection
60         // won't spill beyond the scope of the marked element.
61         // </summary>
62         internal static readonly DependencyProperty IsSelectionScopeProperty = DependencyProperty.RegisterAttached(
63             "IsSelectionScope",
64             typeof(bool),
65             typeof(PropertySelection),
66             new PropertyMetadata(false));
67
68         // <summary>
69         // Attached property we use to route non-navigational key strokes from one FrameworkElement to
70         // another.  When this property is set on a FrameworkElement, we hook into its KeyDown event
71         // and send any unhandled, non-navigational key strokes to the FrameworkElement specified
72         // by this property.  The target FrameworkElement must be focusable or have a focusable child.
73         // When the first eligible key stroke is detected, the focus will be shifted to the focusable
74         // element and the key stroke will be sent to it.
75         // </summary>
76         internal static readonly DependencyProperty KeyDownTargetProperty = DependencyProperty.RegisterAttached(
77             "KeyDownTarget",
78             typeof(FrameworkElement),
79             typeof(PropertySelection),
80             new PropertyMetadata(null, new PropertyChangedCallback(OnKeyDownTargetChanged)));
81
82         // Constant that determines how deep in the visual tree we search for SelectionStops that
83         // are children or neighbors of a given element (usually one that the user clicked on) before
84         // giving up.  This constant is UI-dependent.
85         private const int MaxSearchDepth = 11;
86
87         // <summary>
88         // Gets PropertySelection.IsSelected property from the specified DependencyObject
89         // </summary>
90         // <param name="obj">DependencyObject to examine</param>
91         // <returns>Value of the IsSelected property</returns>
92         internal static bool GetIsSelected(DependencyObject obj)
93         {
94             if (obj == null)
95             {
96                 throw FxTrace.Exception.ArgumentNull("obj");
97             }
98
99             return (bool)obj.GetValue(IsSelectedProperty);
100         }
101
102         // Private (internal) setter that we use to mark objects as selected from within CategoryList class
103         //
104         internal static void SetIsSelected(DependencyObject obj, bool value)
105         {
106             if (obj == null)
107             {
108                 throw FxTrace.Exception.ArgumentNull("obj");
109             }
110
111             obj.SetValue(IsSelectedPropertyKey, value);
112         }
113
114
115         // SelectionStop Attached DP
116
117         // <summary>
118         // Gets PropertySelection.SelectionStop property from the specified DependencyObject
119         // </summary>
120         // <param name="obj">DependencyObject to examine</param>
121         // <returns>Value of the SelectionStop property.</returns>
122         internal static ISelectionStop GetSelectionStop(DependencyObject obj)
123         {
124             if (obj == null)
125             {
126                 throw FxTrace.Exception.ArgumentNull("obj");
127             }
128
129             return (ISelectionStop)obj.GetValue(SelectionStopProperty);
130         }
131
132         // <summary>
133         // Sets PropertySelection.SelectionStop property on the specified DependencyObject
134         // </summary>
135         // <param name="obj">DependencyObject to modify</param>
136         // <param name="value">New value of SelectionStop</param>
137         internal static void SetSelectionStop(DependencyObject obj, ISelectionStop value)
138         {
139             if (obj == null)
140             {
141                 throw FxTrace.Exception.ArgumentNull("obj");
142             }
143
144             obj.SetValue(SelectionStopProperty, value);
145         }
146
147         // <summary>
148         // Clears PropertySelection.SelectionStop property from the specified DependencyObject
149         // </summary>
150         // <param name="obj">DependencyObject to clear</param>
151         internal static void ClearSelectionStop(DependencyObject obj)
152         {
153             if (obj == null)
154             {
155                 throw FxTrace.Exception.ArgumentNull("obj");
156             }
157
158             obj.ClearValue(SelectionStopProperty);
159         }
160
161
162         // IsSelectionStopDoubleClickTarget Attached DP
163
164         // <summary>
165         // Gets PropertySelection.IsSelectionStopDoubleClickTarget property from the specified DependencyObject
166         // </summary>
167         // <param name="obj">DependencyObject to examine</param>
168         // <returns>Value of the IsSelectionStopDoubleClickTarget property.</returns>
169         internal static bool GetIsSelectionStopDoubleClickTarget(DependencyObject obj)
170         {
171             if (obj == null)
172             {
173                 throw FxTrace.Exception.ArgumentNull("obj");
174             }
175
176             return (bool)obj.GetValue(IsSelectionStopDoubleClickTargetProperty);
177         }
178
179         // <summary>
180         // Sets PropertySelection.IsSelectionStopDoubleClickTarget property on the specified DependencyObject
181         // </summary>
182         // <param name="obj">DependencyObject to modify</param>
183         // <param name="value">New value of IsSelectionStopDoubleClickTarget</param>
184         internal static void SetIsSelectionStopDoubleClickTarget(DependencyObject obj, bool value)
185         {
186             if (obj == null)
187             {
188                 throw FxTrace.Exception.ArgumentNull("obj");
189             }
190
191             obj.SetValue(IsSelectionStopDoubleClickTargetProperty, value);
192         }
193
194         // <summary>
195         // Clears PropertySelection.IsSelectionStopDoubleClickTarget property from the specified DependencyObject
196         // </summary>
197         // <param name="obj">DependencyObject to modify</param>
198         internal static void ClearIsSelectionStopDoubleClickTarget(DependencyObject obj)
199         {
200             if (obj == null)
201             {
202                 throw FxTrace.Exception.ArgumentNull("obj");
203             }
204
205             obj.ClearValue(IsSelectionStopDoubleClickTargetProperty);
206         }
207
208         // Called when some object gets specified as the SelectionStop double-click target:
209         //
210         //      * Hook into the MouseDown event so that we can detect double-clicks and automatically
211         //        expand or collapse the corresponding SelectionStop, if possible
212         //
213         private static void OnIsSelectionStopDoubleClickTargetChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
214         {
215             FrameworkElement target = sender as FrameworkElement;
216             if (target == null)
217             {
218                 return;
219             }
220
221             if (bool.Equals(e.OldValue, false) && bool.Equals(e.NewValue, true))
222             {
223                 AddDoubleClickHandler(target);
224             }
225             else if (bool.Equals(e.OldValue, true) && bool.Equals(e.NewValue, false))
226             {
227                 RemoveDoubleClickHandler(target);
228             }
229         }
230
231         // Called when some SelectionStop double-click target gets unloaded:
232         //
233         //      * Unhook from events so that we don't prevent garbage collection
234         //
235         private static void OnSelectionStopDoubleClickTargetUnloaded(object sender, RoutedEventArgs e)
236         {
237             FrameworkElement target = sender as FrameworkElement;
238             Fx.Assert(target != null, "sender parameter should not be null");
239
240             if (target == null)
241             {
242                 return;
243             }
244
245             RemoveDoubleClickHandler(target);
246         }
247
248         // Called when the UI object representing a SelectionStop gets clicked:
249         //
250         //      * If this is a double-click and the SelectionStop can be expanded / collapsed,
251         //        expand / collapse the SelectionStop
252         //
253         private static void OnSelectionStopDoubleClickTargetMouseDown(object sender, MouseButtonEventArgs e)
254         {
255             DependencyObject target = e.OriginalSource as DependencyObject;
256             if (target == null)
257             {
258                 return;
259             }
260
261             if (e.ClickCount > 1)
262             {
263
264                 FrameworkElement parentSelectionStopVisual = PropertySelection.FindParentSelectionStop<FrameworkElement>(target);
265                 if (parentSelectionStopVisual != null)
266                 {
267
268                     ISelectionStop parentSelectionStop = PropertySelection.GetSelectionStop(parentSelectionStopVisual);
269                     if (parentSelectionStop != null && parentSelectionStop.IsExpandable)
270                     {
271                         parentSelectionStop.IsExpanded = !parentSelectionStop.IsExpanded;
272                     }
273                 }
274             }
275         }
276
277         private static void AddDoubleClickHandler(FrameworkElement target)
278         {
279             target.AddHandler(UIElement.MouseDownEvent, new MouseButtonEventHandler(OnSelectionStopDoubleClickTargetMouseDown), false);
280             target.Unloaded += new RoutedEventHandler(OnSelectionStopDoubleClickTargetUnloaded);
281         }
282
283         private static void RemoveDoubleClickHandler(FrameworkElement target)
284         {
285             target.Unloaded -= new RoutedEventHandler(OnSelectionStopDoubleClickTargetUnloaded);
286             target.RemoveHandler(UIElement.MouseDownEvent, new MouseButtonEventHandler(OnSelectionStopDoubleClickTargetMouseDown));
287         }
288
289
290         // IsSelectionScope Attached DP
291
292         // <summary>
293         // Gets PropertySelection.IsSelectionScope property from the specified DependencyObject
294         // </summary>
295         // <param name="obj">DependencyObject to examine</param>
296         // <returns>Value of the IsSelectionScope property.</returns>
297         internal static bool GetIsSelectionScope(DependencyObject obj)
298         {
299             if (obj == null)
300             {
301                 throw FxTrace.Exception.ArgumentNull("obj");
302             }
303
304             return (bool)obj.GetValue(IsSelectionScopeProperty);
305         }
306
307         // <summary>
308         // Sets PropertySelection.IsSelectionScope property on the specified DependencyObject
309         // </summary>
310         // <param name="obj">DependencyObject to modify</param>
311         // <param name="value">New value of IsSelectionScope</param>
312         internal static void SetIsSelectionScope(DependencyObject obj, bool value)
313         {
314             if (obj == null)
315             {
316                 throw FxTrace.Exception.ArgumentNull("obj");
317             }
318
319             obj.SetValue(IsSelectionScopeProperty, value);
320         }
321
322         // KeyDownTarget Attached DP
323
324         // <summary>
325         // Gets PropertySelection.KeyDownTarget property from the specified DependencyObject
326         // </summary>
327         // <param name="obj">DependencyObject to examine</param>
328         // <returns>Value of the KeyDownTarget property.</returns>
329         internal static FrameworkElement GetKeyDownTarget(DependencyObject obj)
330         {
331             if (obj == null)
332             {
333                 throw FxTrace.Exception.ArgumentNull("obj");
334             }
335
336             return (FrameworkElement)obj.GetValue(KeyDownTargetProperty);
337         }
338
339         // <summary>
340         // Sets PropertySelection.KeyDownTarget property on the specified DependencyObject
341         // </summary>
342         // <param name="obj">DependencyObject to modify</param>
343         // <param name="value">New value of KeyDownTarget</param>
344         internal static void SetKeyDownTarget(DependencyObject obj, FrameworkElement value)
345         {
346             if (obj == null)
347             {
348                 throw FxTrace.Exception.ArgumentNull("obj");
349             }
350
351             obj.SetValue(KeyDownTargetProperty, value);
352         }
353
354         // Called when some FrameworkElement gets specified as the target for KeyDown RoutedEvents -
355         // hook into / unhook from the KeyDown event of the source
356         private static void OnKeyDownTargetChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
357         {
358             FrameworkElement target = sender as FrameworkElement;
359             if (target == null)
360             {
361                 return;
362             }
363
364             if (e.OldValue != null && e.NewValue == null)
365             {
366                 RemoveKeyStrokeHandlers(target);
367             }
368             else if (e.NewValue != null && e.OldValue == null)
369             {
370                 AddKeyStrokeHandlers(target);
371             }
372         }
373
374         // Called when a KeyDownTarget gets unloaded -
375         // unhook from events so that we don't prevent garbage collection
376         private static void OnKeyDownTargetUnloaded(object sender, RoutedEventArgs e)
377         {
378             FrameworkElement target = sender as FrameworkElement;
379             Fx.Assert(target != null, "sender parameter should not be null");
380
381             if (target == null)
382             {
383                 return;
384             }
385
386             RemoveKeyStrokeHandlers(target);
387         }
388
389         // Called when a KeyDownTarget is specified and a KeyDown event is detected on the source
390         private static void OnKeyDownTargetKeyDown(object sender, KeyEventArgs e)
391         {
392
393             // Ignore handled events
394             if (e.Handled)
395             {
396                 return;
397             }
398
399             // Ignore navigation keys
400             if (e.Key == Key.Left || e.Key == Key.Right || e.Key == Key.Up || e.Key == Key.Down ||
401                 e.Key == Key.Tab || e.Key == Key.Escape || e.Key == Key.Return || e.Key == Key.Enter ||
402                 e.Key == Key.PageUp || e.Key == Key.PageDown || e.Key == Key.Home || e.Key == Key.End || e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl)
403             {
404                 return;
405             }
406
407             if (Keyboard.Modifiers == ModifierKeys.Control)
408             {
409                 return;
410             }
411
412
413
414             DependencyObject keySender = sender as DependencyObject;
415             Fx.Assert(keySender != null, "keySender should not be null");
416             if (keySender == null)
417             {
418                 return;
419             }
420
421             FrameworkElement keyTarget = GetKeyDownTarget(keySender);
422             Fx.Assert(keyTarget != null, "keyTarget should not be null");
423             if (keyTarget == null)
424             {
425                 return;
426             }
427
428             // Find a focusable element on the target, set focus to it, and send the keys over
429             FrameworkElement focusable = VisualTreeUtils.FindFocusableElement<FrameworkElement>(keyTarget);
430             if (focusable != null && focusable == Keyboard.Focus(focusable))
431             {
432                 focusable.RaiseEvent(e);
433             }
434         }
435
436         private static void AddKeyStrokeHandlers(FrameworkElement target)
437         {
438             target.AddHandler(UIElement.KeyDownEvent, new KeyEventHandler(OnKeyDownTargetKeyDown), false);
439             target.Unloaded += new RoutedEventHandler(OnKeyDownTargetUnloaded);
440         }
441
442         private static void RemoveKeyStrokeHandlers(FrameworkElement target)
443         {
444             target.Unloaded -= new RoutedEventHandler(OnKeyDownTargetUnloaded);
445             target.RemoveHandler(UIElement.KeyDownEvent, new KeyEventHandler(OnKeyDownTargetKeyDown));
446         }
447
448
449         // <summary>
450         // Returns the closest parent (or the element itself) marked as a SelectionStop.
451         // </summary>
452         // <typeparam name="T">Type of element to look for</typeparam>
453         // <param name="element">Element to examine</param>
454         // <returns>The closest parent (or the element itself) marked as a SelectionStop;
455         // null if not found.</returns>
456         internal static T FindParentSelectionStop<T>(DependencyObject element) where T : DependencyObject
457         {
458             if (element == null)
459             {
460                 return null;
461             }
462
463             do
464             {
465                 // IsEligibleSelectionStop already checks for visibility, so we don't need to
466                 // to do a specific check somewhere else in this loop
467                 if (IsEligibleSelectionStop<T>(element))
468                 {
469                     return (T)element;
470                 }
471
472                 element = VisualTreeHelper.GetParent(element);
473             } while (element != null);
474
475             return null;
476         }
477
478         // <summary>
479         // Returns the closest neighbor in the given direction marked as a SelectionStop.
480         // </summary>
481         // <typeparam name="T">Type of element to look for</typeparam>
482         // <param name="element">Element to examine</param>
483         // <param name="direction">Direction to search in</param>
484         // <returns>The closest neighboring element in the given direction marked as a IsSelectionStop,
485         // if found, null otherwise.</returns>
486         internal static T FindNeighborSelectionStop<T>(DependencyObject element, SearchDirection direction) where T : DependencyObject
487         {
488
489             if (element == null)
490             {
491                 throw FxTrace.Exception.ArgumentNull("element");
492             }
493
494             T neighbor;
495             int maxSearchDepth = MaxSearchDepth;
496
497             // If we are looking for the NEXT element and we can dig deeper, start by digging deeper
498             // before trying to look for any siblings.
499             //
500             if (direction == SearchDirection.Next && IsExpanded(element))
501             {
502                 neighbor = FindChildSelectionStop<T>(element, 0, VisualTreeHelper.GetChildrenCount(element) - 1, direction, maxSearchDepth, MatchDirection.Down);
503
504                 if (neighbor != null)
505                 {
506                     return neighbor;
507                 }
508             }
509
510             int childIndex, childrenCount, childDepth;
511             bool isParentSelectionStop, isParentSelectionScope = false;
512             DependencyObject parent = element;
513
514             while (true)
515             {
516                 while (true)
517                 {
518                     // If we reached the selection scope, don't try to go beyond it
519                     if (isParentSelectionScope)
520                     {
521                         return null;
522                     }
523
524                     parent = GetEligibleParent(parent, out childIndex, out childrenCount, out childDepth, out isParentSelectionStop, out isParentSelectionScope);
525                     maxSearchDepth += childDepth;
526
527                     if (parent == null)
528                     {
529                         return null;
530                     }
531
532                     if (direction == SearchDirection.Next && (childIndex + 1) >= childrenCount)
533                     {
534                         continue;
535                     }
536
537                     if (direction == SearchDirection.Previous && isParentSelectionStop == false && (childIndex < 1))
538                     {
539                         continue;
540                     }
541
542                     break;
543                 }
544
545                 // If we get here, that means we found a SelectionStop on which we need to look for children that are
546                 // SelectionStops themselves.  The first such child found should be returned.  Otherwise, if no such child
547                 // is found, we potentially look at the node itself and return it OR we repeat the process and keep looking
548                 // for a better parent.
549
550                 int leftIndex, rightIndex;
551                 MatchDirection matchDirection;
552
553                 if (direction == SearchDirection.Previous)
554                 {
555                     leftIndex = 0;
556                     rightIndex = childIndex - 1;
557                     matchDirection = MatchDirection.Up;
558                 }
559                 else
560                 {
561                     leftIndex = childIndex + 1;
562                     rightIndex = childrenCount - 1;
563                     matchDirection = MatchDirection.Down;
564                 }
565
566                 neighbor = FindChildSelectionStop<T>(parent, leftIndex, rightIndex, direction, maxSearchDepth, matchDirection);
567                 if (neighbor != null)
568                 {
569                     return neighbor;
570                 }
571
572                 if (direction == SearchDirection.Previous &&
573                     IsEligibleSelectionStop<T>(parent))
574                 {
575                     return (T)parent;
576                 }
577             }
578         }
579
580         // Helper method used from GetNeighborSelectionStop()
581         // Returns a parent DependencyObject of the specified element that is
582         // 
583         //  * Visible AND
584         //  * ( Marked with a SelectionStop OR
585         //  *   Marked with IsSelectionScope = true OR
586         //  *   Has more than one child )
587         //
588         private static DependencyObject GetEligibleParent(DependencyObject element, out int childIndex, out int childrenCount, out int childDepth, out bool isSelectionStop, out bool isSelectionScope)
589         {
590             childDepth = 0;
591             isSelectionStop = false;
592             isSelectionScope = false;
593             bool isVisible;
594
595             do
596             {
597                 element = VisualTreeUtils.GetIndexedVisualParent(element, out childrenCount, out childIndex);
598                 isSelectionStop = element == null ? false : (GetSelectionStop(element) != null);
599                 isSelectionScope = element == null ? false : GetIsSelectionScope(element);
600                 isVisible = VisualTreeUtils.IsVisible(element as UIElement);
601
602                 childDepth++;
603             }
604             while (
605                 element != null &&
606                 (isVisible == false ||
607                 (isSelectionStop == false &&
608                 isSelectionScope == false &&
609                 childrenCount < 2)));
610
611             return element;
612         }
613
614         // Helper method that performs a recursive, depth-first search of children starting at the specified parent,
615         // looking for any children that conform to the specified Type and are marked with a SelectionStop
616         //
617         private static T FindChildSelectionStop<T>(DependencyObject parent, int leftIndex, int rightIndex, SearchDirection iterationDirection, int maxDepth, MatchDirection matchDirection)
618             where T : DependencyObject
619         {
620
621             if (parent == null || maxDepth <= 0)
622             {
623                 return null;
624             }
625
626             int step = iterationDirection == SearchDirection.Next ? 1 : -1;
627             int index = iterationDirection == SearchDirection.Next ? leftIndex : rightIndex;
628
629             for (; index >= leftIndex && index <= rightIndex; index = index + step)
630             {
631
632                 DependencyObject child = VisualTreeHelper.GetChild(parent, index);
633
634                 // If MatchDirection is set to Down, do an eligibility match BEFORE we dive down into
635                 // more children.
636                 //
637                 if (matchDirection == MatchDirection.Down && IsEligibleSelectionStop<T>(child))
638                 {
639                     return (T)child;
640                 }
641
642                 // If this child is not an eligible SelectionStop because it is not visible,
643                 // there is no point digging down to get to more children.
644                 //
645                 if (!VisualTreeUtils.IsVisible(child as UIElement))
646                 {
647                     continue;
648                 }
649
650                 int grandChildrenCount = VisualTreeHelper.GetChildrenCount(child);
651                 if (grandChildrenCount > 0 && IsExpanded(child))
652                 {
653                     T element = FindChildSelectionStop<T>(child, 0, grandChildrenCount - 1, iterationDirection, maxDepth - 1, matchDirection);
654
655                     if (element != null)
656                     {
657                         return element;
658                     }
659                 }
660
661                 // If MatchDirection is set to Up, do an eligibility match AFTER we tried diving into
662                 // more children and failed to find something we could return.
663                 //
664                 if (matchDirection == MatchDirection.Up && IsEligibleSelectionStop<T>(child))
665                 {
666                     return (T)child;
667                 }
668             }
669
670             return null;
671         }
672
673         // Helper method that returns false if the given element is a collapsed SelectionStop,
674         // true otherwise.
675         //
676         private static bool IsExpanded(DependencyObject element)
677         {
678             ISelectionStop selectionStop = PropertySelection.GetSelectionStop(element);
679             return selectionStop == null || selectionStop.IsExpanded;
680         }
681
682         // Helper method that return true if the given element is marked with a SelectionStop,
683         // if it derives from the specified Type, and if it is Visible (assuming it derives from UIElement)
684         //
685         private static bool IsEligibleSelectionStop<T>(DependencyObject element) where T : DependencyObject
686         {
687             return (GetSelectionStop(element) != null) && typeof(T).IsAssignableFrom(element.GetType()) && VisualTreeUtils.IsVisible(element as UIElement);
688         }
689
690         // <summary>
691         // Private enum we use to specify whether FindSelectionStopChild() should return any matches
692         // as it drills down into the visual tree (Down) or whether it should wait on looking at
693         // matches until it's bubbling back up again (Up).
694         // </summary>
695         private enum MatchDirection
696         {
697             Down,
698             Up
699         }
700
701         // IsSelected ReadOnly, Attached DP
702     }
703 }