// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; using System.ComponentModel.Design; using System.Windows.Forms.Design; namespace System.Windows.Forms.PropertyGridInternal; /// <summary> /// Root <see cref="GridEntry"/> for the <see cref="PropertyGrid"/> when there is only one object /// in <see cref="PropertyGrid.SelectedObjects"/>. /// </summary> internal class SingleSelectRootGridEntry : GridEntry, IRootGridEntry { private string? _valueClassName; private GridEntry? _defaultEntry; private IDesignerHost? _host; private IServiceProvider _baseProvider; private PropertyTab _ownerTab; private PropertyGridView _ownerGridView; private AttributeCollection? _browsableAttributes; private IComponentChangeService? _changeService; protected bool _forceReadOnlyChecked; internal SingleSelectRootGridEntry( PropertyGridView ownerGridView, object target, IServiceProvider baseProvider, IDesignerHost? host, PropertyTab ownerTab, PropertySort sortType) : base(ownerGridView.OwnerGrid, parent: null) { Debug.Assert(target is not null, "Can't browse a null object!"); _host = host; _ownerGridView = ownerGridView; _baseProvider = baseProvider; _ownerTab = ownerTab; Target = target; _valueClassName = TypeDescriptor.GetClassName(Target); IsExpandable = true; // Default to categories. _propertySort = sortType; InternalExpanded = true; } /// <summary> /// The target object for this root entry. This is either a single object or an array of objects from /// <see cref="PropertyGrid.SelectedObjects" /> /// </summary> protected object Target { get; private set; } [AllowNull] public override AttributeCollection BrowsableAttributes { get => _browsableAttributes ??= new(BrowsableAttribute.Yes); set { if (value is null) { ResetBrowsableAttributes(); return; } bool same = true; if (_browsableAttributes is not null && value is not null && _browsableAttributes.Count == value.Count) { var currentAttributes = new Attribute[_browsableAttributes.Count]; var newAttributes = new Attribute[value.Count]; _browsableAttributes.CopyTo(currentAttributes, 0); value.CopyTo(newAttributes, 0); Array.Sort(currentAttributes, AttributeTypeSorter); Array.Sort(newAttributes, AttributeTypeSorter); for (int i = 0; i < currentAttributes.Length; i++) { if (!currentAttributes[i].Equals(newAttributes[i])) { same = false; break; } } } else { same = false; } _browsableAttributes = value; if (!same && ChildCount > 0) { DisposeChildren(); } } } protected override IComponentChangeService? ComponentChangeService => _changeService ?? this.GetService<IComponentChangeService>(); public override PropertyTab OwnerTab => _ownerTab; internal sealed override GridEntry? DefaultChild { get => _defaultEntry; set => _defaultEntry = value; } internal sealed override IDesignerHost? DesignerHost { get => _host; set => _host = value; } internal override bool ForceReadOnly { get { if (!_forceReadOnlyChecked) { if ((TypeDescriptorHelper.TryGetAttribute(Target, out ReadOnlyAttribute? readOnlyAttribute) && !readOnlyAttribute.IsDefaultAttribute()) || TypeDescriptor.GetAttributes(Target).Contains(InheritanceAttribute.InheritedReadOnly)) { SetForceReadOnlyFlag(); } _forceReadOnlyChecked = true; } return base.ForceReadOnly || (OwnerGridView is not null && !OwnerGridView.Enabled); } } internal override PropertyGridView OwnerGridView { get => _ownerGridView; set => _ownerGridView = value; } public override GridItemType GridItemType => GridItemType.Root; public override string? HelpKeyword { get { if (TypeDescriptorHelper.TryGetAttribute(Target, out HelpKeywordAttribute? helpAttribute) && !helpAttribute.IsDefaultAttribute()) { return helpAttribute.HelpKeyword; } return _valueClassName; } } public override string? PropertyLabel { get { if (Target is IComponent component) { return component.Site?.Name ?? Target.GetType().Name; } return Target?.ToString(); } } [AllowNull] public override object PropertyValue { get => Target; set { object old = Target; if (value is not null) { Target = value; _valueClassName = TypeDescriptor.GetClassName(Target); OwnerGrid.ReplaceSelectedObject(old, value); } } } protected override bool CreateChildren(bool diffOldChildren = false) { bool expandable = base.CreateChildren(diffOldChildren); CategorizePropertyEntries(); return expandable; } protected override void Dispose(bool disposing) { _host = null; _baseProvider = null!; _ownerTab = null!; _ownerGridView = null!; _changeService = null; Target = null!; _valueClassName = null; _defaultEntry = null; base.Dispose(disposing); } public override object? GetService(Type serviceType) => _host?.GetService(serviceType) ?? _baseProvider?.GetService(serviceType); public void ResetBrowsableAttributes() => _browsableAttributes = new(BrowsableAttribute.Yes); public virtual void ShowCategories(bool sortByCategories) { if (((_propertySort &= PropertySort.Categorized) != 0) != sortByCategories) { if (sortByCategories) { _propertySort |= PropertySort.Categorized; } else { _propertySort &= ~PropertySort.Categorized; } // Recreate the children. if (Expandable && ChildCollection is not null) { CreateChildren(); } } } /// <summary> /// Groups all children under category grid entries. /// </summary> protected void CategorizePropertyEntries() { if (Children.Count == 0 || (_propertySort & PropertySort.Categorized) == 0) { return; } // First, walk through all the entries and group them by their category. Dictionary<string, List<GridEntry>> categories = []; foreach (var child in Children) { string category = child.PropertyCategory; if (!categories.TryGetValue(category, out var gridEntries)) { gridEntries = []; categories[category] = gridEntries; } gridEntries.Add(child); } // Now walk through and create a CategoryGridEntry that holds all the properties for each category. List<GridEntry> categoryGridEntries = []; foreach (var entry in categories) { var gridEntries = entry.Value; if (gridEntries.Count > 0) { categoryGridEntries.Add(new CategoryGridEntry(OwnerGrid, this, entry.Key, entry.Value)); } } categoryGridEntries.Sort(GridEntryComparer.Default); ChildCollection.Clear(); ChildCollection.AddRange(categoryGridEntries); } } |