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.
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.
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.
OnPropertyChangedmust be thread-safe in the sense that it safely invokes PropertyChanged?.Invoke(...) (null-conditional operator used).
DisplayResourceAttribute.DisplayName and DescriptionResourceAttribute.Descriptionmust fall back to "##ResourceNotFound##" + resourceId or "##DescriptionNotFound##" + resourceId respectively if the resource string is missing.
DynamicTypeDescriptormust only include browsable properties in its Properties collection during construction.
DynamicTypeDescriptor.FromComponentmust 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 IsReadOnlymust prioritize instance-set values (e.g., _displayName, _browsable) over base/attribute values.
DynamicProperty.SetValue and ResetValuemust call _descriptor.OnValueChanged(this) after modifying the value.
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 afterFromComponent is called: Properties added via AddProperty to the originalDynamicTypeDescriptor 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.IsReadOnlyor inspects ReadOnlyAttribute on the currentAttributes—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.