Files
2026-04-17 14:55:32 -04:00

12 KiB
Raw Permalink Blame History

source_files, generated_at, model, schema_version, sha256
source_files generated_at model schema_version sha256
Common/DTS.CommonCore/Base/Classes/BasePropertyChanged.cs
Common/DTS.CommonCore/Base/Classes/DisplayResourceAttribute.cs
Common/DTS.CommonCore/Base/Classes/DescriptionResourceAttribute.cs
Common/DTS.CommonCore/Base/Classes/DynamicTypeDescriptor.cs
2026-04-16T02:51:28.846308+00:00 Qwen/Qwen3-Coder-Next-FP8 1 e84e534d3f8838df

Classes

Documentation: DTS.Common.Base Module Property Change Notification & Dynamic Type Descriptor Infrastructure


1. Purpose

This module provides foundational infrastructure for property change notification and dynamic runtime customization of object metadata—primarily to support UI frameworks like PropertyGrid where static attributes (e.g., DisplayName, Description) must be overridden or extended at runtime. It includes a base class (BasePropertyChanged) for implementing INotifyPropertyChanged, and two custom attribute classes (DisplayResourceAttribute, DescriptionResourceAttribute) that delegate display strings to localized resources via Strings.Strings.ResourceManager. Additionally, DynamicTypeDescriptor enables dynamic modification of property metadata (e.g., browsability, display name, category) for objects, especially useful for types like Entity Framework proxies where attributes cannot be applied directly.


2. Public Interface

BasePropertyChanged

  • event PropertyChangedEventHandler PropertyChanged
    Virtual event for property change notifications. Subclasses may override to customize notification behavior.

  • bool SetProperty<T>(ref T storage, T value, string propertyName = null)
    Compares storage and value for equality; if different, updates storage, invokes OnPropertyChanged, and returns true. Returns false if values are equal. Used to implement INotifyPropertyChanged concisely.

  • virtual void OnPropertyChanged(string propertyName = null)
    Raises the PropertyChanged event with the given property name. If propertyName is null, raises PropertyChangedEventArgs.Empty.

DisplayResourceAttribute

  • DisplayResourceAttribute(string resourceId)
    Constructor. Stores resourceId for later lookup in Strings.Strings.ResourceManager.

  • override string DisplayName
    Returns the localized string from Strings.Strings.ResourceManager.GetString(resourceId), or "##ResourceNotFound##" + resourceId if not found.

DescriptionResourceAttribute

  • DescriptionResourceAttribute(string resourceId)
    Constructor. Stores resourceId for later lookup in Strings.Strings.ResourceManager.

  • override string Description
    Returns the localized string from Strings.Strings.ResourceManager.GetString(resourceId), or "##DescriptionNotFound##" + resourceId if not found.

DynamicTypeDescriptor

  • DynamicTypeDescriptor(Type type)
    Constructor. Initializes internal state by querying TypeDescriptor for the given type. Filters out non-browsable properties. Stores original properties, type converter, default event/property, and editors.

  • T GetPropertyValue<T>(string name, T defaultValue)
    Retrieves the value of the property named name from the current Component, casting to T. Returns defaultValue on failure or if property not found.

  • void SetPropertyValue(string name, object value)
    Sets the value of the property named name on the current Component. No-op if property not found.

  • DynamicProperty AddProperty(Type type, string name, object value, string displayName, string description, string category, bool hasDefaultValue, object defaultValue, bool readOnly, Type uiTypeEditor)
    Creates and adds a new DynamicProperty with specified metadata. Overload omits uiTypeEditor (defaults to null).

  • void AddProperty(PropertyDescriptor property)
    Adds a pre-constructed PropertyDescriptor (e.g., DynamicProperty) to the Properties collection.

  • void RemoveProperty(string name)
    Removes the property named name from the Properties collection.

  • DynamicTypeDescriptor FromComponent(object component)
    Creates a new DynamicTypeDescriptor instance bound to component, copying original metadata and creating DynamicProperty wrappers for each property. Requires component to be assignable to the original _type.

  • event PropertyChangedEventHandler PropertyChanged
    Implements INotifyPropertyChanged. Raised internally via OnValueChanged(PropertyDescriptor) when a property value changes.

  • PropertyDescriptorCollection Properties { get; }
    Mutable collection of browsable properties (original + dynamically added/modified). Non-browsable properties are excluded during construction.

  • PropertyDescriptorCollection OriginalProperties { get; }
    Immutable snapshot of the original properties (including non-browsable ones) as returned by TypeDescriptor.GetProperties(type).

  • object Component { get; }
    The object instance whose properties are being described. Set only during FromComponent.

  • string ClassName { get; set; } / string ComponentName { get; set; }
    Overridable display names for the type and component, respectively, used in ICustomTypeDescriptor.GetClassName()/GetComponentName().

