1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 //----------------------------------------------------------------
4 namespace System.Activities.Presentation.Internal.PropertyEditing.Automation
7 using System.Collections.Generic;
8 using System.ComponentModel;
9 using System.Diagnostics;
12 using System.Windows.Automation;
13 using System.Windows.Automation.Peers;
14 using System.Windows.Automation.Provider;
15 using System.Windows.Controls;
16 using System.Windows.Media;
19 using System.Activities.Presentation.PropertyEditing;
20 using System.Activities.Presentation.Internal.PropertyEditing.FromExpression.Framework.PropertyInspector;
22 using System.Activities.Presentation.Internal.Properties;
23 using System.Activities.Presentation.Internal.PropertyEditing.Model;
24 using System.Activities.Presentation.Internal.PropertyEditing.Selection;
27 // This class contains the core logic for CategoryContainerAutomationPeer. It's constructor is
28 // private because it can be instantiated in two different ways, which are exposed through
29 // static methods: CreateStandAloneAutomationPeer() and CreateItemAutomationPeer().
31 // CreateStandAloneAutomationPeer() returns an AutomationPeer that is agnostic of its parent
32 // CreateItemAutomationPeer() returns an AutomationPeer where the parent is assumed to be an ItemsControl.
34 // Because in the CategoryList : ItemsControl class we manually override GetContainerForItemOverride()
35 // as a way of reducing control count in PI, we need to provide both versions of the
36 // CategoryContainerAutomationPeer.
38 internal class CategoryContainerAutomationPeer : IExpandCollapseProvider, IScrollItemProvider
41 private CiderCategoryContainer _container;
42 private AutomationPeer _itemAutomationPeer;
44 // Private ctor called from two public, static accessors
45 private CategoryContainerAutomationPeer(CiderCategoryContainer container, AutomationPeer itemPeer)
47 Fx.Assert(container != null, "CategoryContainer not specified.");
48 Fx.Assert(itemPeer != null, "CategoryContainerItemAutomationPeer not specified.");
49 _container = container;
50 _itemAutomationPeer = itemPeer;
55 // Gets the expand / collapse state of the contained CategoryContainer
57 public ExpandCollapseState ExpandCollapseState
60 if (_container == null)
62 return ExpandCollapseState.Collapsed;
65 return _container.Expanded ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed;
69 // IScrollItemProvider Members
72 // Creates a CategoryContainerAutomationPeer that can be returned directly from
73 // the CiderCategoryContainer control itself.
75 // <param name="container">Container to automate</param>
76 // <returns>Instance of CategoryContainerAutomationPeer that can be returned directly from
77 // the CiderCategoryContainer control itself</returns>
78 public static UIElementAutomationPeer CreateStandAloneAutomationPeer(CiderCategoryContainer container)
80 return new CategoryContainerStandAloneAutomationPeer(container);
84 // Creates a CategoryContainerAutomationPeer that can be returned for CategoryContainers
85 // belonging to a CategoryList control
87 // <param name="category">CategoryEntry instance used by ItemsControl</param>
88 // <param name="container">Container to automate</param>
89 // <param name="parentAutomationPeer">Parent AutomationPeer</param>
90 // <returns>Instance of CategoryContainerAutomationPeer that can be returned for CategoryContainers
91 // belonging to a CategoryList control</returns>
92 public static ItemAutomationPeer CreateItemAutomationPeer(ModelCategoryEntry category, CiderCategoryContainer container, CategoryListAutomationPeer parentAutomationPeer)
94 return new CategoryContainerItemAutomationPeer(category, container, parentAutomationPeer);
97 // Gets the children of the contained CategoryContainer, returning AutomationPeers
98 // for CategoryEditors followed by AutomationPeers for any left-over properties
99 private List<AutomationPeer> GetChildrenCore()
101 List<AutomationPeer> children = new List<AutomationPeer>();
103 if (_container != null)
105 AddCategoryEditors(children, VisualTreeUtils.GetNamedChild<ItemsControl>(_container, "PART_BasicCategoryEditors"), Resources.PropertyEditing_BasicCategoryEditors);
106 AddCategoryProperties(children, VisualTreeUtils.GetNamedChild<ItemsControl>(_container, "PART_BasicPropertyList"));
108 if (_container.Expanded)
111 Expander advancedExpander = VisualTreeUtils.GetNamedChild<Expander>(_container, "PART_AdvancedExpander");
113 if (advancedExpander != null &&
114 advancedExpander.Visibility == Visibility.Visible)
116 children.Add(new AdvancedCategoryContainerAutomationPeer(_container, advancedExpander));
124 // Adds AutomationPeers for all CategoryEditors displayed within the contained CategoryContainer
125 private static void AddCategoryEditors(List<AutomationPeer> peers, ItemsControl editors, string containerDisplayName)
127 if (editors == null || editors.Items.Count == 0)
132 peers.Add(new CategoryEditorListAutomationPeer(containerDisplayName, editors));
135 // Adds AutomationPeers for all PropertyEntries not consumed by the CategoryEditors within this
137 private static void AddCategoryProperties(List<AutomationPeer> peers, ItemsControl properties)
139 if (properties == null)
144 int childCount = properties.Items.Count;
146 for (int i = 0; i < childCount; i++)
148 PropertyContainer propertyContainer = properties.ItemContainerGenerator.ContainerFromIndex(i) as PropertyContainer;
150 if (propertyContainer != null)
152 peers.Add(new PropertyContainerAutomationPeer(propertyContainer));
157 // Gets the implementation of the specified pattern if one exists.
158 // Currently supported patterns: ExpandCollapse
159 private object GetPattern(PatternInterface patternInterface)
161 if (patternInterface == PatternInterface.ExpandCollapse)
165 else if (patternInterface == PatternInterface.ScrollItem)
173 // Returns the name of the represented category
174 private string GetNameCore()
176 if (_container != null)
178 return _container.Category.CategoryName;
184 // Return "CategoryContainer"
185 private static string GetClassNameCore()
187 return typeof(CategoryContainer).Name;
190 // Gets the help text to associated with the contained CategoryContainer
191 private static string GetHelpTextCore()
193 return Resources.PropertyEditing_CategoryContainerAutomationPeerHelp;
196 // IExpandCollapseProvider Members
199 // Collapses the contained CategoryContainer
201 public void Collapse()
203 if (_container != null)
205 _container.Expanded = false;
210 // Expands the contained CategoryContainer
214 if (_container != null)
216 _container.Expanded = true;
221 // Scrolls the contained CategoryContainer into view
223 public void ScrollIntoView()
225 if (_container != null)
227 _container.BringIntoView();
232 // RaiseSelectionEventsForScreenReader
235 // This public method is called when parent creates the CategoryContainerItemAutomationPeer
236 // in the GetChildrenCore, thus listening to the changes in the IsSelectedProperty.
238 public void AddFocusEvents()
240 if (_container != null)
242 HookUpFocusEvents(VisualTreeUtils.GetNamedChild<Expander>(_container, "PART_MainExpander"), OnIsSelectedValueChanged);
247 // Private helper function to listen to the changes in the IsSelectedProperty,
248 // which then fires the OnValueChanged event.
250 // <param name="expander">Expander control</param>
251 // <param name="valueChangedEvent">ValueChanged event</param>
252 private static void HookUpFocusEvents(Expander expander, EventHandler valueChangedEvent)
254 if (expander != null)
256 FrameworkElement expanderGrid = VisualTreeUtils.GetNamedChild<FrameworkElement>(expander, "PART_BasicSection");
257 if (expanderGrid != null)
259 DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(PropertySelection.IsSelectedProperty, typeof(Grid));
262 dpd.AddValueChanged(expanderGrid, valueChangedEvent);
269 // The actual event handler, that fires when the IsSelected DP changes.
270 // Here we raise the AutomationFocus event.
272 // <param name="sender">Expander</param>
273 // <param name="e">EventArgs</param>
274 private void OnIsSelectedValueChanged(object sender, EventArgs e)
276 // Add logic to respond to "Selection"
277 bool curVal = PropertySelection.GetIsSelected(sender as DependencyObject);
280 _itemAutomationPeer.RaiseAutomationEvent(AutomationEvents.AutomationFocusChanged);
287 // This public method is called when parent creates the CategoryContainerItemAutomationPeer
288 // in the GetChildrenCore, thus clearing off all the event listeners before we add new ones
290 public void RemoveFocusEvents()
292 if (_container != null)
294 UnHookFocusEvents(VisualTreeUtils.GetNamedChild<Expander>(_container, "PART_MainExpander"), OnIsSelectedValueChanged);
299 // Private method to unhook the ValueChanged event.
301 // <param name="expander">Expander</param>
302 // <param name="valueChangedEvent">ValueChanged event</param>
303 private static void UnHookFocusEvents(Expander expander, EventHandler valueChangedEvent)
305 if (expander != null)
307 FrameworkElement expanderGrid = VisualTreeUtils.GetNamedChild<FrameworkElement>(expander, "PART_BasicSection");
308 if (expanderGrid != null)
310 DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(PropertySelection.IsSelectedProperty, typeof(Grid));
313 dpd.RemoveValueChanged(expanderGrid, valueChangedEvent);
319 // end RaiseSelectionEventsForScreenReader
322 // Helper AutomationPeer that represents CategoryContainerAutomationPeer when the CategoryContainer
323 // is hosted in a CategoryList control. All of its methods delegate to CategoryContainerAutomationPeer
325 internal class CategoryContainerItemAutomationPeer : ItemAutomationPeer, IAutomationFocusChangedEventSource
328 private CategoryContainerAutomationPeer _coreLogic;
329 private List<AutomationPeer> _children;
331 public CategoryContainerItemAutomationPeer(
332 ModelCategoryEntry item,
333 CiderCategoryContainer container,
334 CategoryListAutomationPeer parentAutomationPeer)
335 : base(item, parentAutomationPeer)
337 _coreLogic = new CategoryContainerAutomationPeer(container, this);
338 _coreLogic.AddFocusEvents();
341 // Implementation of this method is specific to CategoryContainerItemAutomationPeer
342 protected override AutomationControlType GetAutomationControlTypeCore()
344 return AutomationControlType.List;
347 protected override List<AutomationPeer> GetChildrenCore()
349 _children = _coreLogic.GetChildrenCore();
353 protected override string GetNameCore()
355 return _coreLogic.GetNameCore();
358 protected override string GetClassNameCore()
360 return CategoryContainerAutomationPeer.GetClassNameCore();
363 protected override string GetHelpTextCore()
365 return CategoryContainerAutomationPeer.GetHelpTextCore();
368 public override object GetPattern(PatternInterface patternInterface)
370 return _coreLogic.GetPattern(patternInterface);
373 // IAutomationFocusChangedEventSource Members
374 public void UnloadEventHook()
376 _coreLogic.RemoveFocusEvents();
377 if (_children != null)
379 foreach (AutomationPeer peer in _children)
381 IAutomationFocusChangedEventSource unhookEventPeer = peer as IAutomationFocusChangedEventSource;
382 if (unhookEventPeer != null)
384 unhookEventPeer.UnloadEventHook();
392 // Automation peer we use to display the list of CategoryEditors
394 private class CategoryEditorListAutomationPeer : ItemsControlAutomationPeer
397 private string _displayName;
399 // Note: the display name we use here is for completeness only. This class is hidden
400 // from screen readers.
401 public CategoryEditorListAutomationPeer(string displayName, ItemsControl owner)
404 _displayName = displayName ?? string.Empty;
407 protected override ItemAutomationPeer CreateItemAutomationPeer(object item)
409 return new CategoryEditorAutomationPeer(item, this);
412 protected override string GetClassNameCore()
414 return typeof(ItemsControl).Name;
416 protected override string GetNameCore()
420 protected override bool IsControlElementCore()
424 protected override bool IsContentElementCore()
429 private class CategoryEditorAutomationPeer : ItemAutomationPeer
432 public CategoryEditorAutomationPeer(object item, CategoryEditorListAutomationPeer parent)
437 public override object GetPattern(PatternInterface patternInterface)
441 protected override AutomationControlType GetAutomationControlTypeCore()
443 return AutomationControlType.Custom;
445 protected override string GetClassNameCore()
447 return typeof(CategoryEditor).Name;
449 protected override string GetNameCore()
451 return Item == null ? string.Empty : Item.GetType().Name;
453 protected override bool IsContentElementCore()
457 protected override bool IsControlElementCore()
465 // Helper AutomationPeer for the advanced portion of the CategoryContainer
467 private class AdvancedCategoryContainerAutomationPeer : ExpanderAutomationPeer, IAutomationFocusChangedEventSource
470 private CiderCategoryContainer _container;
471 private Expander _expander;
472 private List<AutomationPeer> _children;
474 public AdvancedCategoryContainerAutomationPeer(CiderCategoryContainer container, Expander expander)
477 Fx.Assert(container != null, "CategoryContainer not specified.");
478 Fx.Assert(expander != null, "Expander not specified.");
479 _expander = expander;
480 _container = container;
485 // This public method is called when parent creates the CategoryContainerItemAutomationPeer
486 // in the GetChildrenCore, thus listening to the changes in the IsSelectedProperty.
488 private void AddFocusEvents()
490 if (_container != null)
492 HookUpFocusEvents(VisualTreeUtils.GetNamedChild<Expander>(_container, "PART_AdvancedExpander"), OnIsSelectedValueChanged);
496 protected override List<AutomationPeer> GetChildrenCore()
498 _children = new List<AutomationPeer>();
499 if (_container != null)
501 CategoryContainerAutomationPeer.AddCategoryEditors(_children, VisualTreeUtils.GetNamedChild<ItemsControl>(_container, "PART_AdvancedCategoryEditors"), Resources.PropertyEditing_AdvancedCategoryEditors);
502 CategoryContainerAutomationPeer.AddCategoryProperties(_children, VisualTreeUtils.GetNamedChild<ItemsControl>(_container, "PART_AdvancedPropertyList"));
504 //Add focus events for Subproperty editor
505 foreach (AutomationPeer peer in _children)
507 PropertyContainerAutomationPeer pcAutomationPeer = peer as PropertyContainerAutomationPeer;
508 if (pcAutomationPeer != null)
510 pcAutomationPeer.AddFocusEvents();
516 protected override string GetNameCore()
518 return _expander.Header.ToString();
522 // Private helper function to listen to the changes in the IsSelectedProperty,
523 // which then fires the OnValueChanged event.
525 // <param name="expander">Expander control</param>
526 // <param name="valueChangedEvent">ValueChanged event</param>
527 private static void HookUpFocusEvents(Expander expander, EventHandler valueChangedEvent)
529 if (expander != null)
531 FrameworkElement expanderGrid = VisualTreeUtils.GetNamedChild<FrameworkElement>(expander, "PART_AdvancedSection");
532 if (expanderGrid != null)
534 DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(PropertySelection.IsSelectedProperty, typeof(Grid));
537 dpd.AddValueChanged(expanderGrid, valueChangedEvent);
544 // The actual event handler, that fires when the IsSelected DP changes.
545 // Here we raise the AutomationFocus event for the
546 // Advanced (More Properties) properties expander.
548 // <param name="sender">Expander</param>
549 // <param name="e">EventArgs</param>
550 private void OnIsSelectedValueChanged(object sender, EventArgs e)
552 // Add logic to respond to "Selection"
553 bool curVal = PropertySelection.GetIsSelected(sender as DependencyObject);
556 this.RaiseAutomationEvent(AutomationEvents.AutomationFocusChanged);
561 // This public method is called when parent creates the CategoryContainerItemAutomationPeer
562 // in the GetChildrenCore, thus clearing off all the event listeners before we add new ones
564 public void RemoveFocusEvents()
566 if (_container != null)
568 UnHookFocusEvents(VisualTreeUtils.GetNamedChild<Expander>(_container, "PART_AdvancedExpander"), OnIsSelectedValueChanged);
573 // Private method to unhook the ValueChanged event.
575 // <param name="expander">Expander</param>
576 // <param name="valueChangedEvent">ValueChanged event</param>
577 private static void UnHookFocusEvents(Expander expander, EventHandler valueChangedEvent)
579 if (expander != null)
581 FrameworkElement expanderGrid = VisualTreeUtils.GetNamedChild<FrameworkElement>(expander, "PART_AdvancedSection");
582 if (expanderGrid != null)
584 DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(PropertySelection.IsSelectedProperty, typeof(Grid));
587 dpd.RemoveValueChanged(expanderGrid, valueChangedEvent);
593 // IAutomationFocusChangedEventSource Members
594 public void UnloadEventHook()
597 if (_children != null)
599 foreach (AutomationPeer peer in _children)
601 IAutomationFocusChangedEventSource unhookEventPeer = peer as IAutomationFocusChangedEventSource;
602 if (unhookEventPeer != null)
604 unhookEventPeer.UnloadEventHook();
612 // Helper AutomationPeer that represents CategoryContainerAutomationPeer when the CategoryContainer
613 // sits on its own. All of its methods delegate to CategoryContainerAutomationPeer
615 private class CategoryContainerStandAloneAutomationPeer : UIElementAutomationPeer, IAutomationFocusChangedEventSource
618 private CategoryContainerAutomationPeer _coreLogic;
620 public CategoryContainerStandAloneAutomationPeer(CiderCategoryContainer container)
623 _coreLogic = new CategoryContainerAutomationPeer(container, this);
624 _coreLogic.AddFocusEvents();
627 protected override List<AutomationPeer> GetChildrenCore()
629 return _coreLogic.GetChildrenCore();
632 protected override string GetNameCore()
634 return _coreLogic.GetNameCore();
637 protected override string GetClassNameCore()
639 return CategoryContainerAutomationPeer.GetClassNameCore();
642 protected override string GetHelpTextCore()
644 return CategoryContainerAutomationPeer.GetHelpTextCore();
647 public override object GetPattern(PatternInterface patternInterface)
649 return _coreLogic.GetPattern(patternInterface);
652 // IAutomationFocusChangedEventSource Members
654 public void UnloadEventHook()
656 _coreLogic.RemoveFocusEvents();