initial commit
This commit is contained in:
@@ -0,0 +1,335 @@
|
||||
# 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
|
||||
```csharp
|
||||
[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
|
||||
```csharp
|
||||
[Range(1, 1000, ErrorMessage = "Max connections must be between 1 and 1000")]
|
||||
public int MaxConnections { get; set; }
|
||||
```
|
||||
|
||||
### Required Field Validation
|
||||
```csharp
|
||||
[Required]
|
||||
public string Hostname { get; set; }
|
||||
```
|
||||
|
||||
## Change Notification System
|
||||
|
||||
### Implementation Pattern
|
||||
```csharp
|
||||
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:
|
||||
```csharp
|
||||
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:
|
||||
|
||||
```csharp
|
||||
[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:
|
||||
|
||||
```csharp
|
||||
[Display(Order = 1)] // First in category
|
||||
[Display(Order = 2)] // Second in category
|
||||
[Display(Order = 3)] // Third in category
|
||||
```
|
||||
|
||||
## Special Attributes
|
||||
|
||||
### PasswordPropertyText
|
||||
Hides sensitive information:
|
||||
```csharp
|
||||
[PasswordPropertyText(true)]
|
||||
public string ApiKey { get; set; }
|
||||
```
|
||||
|
||||
### EditorBrowsable
|
||||
Groups advanced properties:
|
||||
```csharp
|
||||
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
||||
public string LogFilePath { get; set; }
|
||||
```
|
||||
|
||||
### ReadOnly
|
||||
Prevents property editing:
|
||||
```csharp
|
||||
[ReadOnly(true)]
|
||||
public DateTime CreatedDate { get; set; }
|
||||
```
|
||||
|
||||
## Data Binding in XAML
|
||||
|
||||
### Setting DataContext
|
||||
```csharp
|
||||
// In MainWindow.axaml.cs
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new AdvancedConfiguration();
|
||||
}
|
||||
```
|
||||
|
||||
### PropertyGrid Binding
|
||||
```xml
|
||||
<Window x:DataType="models:AdvancedConfiguration">
|
||||
<pg:PropertyGrid SelectedObject="{Binding}"
|
||||
ShowCategories="True"
|
||||
ShowDescriptions="True" />
|
||||
</Window>
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- `x:DataType` enables compiled bindings (.NET 10 requirement)
|
||||
- `{Binding}` binds to the entire DataContext
|
||||
- `ShowCategories` enables category grouping
|
||||
- `ShowDescriptions` displays property descriptions
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Efficient Change Notifications
|
||||
```csharp
|
||||
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
|
||||
```csharp
|
||||
private DatabaseSettings _databaseSettings = new(); // Initialized once
|
||||
public DatabaseSettings DatabaseSettings => _databaseSettings;
|
||||
```
|
||||
|
||||
## Testing Scenarios
|
||||
|
||||
### Validation Testing
|
||||
1. Enter invalid server name (< 3 chars or > 50 chars)
|
||||
2. Set MaxConnections outside range (< 1 or > 1000)
|
||||
3. Leave required fields empty
|
||||
|
||||
### Change Notification Testing
|
||||
1. Change ConnectionType to/from Ssl
|
||||
2. Observe RequiresCertificate updates automatically
|
||||
3. Note LastModified timestamp changes
|
||||
|
||||
### Nested Object Testing
|
||||
1. Expand NetworkSettings
|
||||
2. Modify nested properties
|
||||
3. Verify changes propagate correctly
|
||||
|
||||
## Extension Points
|
||||
|
||||
### Adding New Properties
|
||||
1. Add private backing field
|
||||
2. Create public property with notifications
|
||||
3. Add appropriate attributes
|
||||
4. Assign to category
|
||||
|
||||
### Adding New Categories
|
||||
Simply use a new category name:
|
||||
```csharp
|
||||
[Category("New Category Name")]
|
||||
public string NewProperty { get; set; }
|
||||
```
|
||||
|
||||
### Custom Validation
|
||||
Implement `IDataErrorInfo` or `INotifyDataErrorInfo`:
|
||||
```csharp
|
||||
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.
|
||||
Reference in New Issue
Block a user