DynamicTypeDescriptor.DynamicProperty

  • DynamicProperty(DynamicTypeDescriptor descriptor, Type type, object value, string name, Attribute[] attrs)
    Internal constructor for a new dynamic property (not tied to an existing property descriptor).

  • DynamicProperty(DynamicTypeDescriptor descriptor, PropertyDescriptor existing, object component)
    Internal constructor wrapping an existing property descriptor, capturing its value and attributes.

  • void SetDisplayName(string displayName) / void SetDescription(string description) / void SetCategory(string category)
    Overrides the corresponding property metadata (e.g., DisplayName, Description, Category) for this dynamic property.

  • void SetBrowsable(bool browsable) / void SetIsReadOnly(bool readOnly)
    Overrides IsBrowsable and IsReadOnly behavior.

  • void RemoveAttributesOfType<T>()
    Removes all attributes of type T (or derived) from the internal _attributes list.

  • IList<Attribute> AttributesList { get; }
    Exposes the internal list of attributes for inspection/modification.

  • override object GetValue(object component) / override void SetValue(object component, object value)
    Gets/sets the property value. If wrapping an _existing property, delegates to it; otherwise uses the internal Value field.

  • override void ResetValue(object component)
    Resets value to default (if _existing exists, delegates; otherwise uses _defaultValue or Value).

  • override bool IsBrowsable / override bool IsReadOnly / override string DisplayName / override string Description / override string Category
    Returns overridden values if set; otherwise falls back to base implementation or wrapped _existing property.


3. Invariants

  • BasePropertyChanged.SetProperty

    • Only raises PropertyChanged if storage and value are not equal (via Equals).
    • storage is updated before OnPropertyChanged is called.
  • DynamicTypeDescriptor

    • Properties collection excludes non-browsable properties at construction time (via if (!property.IsBrowsable) continue;).
    • FromComponent performs a shallow copy of internal state (e.g., _editors, _typeConverter), but creates new DynamicProperty instances per property.
    • DynamicProperty overrides metadata only if explicitly set via Set* methods; otherwise falls back to original or base behavior.
    • DynamicTypeDescriptor implements ICustomTypeDescriptor and INotifyPropertyChanged—consumers may rely on both interfaces.
  • DisplayResourceAttribute / DescriptionResourceAttribute

    • DisplayName/Description always returns a non-null string: either the localized value or a fallback marker (##ResourceNotFound##/##DescriptionNotFound##) concatenated with the resourceId.
    • Resource lookup uses Strings.Strings.ResourceManager.GetString(resourceId)no automatic suffixing (e.g., "PropertyName_Description" must be the exact resourceId passed to the constructor).

4. Dependencies

  • Internal Dependencies

    • System.ComponentModel (INotifyPropertyChanged, PropertyChangedEventHandler, Attribute, PropertyDescriptor, etc.).
    • Strings.Strings.ResourceManager (from DTS.Common resources) for DisplayResourceAttribute and DescriptionResourceAttribute.
    • System.Drawing.Design (UITypeEditor, EditorAttribute) for editor support in DynamicTypeDescriptor.
  • External Dependencies

    • DynamicTypeDescriptor is used by UI components (e.g., PropertyGrid) that rely on ICustomTypeDescriptor.
    • BasePropertyChanged is intended for use by view models or entities requiring INotifyPropertyChanged.
    • DisplayResourceAttribute/DescriptionResourceAttribute are used as attributes on properties to enable localization.
  • Inferred Usage

    • Likely consumed by UI layers (e.g., WinForms PropertyGrid, WPF data binding) where dynamic metadata or localization is needed.

5. Gotchas

  • DynamicTypeDescriptor does not automatically propagate changes to the underlying Component when using DynamicProperty with no _existing property. SetValue/ResetValue only update the internal Value field of the DynamicProperty. To bind to real properties, use FromComponent to wrap an existing property descriptor.

  • DynamicProperty.Attributes is a snapshot of _attributes at construction time unless modified via RemoveAttributesOfType<T> or Set* methods. Direct mutation of _attributes is not exposed.

  • DisplayResourceAttribute and DescriptionResourceAttribute do not support fallback to property name—if the resource ID is missing, the marker string (##ResourceNotFound##/##DescriptionNotFound##) is returned as-is, including the ID. This may cause UI clutter if not handled.

  • BasePropertyChanged.SetProperty uses Equals for comparison, which may be unsafe for mutable reference types (e.g., collections). Consider using EqualityComparer<T>.Default or custom comparison if needed.

  • DynamicTypeDescriptor filters properties by IsBrowsable at construction time, so non-browsable properties (e.g., DesignerSerializationVisibility.Hidden) are permanently excluded from Properties. Use OriginalProperties to inspect all properties.

  • DynamicTypeDescriptor.FromComponent requires component.GetType() to be assignable to _type. Passing a derived type may throw ArgumentException.

  • DynamicPropertys ShouldSerializeValue always returns false unless wrapping an _existing property—this may interfere with serialization logic expecting ShouldSerialize* semantics.

  • No thread-safety guarantees are documented or implied for DynamicTypeDescriptor or BasePropertyChanged. Concurrent access to Properties or PropertyChanged events may require external synchronization.

  • Strings.Strings.ResourceManager must be initialized before DisplayResourceAttribute/DescriptionResourceAttribute are instantiated, or GetString will return null. This is a runtime dependency on the applications localization setup.


Documentation generated from source files only. No external behavior or assumptions beyond the provided code.