init
This commit is contained in:
174
enriched-qwen3-coder-next/Common/DTS.Common/Base/Classes.md
Normal file
174
enriched-qwen3-coder-next/Common/DTS.Common/Base/Classes.md
Normal file
@@ -0,0 +1,174 @@
|
||||
---
|
||||
source_files:
|
||||
- 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
|
||||
generated_at: "2026-04-16T03:27:54.091271+00:00"
|
||||
model: "Qwen/Qwen3-Coder-Next-FP8"
|
||||
schema_version: 1
|
||||
sha256: "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.
|
||||
- **`DynamicProperty`’s `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 property’s 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).
|
||||
- **`DynamicProperty`’s `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.
|
||||
Reference in New Issue
Block a user