8.8 KiB
8.8 KiB
PropertyGrid Advanced Example - Technical Documentation
Architecture Overview
The Advanced Example demonstrates a production-ready PropertyGrid implementation with enterprise-level features.
Class Hierarchy
AdvancedConfiguration (INotifyPropertyChanged)
├── Server Configuration
│ ├── ServerName (string, validated)
│ ├── MaxConnections (int, range: 1-1000)
│ ├── ConnectionType (enum)
│ └── RequiresCertificate (computed, read-only)
├── Logging
│ ├── EnableLogging (bool)
│ ├── LogLevel (enum)
│ └── LogFilePath (string, advanced)
├── Network
│ ├── Timeout (TimeSpan)
│ └── NetworkSettings (nested object)
│ ├── Hostname (string, required)
│ ├── Port (int, range: 1-65535)
│ ├── UseProxy (bool)
│ └── ProxyAddress (string)
├── Database
│ └── DatabaseSettings (nested object)
│ ├── ConnectionString (string, required)
│ ├── Provider (enum)
│ ├── CommandTimeout (int)
│ ├── EnableRetry (bool)
│ └── MaxRetryCount (int, range: 1-10)
├── Security
│ ├── ApiKey (string, password)
│ └── AllowedIPs (List<string>)
├── Performance
│ ├── CompressionLevel (int, range: 0-9)
│ └── CacheSizeMB (int, range: 10-1024)
└── Metadata (all read-only)
├── CreatedDate (DateTime)
├── LastModified (DateTime)
└── ConfigurationId (Guid)
Property Types Reference
Basic Types
| Type | Example Property | Validation | Category |
|---|---|---|---|
string |
ServerName | Required, Length(3-50) | Server Configuration |
int |
MaxConnections | Range(1-1000) | Server Configuration |
bool |
EnableLogging | None | Logging |
decimal |
Price (QuickStart) | None | Financial |
Advanced Types
| Type | Example Property | Notes | Category |
|---|---|---|---|
TimeSpan |
Timeout | Duration selection | Network |
DateTime |
CreatedDate | Date/time picker | Metadata |
Guid |
ConfigurationId | Unique identifier | Metadata |
List<string> |
AllowedIPs | Collection editing | Security |
Enum Types
| Enum | Values | Description Support | Category |
|---|---|---|---|
| ConnectionType | Http, Ssl, Tcp, Custom | ✅ | Server Configuration |
| LogLevel | Trace, Debug, Info, Warning, Error, Critical, None | ❌ | Logging |
| DatabaseProvider | SqlServer, PostgreSQL, MySQL, SQLite, Oracle | ✅ | Database |
Nested Objects
| Object | Properties | Implements INPC | Category |
|---|---|---|---|
| NetworkSettings | 4 properties | ✅ | Network |
| DatabaseSettings | 5 properties | ✅ | Database |
Validation Rules
String Validation
[Required(ErrorMessage = "Server name is required")]
[StringLength(50, MinimumLength = 3,
ErrorMessage = "Server name must be between 3 and 50 characters")]
public string ServerName { get; set; }
Numeric Range Validation
[Range(1, 1000, ErrorMessage = "Max connections must be between 1 and 1000")]
public int MaxConnections { get; set; }
Required Field Validation
[Required]
public string Hostname { get; set; }
Change Notification System
Implementation Pattern
private string _serverName = "Production-Server-01";
public string ServerName
{
get => _serverName;
set => SetProperty(ref _serverName, value);
}
protected bool SetProperty<T>(ref T field, T value,
[CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
LastModified = DateTime.Now; // Auto-update metadata
OnPropertyChanged(propertyName);
return true;
}
Cascading Updates
When ConnectionType changes, RequiresCertificate automatically updates:
public ConnectionType ConnectionType
{
get => _connectionType;
set
{
if (SetProperty(ref _connectionType, value))
{
OnPropertyChanged(nameof(RequiresCertificate)); // Cascade
}
}
}
public bool RequiresCertificate => ConnectionType == ConnectionType.Ssl;
Computed Properties
Computed properties derive their value from other properties:
[Display(Name = "Requires Certificate", Order = 4)]
[Category("Server Configuration")]
[ReadOnly(true)]
public bool RequiresCertificate => ConnectionType == ConnectionType.Ssl;
Benefits:
- Always in sync with source properties
- No manual updates needed
- Marked as read-only
- Displayed in PropertyGrid but not editable
Category Organization
Categories organize properties logically:
| Category | Purpose | Property Count |
|---|---|---|
| Server Configuration | Core server settings | 4 |
| Logging | Logging and diagnostics | 3 |
| Network | Network connectivity | 2 (+ nested) |
| Database | Database connection | 1 (nested) |
| Security | Authentication & access | 2 |
| Performance | Optimization settings | 2 |
| Metadata | System information | 3 |
Property Ordering
Properties within categories are ordered using the Order attribute:
[Display(Order = 1)] // First in category
[Display(Order = 2)] // Second in category
[Display(Order = 3)] // Third in category
Special Attributes
PasswordPropertyText
Hides sensitive information:
[PasswordPropertyText(true)]
public string ApiKey { get; set; }
EditorBrowsable
Groups advanced properties:
[EditorBrowsable(EditorBrowsableState.Advanced)]
public string LogFilePath { get; set; }
ReadOnly
Prevents property editing:
[ReadOnly(true)]
public DateTime CreatedDate { get; set; }
Data Binding in XAML
Setting DataContext
// In MainWindow.axaml.cs
public MainWindow()
{
InitializeComponent();
DataContext = new AdvancedConfiguration();
}
PropertyGrid Binding
<Window x:DataType="models:AdvancedConfiguration">
<pg:PropertyGrid SelectedObject="{Binding}"
ShowCategories="True"
ShowDescriptions="True" />
</Window>
Key Points:
x:DataTypeenables compiled bindings (.NET 10 requirement){Binding}binds to the entire DataContextShowCategoriesenables category groupingShowDescriptionsdisplays property descriptions
Performance Considerations
Efficient Change Notifications
protected bool SetProperty<T>(ref T field, T value, ...)
{
// Early exit if value hasn't changed
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
// Only notify if changed
field = value;
OnPropertyChanged(propertyName);
return true;
}
Lazy Initialization
private DatabaseSettings _databaseSettings = new(); // Initialized once
public DatabaseSettings DatabaseSettings => _databaseSettings;
Testing Scenarios
Validation Testing
- Enter invalid server name (< 3 chars or > 50 chars)
- Set MaxConnections outside range (< 1 or > 1000)
- Leave required fields empty
Change Notification Testing
- Change ConnectionType to/from Ssl
- Observe RequiresCertificate updates automatically
- Note LastModified timestamp changes
Nested Object Testing
- Expand NetworkSettings
- Modify nested properties
- Verify changes propagate correctly
Extension Points
Adding New Properties
- Add private backing field
- Create public property with notifications
- Add appropriate attributes
- Assign to category
Adding New Categories
Simply use a new category name:
[Category("New Category Name")]
public string NewProperty { get; set; }
Custom Validation
Implement IDataErrorInfo or INotifyDataErrorInfo:
public class AdvancedConfiguration : INotifyPropertyChanged, IDataErrorInfo
{
public string Error => string.Empty;
public string this[string columnName]
{
get
{
// Return validation error for property
return string.Empty;
}
}
}
Common Patterns
✅ DO
- Use INotifyPropertyChanged for all mutable properties
- Add descriptions to all properties
- Organize properties into logical categories
- Validate user input
- Use appropriate data types
- Mark computed properties as read-only
❌ DON'T
- Skip property change notifications
- Mix unrelated properties in same category
- Use generic category names like "Properties"
- Forget validation on user input
- Use string for structured data (use enums or objects)
- Make computed properties editable
Summary
The Advanced Example demonstrates:
- ✅ 25+ properties across 7 categories
- ✅ 2 levels of nested objects
- ✅ Full INotifyPropertyChanged implementation
- ✅ Comprehensive validation
- ✅ Computed properties
- ✅ Multiple data types
- ✅ Production-ready code structure
This serves as a blueprint for real-world PropertyGrid implementations.