This commit is contained in:
2026-04-17 14:55:32 -04:00
commit bc3ac1d4c9
18017 changed files with 4371742 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
---
source_files:
- 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
generated_at: "2026-04-16T02:51:28.846308+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "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`.
- **`DynamicProperty`s `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.*