Files
DP44/enriched-qwen3-coder-next/Common/DTS.Common/Base/Classes.md
2026-04-17 14:55:32 -04:00

12 KiB
Raw Blame History

source_files, generated_at, model, schema_version, sha256
source_files generated_at model schema_version sha256
Common/DTS.Common/Base/Classes/BaseUserControl.cs
Common/DTS.Common/Base/Classes/BasePropertyChanged.cs
Common/DTS.Common/Base/Classes/DisplayResourceAttribute.cs
Common/DTS.Common/Base/Classes/DescriptionResourceAttribute.cs
Common/DTS.Common/Base/Classes/DynamicTypeDescriptor.cs
2026-04-16T03:27:54.091271+00:00 Qwen/Qwen3-Coder-Next-FP8 1 7d5e0417dd645280

Classes

Purpose

This module provides foundational infrastructure for WPF-based UI components and data binding within the DTS system, specifically enabling property change notification, dynamic property metadata customization, and localized display names/descriptions for properties and enums. It supports two primary patterns: BaseUserControl for WPF user controls that require INotifyPropertyChanged integration, and BasePropertyChanged for non-UI classes needing the same notification capability. Additionally, it offers attribute-based localization via DisplayResourceAttribute and DescriptionResourceAttribute, which resolve display strings from a centralized resource file (Strings.Strings). Finally, DynamicTypeDescriptor enables runtime modification of property metadata (e.g., DisplayName, Category, ReadOnly, Browsable) for use with PropertyGrid controls, particularly to override or extend metadata from sources like Entity Framework where attributes cannot be applied directly.


Public Interface

BaseUserControl

  • Namespace: DTS.Common.Base.Classes
  • Inherits: UserControl
  • Abstract: Yes
  • Constructors: None (abstract base class)
  • Events:
    • event PropertyChangedEventHandler PropertyChanged
  • Methods:
    • protected bool SetProperty<T>(ref T storage, T value, string propertyName = null)
      • Sets storage to value if not equal, raises PropertyChanged, and returns true; otherwise returns false. Used to implement property setters with change notification.
    • protected void OnPropertyChanged(string propertyName = null)
      • Raises the PropertyChanged event with the given property name (or null for all properties).

BasePropertyChanged

  • Namespace: DTS.Common.Base
  • Implements: IBasePropertyChanged (not shown in source, but referenced)
  • Abstract: Yes
  • Constructors: None (abstract base class)
  • Events:
    • public virtual event PropertyChangedEventHandler PropertyChanged
  • Methods:
    • public bool SetProperty<T>(ref T storage, T value, string propertyName = null)
      • Same semantics as BaseUserControl.SetProperty.
    • public virtual void OnPropertyChanged(string propertyName = null)
      • Same semantics as BaseUserControl.OnPropertyChanged.

DisplayResourceAttribute

  • Namespace: DTS.Common.Base.Classes
  • Inherits: DisplayNameAttribute
  • Constructors:
    • public DisplayResourceAttribute(string resourceId)
      • Stores resourceId for later lookup.
  • Properties:
    • public override string DisplayName
      • Returns the localized string from Strings.Strings.ResourceManager.GetString(resourceId); if not found, returns "##ResourceNotFound##" + resourceId.

DescriptionResourceAttribute

  • Namespace: DTS.Common.Base.Classes
  • Inherits: DescriptionAttribute
  • Constructors:
    • public DescriptionResourceAttribute(string resourceId)
      • Stores resourceId for later lookup.
  • Properties:
    • public override string Description
      • Returns the localized string from Strings.Strings.ResourceManager.GetString(resourceId); if not found, returns "##DescriptionNotFound##" + resourceId.

DynamicTypeDescriptor

  • Namespace: DTS.Common.Base.Classes
  • Implements: ICustomTypeDescriptor, INotifyPropertyChanged
  • Sealed: Yes
  • Constructors:
    • public DynamicTypeDescriptor(Type type)
      • Initializes from a base type, preserving original properties, attributes, converters, and editors. Only includes browsable properties.
  • Events:
    • public event PropertyChangedEventHandler PropertyChanged
  • Properties:
    • public PropertyDescriptorCollection OriginalProperties { get; }
    • public PropertyDescriptorCollection Properties { get; }
    • public object Component { get; private set; }
    • public string ClassName { get; set; }
    • public string ComponentName { get; set; }
  • Methods:
    • public T GetPropertyValue<T>(string name, T defaultValue)
      • Gets the value of property name from the Component, returning defaultValue on failure.
    • public void SetPropertyValue(string name, object value)
      • Sets the value of property name on the Component.
    • public PropertyDescriptor AddProperty(...)
      • Overloads to add a new dynamic property with specified metadata (displayName, description, category, readOnly, hasDefaultValue, defaultValue, uiTypeEditor).
    • public void RemoveProperty(string name)
      • Removes a property by name from Properties.
    • public void AddProperty(PropertyDescriptor property)
      • Adds a pre-constructed PropertyDescriptor to Properties.
    • public DynamicTypeDescriptor FromComponent(object component)
      • Creates a new DynamicTypeDescriptor instance bound to a specific component, copying original metadata and creating DynamicProperty wrappers for each property.
    • internal void OnValueChanged(PropertyDescriptor prop)
      • Raises PropertyChanged for the given property.
    • internal static T GetAttribute<T>(AttributeCollection attributes)
      • Helper to extract an attribute of type T from a collection.

