| File: System\Windows\Automation\Peers\TreeViewItemAutomationPeer.cs | Web Access |
| Project: src\wpf\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Windows.Automation.Provider; using System.Windows.Controls; using System.Windows.Media; namespace System.Windows.Automation.Peers { /// public class TreeViewItemAutomationPeer : ItemsControlAutomationPeer, IExpandCollapseProvider, ISelectionItemProvider, IScrollItemProvider { /// public TreeViewItemAutomationPeer(TreeViewItem owner): base(owner) { } /// protected override string GetClassNameCore() { return "TreeViewItem"; } /// protected override AutomationControlType GetAutomationControlTypeCore() { return AutomationControlType.TreeItem; } /// public override object GetPattern(PatternInterface patternInterface) { if (patternInterface == PatternInterface.ExpandCollapse) { return this; } else if (patternInterface == PatternInterface.SelectionItem) { return this; } else if (patternInterface == PatternInterface.ScrollItem) { return this; } return base.GetPattern(patternInterface); } /// protected override List<AutomationPeer> GetChildrenCore() { List<AutomationPeer> children = null; ItemPeersStorage<ItemAutomationPeer> oldChildren = ItemPeers; //cache the old ones for possible reuse ItemPeers = new ItemPeersStorage<ItemAutomationPeer>(); TreeViewItem owner = Owner as TreeViewItem; if (owner != null) { iterate(this, owner, (IteratorCallback)delegate(AutomationPeer peer) { if (children == null) children = new List<AutomationPeer>(); children.Add(peer); return (false); }, ItemPeers, oldChildren); } return children; } private delegate bool IteratorCallback(AutomationPeer peer); // private static bool iterate(TreeViewItemAutomationPeer logicalParentAp, DependencyObject parent, IteratorCallback callback, ItemPeersStorage<ItemAutomationPeer> dataChildren, ItemPeersStorage<ItemAutomationPeer> oldChildren) { bool done = false; if (parent != null) { AutomationPeer peer = null; int count = VisualTreeHelper.GetChildrenCount(parent); for (int i = 0; i < count && !done; i++) { DependencyObject child = VisualTreeHelper.GetChild(parent, i); if (child != null && child is UIElement) { if (child is TreeViewItem) { object dataItem = (child as UIElement) != null ? (logicalParentAp.Owner as ItemsControl).GetItemOrContainerFromContainer(child as UIElement) : child; peer = oldChildren[dataItem]; if (peer == null) { peer = logicalParentAp.GetPeerFromWeakRefStorage(dataItem); if (peer != null) { // As cached peer is getting used it must be invalidated. peer.AncestorsInvalid = false; peer.ChildrenValid = false; } } if (peer == null) { peer = logicalParentAp.CreateItemAutomationPeer(dataItem); } //perform hookup so the events sourced from wrapper peer are fired as if from the data item if (peer != null) { AutomationPeer wrapperPeer = (peer as ItemAutomationPeer).GetWrapperPeer(); wrapperPeer?.EventsSource = peer; if (dataChildren[dataItem] == null && peer is ItemAutomationPeer) { callback(peer); dataChildren[dataItem] = peer as ItemAutomationPeer; } } } else { peer = CreatePeerForElement((UIElement)child); if (peer != null) done = callback(peer); } if(peer == null) done = iterate(logicalParentAp, child, callback, dataChildren, oldChildren); } else { done = iterate(logicalParentAp, child, callback, dataChildren, oldChildren); } } } return done; } /// <summary> /// It returns the ItemAutomationPeer if it exist corresponding to the item otherwise it creates /// one and does add the Handle and parent info by calling TrySetParentInfo. /// </summary> /// <param name="item"></param> /// <returns></returns> protected internal override ItemAutomationPeer FindOrCreateItemAutomationPeer(object item) { ItemAutomationPeer peer = ItemPeers[item]; AutomationPeer parentPeer = this; if (EventsSource as TreeViewDataItemAutomationPeer != null) { parentPeer = EventsSource as TreeViewDataItemAutomationPeer; } if (peer == null) peer = GetPeerFromWeakRefStorage(item); if (peer == null) { peer = CreateItemAutomationPeer(item); peer?.TrySetParentInfo(parentPeer); } if(peer != null) { AutomationPeer wrapperPeer = (peer as ItemAutomationPeer).GetWrapperPeer(); wrapperPeer?.EventsSource = peer; } return peer; } /// internal override bool IsPropertySupportedByControlForFindItem(int id) { if (base.IsPropertySupportedByControlForFindItem(id)) return true; else { if (SelectionItemPatternIdentifiers.IsSelectedProperty.Id == id) return true; else return false; } } /// <summary> /// Support for IsSelectedProperty should come from SelectorAutomationPeer only, /// </summary> internal override object GetSupportedPropertyValue(ItemAutomationPeer itemPeer, int propertyId) { if (SelectionItemPatternIdentifiers.IsSelectedProperty.Id == propertyId) { ISelectionItemProvider selectionItem = itemPeer.GetPattern(PatternInterface.SelectionItem) as ISelectionItemProvider; if (selectionItem != null) return selectionItem.IsSelected; else return null; } return base.GetSupportedPropertyValue(itemPeer, propertyId); } /// protected override ItemAutomationPeer CreateItemAutomationPeer(object item) { return new TreeViewDataItemAutomationPeer(item, this, EventsSource as TreeViewDataItemAutomationPeer); } // internal override IDisposable UpdateChildren() { // To ensure that the Updation of children should be initiated from DataPeer so as to have the right parent value stored for children TreeViewDataItemAutomationPeer dataPeer = EventsSource as TreeViewDataItemAutomationPeer; if(dataPeer != null) dataPeer.UpdateChildrenInternal(AutomationInteropProvider.ItemsInvalidateLimit); else UpdateChildrenInternal(AutomationInteropProvider.ItemsInvalidateLimit); WeakRefElementProxyStorage.PurgeWeakRefCollection(); return null; } /// <summary> internal void AddDataPeerInfo(TreeViewDataItemAutomationPeer dataPeer) { EventsSource = dataPeer; UpdateWeakRefStorageFromDataPeer(); } /// internal void UpdateWeakRefStorageFromDataPeer() { // To use the already stored WeakRef collection of it's children Items which might be created when last time this item was realized. if(EventsSource as TreeViewDataItemAutomationPeer != null) { if((EventsSource as TreeViewDataItemAutomationPeer).WeakRefElementProxyStorageCache == null) (EventsSource as TreeViewDataItemAutomationPeer).WeakRefElementProxyStorageCache = WeakRefElementProxyStorage; else if(WeakRefElementProxyStorage.Count == 0) { WeakRefElementProxyStorage = (EventsSource as TreeViewDataItemAutomationPeer).WeakRefElementProxyStorageCache; } } } /// void IExpandCollapseProvider.Expand() { if(!IsEnabled()) throw new ElementNotEnabledException(); TreeViewItem treeViewItem = (TreeViewItem)Owner; if (!treeViewItem.HasItems) { throw new InvalidOperationException(SR.UIA_OperationCannotBePerformed); } treeViewItem.IsExpanded = true; } /// void IExpandCollapseProvider.Collapse() { if(!IsEnabled()) throw new ElementNotEnabledException(); TreeViewItem treeViewItem = (TreeViewItem)Owner; if (!treeViewItem.HasItems) { throw new InvalidOperationException(SR.UIA_OperationCannotBePerformed); } treeViewItem.IsExpanded = false; } ExpandCollapseState IExpandCollapseProvider.ExpandCollapseState { get { TreeViewItem treeViewItem = (TreeViewItem)Owner; if (treeViewItem.HasItems) return treeViewItem.IsExpanded ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed; else return ExpandCollapseState.LeafNode; } } // BUG 1555137: Never inline, as we don't want to unnecessarily link the automation DLL [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] internal void RaiseExpandCollapseAutomationEvent(bool oldValue, bool newValue) { if (EventsSource as TreeViewDataItemAutomationPeer != null) { (EventsSource as TreeViewDataItemAutomationPeer).RaiseExpandCollapseAutomationEvent(oldValue, newValue); } else RaisePropertyChangedEvent( ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty, oldValue ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed, newValue ? ExpandCollapseState.Expanded : ExpandCollapseState.Collapsed); } #region ISelectionItemProvider /// <summary> /// Selects this element, removing any other element from the selection. /// </summary> void ISelectionItemProvider.Select() { ((TreeViewItem)Owner).IsSelected = true; } /// <summary> /// Selects this item. /// </summary> void ISelectionItemProvider.AddToSelection() { TreeView treeView = ((TreeViewItem)Owner).ParentTreeView; // If TreeView already has a selected item different from current - we cannot add to selection and throw if (treeView == null || (treeView.SelectedItem != null && treeView.SelectedContainer != Owner)) { throw new InvalidOperationException(SR.UIA_OperationCannotBePerformed); } ((TreeViewItem)Owner).IsSelected = true; } /// <summary> /// Unselects this item. /// </summary> void ISelectionItemProvider.RemoveFromSelection() { ((TreeViewItem)Owner).IsSelected = false; } /// <summary> /// Returns whether the item is selected. /// </summary> bool ISelectionItemProvider.IsSelected { get { return ((TreeViewItem)Owner).IsSelected; } } /// <summary> /// The logical element that supports the SelectionPattern for this item. /// </summary> IRawElementProviderSimple ISelectionItemProvider.SelectionContainer { get { ItemsControl parent = ((TreeViewItem)Owner).ParentItemsControl; if (parent != null) { AutomationPeer peer = UIElementAutomationPeer.FromElement(parent); if (peer != null) return ProviderFromPeer(peer); } return null; } } void IScrollItemProvider.ScrollIntoView() { ((TreeViewItem)Owner).BringIntoView(); } // BUG 1555137: Never inline, as we don't want to unnecessarily link the automation DLL [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] internal void RaiseAutomationIsSelectedChanged(bool isSelected) { if (EventsSource as TreeViewDataItemAutomationPeer != null) { (EventsSource as TreeViewDataItemAutomationPeer).RaiseAutomationIsSelectedChanged(isSelected); } else RaisePropertyChangedEvent( SelectionItemPatternIdentifiers.IsSelectedProperty, !isSelected, isSelected); } // Selection Events needs to be raised on DataItem Peers now when they exist. internal void RaiseAutomationSelectionEvent(AutomationEvents eventId) { if (EventsSource != null) { EventsSource.RaiseAutomationEvent(eventId); } else this.RaiseAutomationEvent(eventId); } #endregion } }