| File: System\Windows\Controls\GroupItem.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. // // Description: GroupItem object - root of the UI subtree generated for a CollectionViewGroup // // Specs: Data Styling.mht // using System.Windows.Media; using System.Windows.Controls.Primitives; using MS.Internal; using System.Windows.Automation; namespace System.Windows.Controls { /// <summary> /// A GroupItem appears as the root of the visual subtree generated for a CollectionViewGroup. /// </summary> public class GroupItem : ContentControl, IHierarchicalVirtualizationAndScrollInfo, IContainItemStorage { static GroupItem() { DefaultStyleKeyProperty.OverrideMetadata(typeof(GroupItem), new FrameworkPropertyMetadata(typeof(GroupItem))); _dType = DependencyObjectType.FromSystemTypeInternal(typeof(GroupItem)); // GroupItems should not be focusable by default FocusableProperty.OverrideMetadata(typeof(GroupItem), new FrameworkPropertyMetadata(false)); AutomationProperties.IsOffscreenBehaviorProperty.OverrideMetadata(typeof(GroupItem), new FrameworkPropertyMetadata(IsOffscreenBehavior.FromClip)); } /// <summary> /// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>) /// </summary> protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer() { return new System.Windows.Automation.Peers.GroupItemAutomationPeer(this); } public override void OnApplyTemplate() { base.OnApplyTemplate(); _header = this.GetTemplateChild("PART_Header") as FrameworkElement; // GroupItem is generally re-templated to have an Expander. // Look for an Expander and store its Header size.g _expander = Helper.FindTemplatedDescendant<Expander>(this, this); // // ItemValueStorage: restore saved values for this item onto the new container // if (_expander != null) { ItemsControl itemsControl = ParentItemsControl; if (itemsControl != null && VirtualizingPanel.GetIsVirtualizingWhenGrouping(itemsControl)) { Helper.SetItemValuesOnContainer(itemsControl, _expander, itemsControl.ItemContainerGenerator.ItemFromContainer(this)); } _expander.Expanded += new RoutedEventHandler(OnExpanded); } } private static void OnExpanded(object sender, RoutedEventArgs e) { GroupItem groupItem = sender as GroupItem; if (groupItem != null && groupItem._expander != null && groupItem._expander.IsExpanded) { ItemsControl itemsControl = groupItem.ParentItemsControl; if (itemsControl != null && VirtualizingPanel.GetIsVirtualizing(itemsControl) && VirtualizingPanel.GetVirtualizationMode(itemsControl) == VirtualizationMode.Recycling) { ItemsPresenter itemsHostPresenter = groupItem.ItemsHostPresenter; if (itemsHostPresenter != null) { // In case a GroupItem that wasn't previously expanded is now // recycled to represent an entity that is expanded, we face a situation // where the ItemsHost isn't connected yet but we do need to synchronously // remeasure the sub tree through the ItemsPresenter leading up to the // ItemsHost panel. If we didnt do this the offsets could get skewed. groupItem.InvalidateMeasure(); Helper.InvalidateMeasureOnPath(itemsHostPresenter, groupItem, false /*duringMeasure*/); } } } } internal override void OnTemplateChangedInternal(FrameworkTemplate oldTemplate,FrameworkTemplate newTemplate) { base.OnTemplateChangedInternal(oldTemplate, newTemplate); _expander?.Expanded -= new RoutedEventHandler(OnExpanded); _expander = null; _itemsHost = null; } protected override Size ArrangeOverride(Size arrangeSize) { arrangeSize = base.ArrangeOverride(arrangeSize); Helper.ComputeCorrectionFactor(ParentItemsControl, this, ItemsHost, HeaderElement); return arrangeSize; } /// <summary> /// Gives a string representation of this object. /// </summary> /// <returns></returns> internal override string GetPlainText() { System.Windows.Data.CollectionViewGroup cvg = Content as System.Windows.Data.CollectionViewGroup; if (cvg != null && cvg.Name != null) { return cvg.Name.ToString(); } return base.GetPlainText(); } //------------------------------------------------------ // // Internal Properties // //------------------------------------------------------ internal ItemContainerGenerator Generator { get { return _generator; } set { _generator = value; } } //------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ internal void PrepareItemContainer(object item, ItemsControl parentItemsControl) { if (Generator == null) return; // user-declared GroupItem - ignore (bug 108423) // If a GroupItem is being recycled set back IsItemsHost _itemsHost?.IsItemsHost = true; bool isVirtualizingWhenGrouping = (parentItemsControl != null && VirtualizingPanel.GetIsVirtualizingWhenGrouping(parentItemsControl)); // Release any previous containers. Also ensures Items and GroupStyle are hooked up correctly if (Generator != null) { if (!isVirtualizingWhenGrouping) { Generator.Release(); } else { Generator.RemoveAllInternal(true /*saveRecycleQueue*/); } } ItemContainerGenerator generator = Generator.Parent; GroupStyle groupStyle = generator.GroupStyle; // apply the container style Style style = groupStyle.ContainerStyle; // no ContainerStyle set, try ContainerStyleSelector if (style == null) { if (groupStyle.ContainerStyleSelector != null) { style = groupStyle.ContainerStyleSelector.SelectStyle(item, this); } } // apply the style, if found if (style != null) { // verify style is appropriate before applying it if (!style.TargetType.IsInstanceOfType(this)) throw new InvalidOperationException(SR.Format(SR.StyleForWrongType, style.TargetType.Name, this.GetType().Name)); this.Style = style; this.WriteInternalFlag2(InternalFlags2.IsStyleSetFromGenerator, true); } // forward the header template information if (ContentIsItem || !HasNonDefaultValue(ContentProperty)) { this.Content = item; ContentIsItem = true; } if (!HasNonDefaultValue(ContentTemplateProperty)) this.ContentTemplate = groupStyle.HeaderTemplate; if (!HasNonDefaultValue(ContentTemplateSelectorProperty)) this.ContentTemplateSelector = groupStyle.HeaderTemplateSelector; if (!HasNonDefaultValue(ContentStringFormatProperty)) this.ContentStringFormat = groupStyle.HeaderStringFormat; // // Clear previously cached items sizes // Helper.ClearVirtualizingElement(this); // // ItemValueStorage: restore saved values for this item onto the new container // if (isVirtualizingWhenGrouping) { Helper.SetItemValuesOnContainer(parentItemsControl, this, item); if (_expander != null) { Helper.SetItemValuesOnContainer(parentItemsControl, _expander, item); } } } internal void ClearItemContainer(object item, ItemsControl parentItemsControl) { if (Generator == null) return; // user-declared GroupItem - ignore (bug 108423) // // ItemValueStorage: save off values for this container if we're a virtualizing Group. // if (parentItemsControl != null && VirtualizingPanel.GetIsVirtualizingWhenGrouping(parentItemsControl)) { Helper.StoreItemValues((IContainItemStorage)parentItemsControl, this, item); if (_expander != null) { Helper.StoreItemValues((IContainItemStorage)parentItemsControl, _expander, item); } // Tell the panel to clear off all its containers. This will cause this method to be called // recursively down the tree, allowing all descendent data to be stored before we save off // the ItemValueStorage DP for this container. VirtualizingPanel vp = _itemsHost as VirtualizingPanel; vp?.OnClearChildrenInternal(); Generator.RemoveAllInternal(true /*saveRecycleQueue*/); } else { Generator.Release(); } ClearContentControl(item); } #region IHierarchicalVirtualizationAndScrollInfo HierarchicalVirtualizationConstraints IHierarchicalVirtualizationAndScrollInfo.Constraints { get { return HierarchicalVirtualizationConstraintsField.GetValue(this); } set { if (value.CacheLengthUnit == VirtualizationCacheLengthUnit.Page) { throw new InvalidOperationException(SR.PageCacheSizeNotAllowed); } HierarchicalVirtualizationConstraintsField.SetValue(this, value); } } HierarchicalVirtualizationHeaderDesiredSizes IHierarchicalVirtualizationAndScrollInfo.HeaderDesiredSizes { get { FrameworkElement headerElement = HeaderElement; Size pixelHeaderSize = new Size(); if (this.IsVisible && headerElement != null) { pixelHeaderSize = headerElement.DesiredSize; Helper.ApplyCorrectionFactorToPixelHeaderSize(ParentItemsControl, this, _itemsHost, ref pixelHeaderSize); } Size logicalHeaderSize = new Size(DoubleUtil.GreaterThanZero(pixelHeaderSize.Width) ? 1 : 0, DoubleUtil.GreaterThanZero(pixelHeaderSize.Height) ? 1 : 0); return new HierarchicalVirtualizationHeaderDesiredSizes(logicalHeaderSize, pixelHeaderSize); } } HierarchicalVirtualizationItemDesiredSizes IHierarchicalVirtualizationAndScrollInfo.ItemDesiredSizes { get { return Helper.ApplyCorrectionFactorToItemDesiredSizes(this, _itemsHost); } set { HierarchicalVirtualizationItemDesiredSizesField.SetValue(this, value); } } Panel IHierarchicalVirtualizationAndScrollInfo.ItemsHost { get { return _itemsHost; } } bool IHierarchicalVirtualizationAndScrollInfo.MustDisableVirtualization { get { return MustDisableVirtualizationField.GetValue(this); } set { MustDisableVirtualizationField.SetValue(this, value); } } bool IHierarchicalVirtualizationAndScrollInfo.InBackgroundLayout { get { return InBackgroundLayoutField.GetValue(this); } set { InBackgroundLayoutField.SetValue(this, value); } } #endregion #region ItemValueStorage object IContainItemStorage.ReadItemValue(object item, DependencyProperty dp) { return Helper.ReadItemValue(this, item, dp.GlobalIndex); } void IContainItemStorage.StoreItemValue(object item, DependencyProperty dp, object value) { Helper.StoreItemValue(this, item, dp.GlobalIndex, value); } void IContainItemStorage.ClearItemValue(object item, DependencyProperty dp) { Helper.ClearItemValue(this, item, dp.GlobalIndex); } void IContainItemStorage.ClearValue(DependencyProperty dp) { Helper.ClearItemValueStorage(this, new int[] {dp.GlobalIndex}); } void IContainItemStorage.Clear() { Helper.ClearItemValueStorage(this); } #endregion private ItemsControl ParentItemsControl { get { DependencyObject parent = this; do { parent = VisualTreeHelper.GetParent(parent); ItemsControl parentItemsControl = parent as ItemsControl; if (parentItemsControl != null) { return parentItemsControl; } } while (parent != null); return null; } } internal IContainItemStorage ParentItemStorageProvider { get { DependencyObject parentPanel = VisualTreeHelper.GetParent(this); if (parentPanel != null) { DependencyObject owner = ItemsControl.GetItemsOwnerInternal(parentPanel); return owner as IContainItemStorage; } return null; } } internal Panel ItemsHost { get { return _itemsHost; } set { _itemsHost = value; } } private ItemsPresenter ItemsHostPresenter { get { if (_expander != null) { return Helper.FindTemplatedDescendant<ItemsPresenter>(_expander, _expander); } else { return Helper.FindTemplatedDescendant<ItemsPresenter>(this, this); } } } internal Expander Expander { get { return _expander; } } private FrameworkElement ExpanderHeader { get { if (_expander != null) { return _expander.GetTemplateChild(ExpanderHeaderPartName) as FrameworkElement; } return null; } } private FrameworkElement HeaderElement { get { FrameworkElement headerElement = null; if (_header != null) { headerElement = _header; } else if (_expander != null) { // Look for Expander. We special case for Expander since its a very common usage of grouping. headerElement = ExpanderHeader; } return headerElement; } } //------------------------------------------------------ // // Private Fields // //------------------------------------------------------ private ItemContainerGenerator _generator; private Panel _itemsHost; private FrameworkElement _header; private Expander _expander; internal static readonly UncommonField<bool> MustDisableVirtualizationField = new UncommonField<bool>(); internal static readonly UncommonField<bool> InBackgroundLayoutField = new UncommonField<bool>(); internal static readonly UncommonField<Thickness> DesiredPixelItemsSizeCorrectionFactorField = new UncommonField<Thickness>(); internal static readonly UncommonField<HierarchicalVirtualizationConstraints> HierarchicalVirtualizationConstraintsField = new UncommonField<HierarchicalVirtualizationConstraints>(); internal static readonly UncommonField<HierarchicalVirtualizationHeaderDesiredSizes> HierarchicalVirtualizationHeaderDesiredSizesField = new UncommonField<HierarchicalVirtualizationHeaderDesiredSizes>(); internal static readonly UncommonField<HierarchicalVirtualizationItemDesiredSizes> HierarchicalVirtualizationItemDesiredSizesField = new UncommonField<HierarchicalVirtualizationItemDesiredSizes>(); #region DTypeThemeStyleKey // Returns the DependencyObjectType for the registered ThemeStyleKey's default // value. Controls will override this method to return approriate types. internal override DependencyObjectType DTypeThemeStyleKey { get { return _dType; } } private static DependencyObjectType _dType; private const string ExpanderHeaderPartName = "HeaderSite"; #endregion DTypeThemeStyleKey } }