DynamicTypeDescriptor.DynamicProperty (nested class)

  • Namespace: DTS.Common.Base.Classes.DynamicTypeDescriptor
  • Inherits: PropertyDescriptor, INotifyPropertyChanged
  • Internal: Yes
  • Constructors:
    • internal DynamicProperty(DynamicTypeDescriptor descriptor, Type type, object value, string name, Attribute[] attrs)
    • internal DynamicProperty(DynamicTypeDescriptor descriptor, PropertyDescriptor existing, object component)
  • Events:
    • public event PropertyChangedEventHandler PropertyChanged
  • Properties:
    • public object Value { get; set; }
    • public override AttributeCollection Attributes { get; }
    • public override bool CanResetValue(object component)
    • public override Type ComponentType { get; }
    • public override object GetValue(object component)
    • public override string Category { get; }
    • public override string Description { get; }
    • public override string DisplayName { get; }
    • public override bool IsBrowsable { get; }
    • public override bool IsReadOnly { get; }
    • public override Type PropertyType { get; }
  • Methods:
    • public void RemoveAttributesOfType<T>()
      • Removes all attributes of type T from the internal _attributes list.
    • public void SetCategory(string category)
    • public void SetDescription(string description)
    • public void SetDisplayName(string displayName)
    • public void SetBrowsable(bool browsable)
    • public void SetIsReadOnly(bool readOnly)
    • public override void ResetValue(object component)
    • public override void SetValue(object component, object value)
    • public override bool ShouldSerializeValue(object component)
    • public override object GetEditor(Type editorBaseType)
    • public void SetEditor(Type editorBaseType, object obj)
    • public IList<Attribute> AttributesList { get; }

Invariants

  • SetProperty<T> must compare storage and value using Equals, and only update storage and raise PropertyChanged if they differ.
  • OnPropertyChanged must be thread-safe in the sense that it safely invokes PropertyChanged?.Invoke(...) (null-conditional operator used).
  • DisplayResourceAttribute.DisplayName and DescriptionResourceAttribute.Description must fall back to "##ResourceNotFound##" + resourceId or "##DescriptionNotFound##" + resourceId respectively if the resource string is missing.
  • DynamicTypeDescriptor must only include browsable properties in its Properties collection during construction.
  • DynamicTypeDescriptor.FromComponent must create a shallow copy of internal fields (_typeConverter, _editors, _attributes, _events, OriginalProperties) and wrap each property in a new DynamicProperty instance bound to the new component.
  • DynamicProperty overrides of DisplayName, Description, Category, IsBrowsable, and IsReadOnly must prioritize instance-set values (e.g., _displayName, _browsable) over base/attribute values.
  • DynamicProperty.SetValue and ResetValue must call _descriptor.OnValueChanged(this) after modifying the value.

Dependencies

  • Depends on:
    • System.ComponentModel (INotifyPropertyChanged, PropertyChangedEventArgs, PropertyDescriptor, AttributeCollection, etc.)
    • System.Windows.Controls (UserControl)
    • System.Drawing.Design (UITypeEditor, EditorAttribute)
    • Strings.Strings (a generated resource class, likely from Strings.resx, accessed via Strings.Strings.ResourceManager)
  • Used by:
    • UI components inheriting from BaseUserControl (e.g., custom WPF user controls).
    • View models or domain classes inheriting from BasePropertyChanged.
    • Code that dynamically configures PropertyGrid metadata (e.g., for configuration editors), using DynamicTypeDescriptor.
    • Code that requires localized property names/descriptions via DisplayResourceAttribute/DescriptionResourceAttribute (e.g., on enum fields or properties).

Gotchas

  • DynamicTypeDescriptor does not support adding properties after FromComponent is called: Properties added via AddProperty to the original DynamicTypeDescriptor are not reflected in instances created via FromComponent, as it performs a shallow copy of Properties at construction time.
  • DynamicPropertys IsReadOnly logic is inconsistent: It checks _readOnly.HasValue, but if null, falls back to _existing.IsReadOnly or inspects ReadOnlyAttribute on the current Attributes—not necessarily the original propertys metadata.
  • DisplayResourceAttribute and DescriptionResourceAttribute assume resource IDs follow a strict naming convention (e.g., PropertyName_Description), but the attributes themselves accept arbitrary resourceId strings. Mismatched IDs cause silent fallback to "##...NotFound##".
  • DynamicTypeDescriptor.FromComponent performs a shallow copy of _editors and _attributes: Modifications to these collections in one instance may affect others.
  • BaseUserControl and BasePropertyChanged duplicate identical functionality (SetProperty, OnPropertyChanged). This duplication suggests a possible refactor opportunity (e.g., shared base or interface), but is preserved for architectural reasons (e.g., avoiding inheritance conflicts with UserControl).
  • No validation or exception handling in GetPropertyValue/SetPropertyValue beyond ArgumentNullException: Casting failures in GetPropertyValue return defaultValue silently, which may mask data type mismatches.
  • DynamicTypeDescriptor does not implement ICustomTypeDescriptor methods fully for all overloads (e.g., GetEvents(Attribute[]) ignores the filter).
  • DynamicPropertys ShouldSerializeValue always returns false unless backed by _existing, which may interfere with serialization logic expecting persistence of dynamic properties.
  • None of the Set* methods on DynamicProperty (e.g., SetDisplayName) raise PropertyChanged, so UI bound to these metadata properties will not update automatically.