Files
wg_cpso/DynamicDescriptors/DynamicPropertyDescriptor.cs
2026-03-25 18:20:24 +08:00

336 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace DynamicDescriptors;
/// <summary>
/// A runtime-customizable implementation of <see cref="PropertyDescriptor"/>.
/// </summary>
public sealed class DynamicPropertyDescriptor : PropertyDescriptor
{
/// <summary>
/// The <see cref="PropertyDescriptor"/> on which this <see cref="DynamicPropertyDescriptor"/> is based.
/// </summary>
private readonly PropertyDescriptor _descriptor;
/// <summary>
/// A dictionary mapping editor base types to editor instances.
/// </summary>
private readonly IDictionary<Type, object> _editorDictionary;
/// <summary>
/// Gets or sets a value indicating whether the dynamic property descriptor is active.
/// If not, it won't be returned by the <see cref="DynamicTypeDescriptor.GetProperties()"/>
/// or <see cref="DynamicTypeDescriptor.GetProperties(Attribute[])"/> methods.
/// </summary>
private bool _active;
/// <summary>
/// If this value is not null, it will be returned by the Category property;
/// otherwise, the base descriptor's Category property will be returned.
/// </summary>
private string _categoryOverride;
/// <summary>
/// If this value is not null, it will be returned by the Converter property;
/// otherwise, the base descriptor's Converter property will be returned.
/// </summary>
private TypeConverter _converterOverride;
/// <summary>
/// If this value is not null, it will be returned by the Description property;
/// otherwise, the base descriptor's Description property will be returned.
/// </summary>
private string _descriptionOverride;
/// <summary>
/// If this value is not null, it will be returned by the DisplayName property;
/// otherwise, the base descriptor's DisplayName property will be returned.
/// </summary>
private string _displayNameOverride;
/// <summary>
/// If this value is not null, it will be returned by the IsReadOnly property;
/// otherwise, the base descriptor's IsReadOnly property will be returned.
/// </summary>
private bool? _isReadOnlyOverride;
/// <summary>
/// The order in which this property will be retrieved from its type descriptor.
/// </summary>
private int? _propertyOrder;
/// <summary>
/// Initializes a new instance of the <see cref="DynamicPropertyDescriptor"/> class.
/// </summary>
/// <param name="descriptor">
/// The <see cref="PropertyDescriptor"/> on which this <see cref="DynamicPropertyDescriptor"/> will be based.
/// </param>
public DynamicPropertyDescriptor(PropertyDescriptor descriptor)
: base(Preconditions.CheckNotNull(descriptor, nameof(descriptor)))
{
_descriptor = descriptor;
_editorDictionary = new Dictionary<Type, object>();
_active = true;
}
/// <summary>
/// Gets or sets a value indicating whether the dynamic property descriptor is active.
/// If not, it won't be returned by the <see cref="DynamicTypeDescriptor.GetProperties()"/>
/// or <see cref="DynamicTypeDescriptor.GetProperties(Attribute[])"/> methods.
/// </summary>
public bool Active
{
get => _active;
set => _active = value;
}
/// <summary>
/// Returns a value indicating whether resetting an object changes its value.
/// </summary>
/// <param name="component">The component to test for reset capability.</param>
/// <returns>true if resetting the component changes its value; otherwise, false.</returns>
public override bool CanResetValue(object component)
{
return _descriptor.CanResetValue(component);
}
/// <summary>
/// Gets the name of the category to which the member belongs,
/// as specified in the <see cref="CategoryAttribute"/>.
/// </summary>
public override string Category => _categoryOverride ?? _descriptor.Category;
/// <summary>
/// Gets the type of the component this property is bound to.
/// </summary>
public override Type ComponentType => _descriptor.ComponentType;
/// <summary>
/// Gets the type converter for this property.
/// </summary>
public override TypeConverter Converter => _converterOverride ?? _descriptor.Converter;
/// <summary>
/// Gets the description of the member,
/// as specified in the <see cref="DescriptionAttribute"/>.
/// </summary>
public override string Description => _descriptionOverride ?? _descriptor.Description;
/// <summary>
/// Gets the name that can be displayed in a window, such as a Properties window.
/// </summary>
public override string DisplayName => _displayNameOverride ?? _descriptor.DisplayName;
/// <summary>
/// Gets an editor of the specified type.
/// </summary>
/// <param name="editorBaseType">
/// The base type of editor, which is used to differentiate between multiple
/// editors that a property supports.
/// </param>
/// <returns>
/// An instance of the requested editor type, or null if an editor cannot be found.
/// </returns>
public override object GetEditor(Type editorBaseType)
{
if (editorBaseType != null)
{
if (_editorDictionary.TryGetValue(editorBaseType, out var editor))
{
return editor;
}
}
return _descriptor.GetEditor(editorBaseType);
}
/// <summary>
/// Returns the current value of the property on a component.
/// </summary>
/// <param name="component">
/// The component with the property for which to retrieve the value.
/// </param>
/// <returns>The value of a property for a given component.</returns>
public override object GetValue(object component)
{
return _descriptor.GetValue(component);
}
/// <summary>
/// Gets a value indicating whether this property is read-only.
/// </summary>
public override bool IsReadOnly => _isReadOnlyOverride ?? _descriptor.IsReadOnly;
/// <summary>
/// Gets the order in which this property will be retrieved from its type descriptor.
/// </summary>
public int? PropertyOrder => _propertyOrder;
/// <summary>
/// Gets the type of the property.
/// </summary>
public override Type PropertyType => _descriptor.PropertyType;
/// <summary>
/// Resets the value for this property of the component to the default value.
/// </summary>
/// <param name="component">
/// The component with the property value that is to be reset to the default value.
/// </param>
public override void ResetValue(object component)
{
_descriptor.ResetValue(component);
OnValueChanged(component, EventArgs.Empty);
}
/// <summary>
/// Sets the value of the component to a different value.
/// </summary>
/// <param name="component">
/// The component with the property value that is to be set.
/// </param>
/// <param name="value">
/// The new value.
/// </param>
public override void SetValue(object component, object value)
{
_descriptor.SetValue(component, value);
OnValueChanged(component, EventArgs.Empty);
}
/// <summary>
/// Determines a value indicating whether the value of this property needs to be persisted.
/// </summary>
/// <param name="component">
/// The component with the property to be examined for persistence.
/// </param>
/// <returns>true if the property should be persisted; otherwise, false.</returns>
public override bool ShouldSerializeValue(object component)
{
return _descriptor.ShouldSerializeValue(component);
}
#region Fluent interface
/// <summary>
/// Sets value that determines whether the dynamic property descriptor is active.
/// </summary>
/// <param name="active">
/// The value that determines whether the dynamic property descriptor is active.
/// </param>
/// <returns>This <see cref="DynamicPropertyDescriptor"/> instance.</returns>
public DynamicPropertyDescriptor SetActive(bool active)
{
_active = active;
return this;
}
/// <summary>
/// Sets the override for the <see cref="Category"/> property.
/// </summary>
/// <param name="category">The new value for the <see cref="Category"/> property.</param>
/// <returns>This <see cref="DynamicPropertyDescriptor"/> instance.</returns>
public DynamicPropertyDescriptor SetCategory(string category)
{
_categoryOverride = category;
return this;
}
/// <summary>
/// Sets the override for the <see cref="Converter"/> property.
/// </summary>
/// <param name="converter">The new value for the <see cref="Converter"/> property.</param>
/// <returns>This <see cref="DynamicPropertyDescriptor"/> instance.</returns>
public DynamicPropertyDescriptor SetConverter(TypeConverter converter)
{
_converterOverride = converter;
return this;
}
/// <summary>
/// Sets the override for the <see cref="Description"/> property.
/// </summary>
/// <param name="description">The new value for the <see cref="Description"/> property.</param>
/// <returns>This <see cref="DynamicPropertyDescriptor"/> instance.</returns>
public DynamicPropertyDescriptor SetDescription(string description)
{
_descriptionOverride = description;
return this;
}
/// <summary>
/// Sets the override for the <see cref="DisplayName"/> property.
/// </summary>
/// <param name="displayName">The new value for the <see cref="DisplayName"/> property.</param>
/// <returns>This <see cref="DynamicPropertyDescriptor"/> instance.</returns>
public DynamicPropertyDescriptor SetDisplayName(string displayName)
{
_displayNameOverride = displayName;
return this;
}
/// <summary>
/// Sets the editor for this type.
/// </summary>
/// <param name="editorBaseType">
/// The base type of editor, which is used to differentiate between multiple editors
/// that a property supports.
/// </param>
/// <param name="editor">
/// An instance of the requested editor type.
/// </param>
/// <returns>This <see cref="DynamicPropertyDescriptor"/> instance.</returns>
public DynamicPropertyDescriptor SetEditor(Type editorBaseType, object editor)
{
if (editorBaseType != null)
{
if (_editorDictionary.ContainsKey(editorBaseType))
{
if (editor == null)
{
_editorDictionary.Remove(editorBaseType);
}
else
{
_editorDictionary[editorBaseType] = editor;
}
}
else
{
if (editor != null)
{
_editorDictionary.Add(editorBaseType, editor);
}
}
}
return this;
}
/// <summary>
/// Sets the order in which this property will be retrieved from its type descriptor.
/// </summary>
/// <param name="propertyOrder">The order in which this property will be retrieved.</param>
/// <returns>This <see cref="DynamicPropertyDescriptor"/> instance.</returns>
public DynamicPropertyDescriptor SetPropertyOrder(int? propertyOrder)
{
_propertyOrder = propertyOrder;
return this;
}
/// <summary>
/// Sets the override for the <see cref="IsReadOnly"/> property.
/// </summary>
/// <param name="readOnly">The new value for the <see cref="IsReadOnly"/> property.</param>
/// <returns>This <see cref="DynamicPropertyDescriptor"/> instance.</returns>
public DynamicPropertyDescriptor SetReadOnly(bool? readOnly)
{
_isReadOnlyOverride = readOnly;
return this;
}
#endregion
}