Files
YMhut-box-C-/参考/wpfui-main/docs/architecture/decisions/ADR-005-feature-folder-controls.md
T
2026-07-06 23:05:40 +08:00

396 lines
11 KiB
Markdown

# ADR-005: Feature Folder Organization for Controls
## Status
Accepted
## Context
With 77+ controls in the library, code organization becomes critical for maintainability. The structure must support:
- Clear isolation between controls
- Easy location of control-related files
- Logical grouping of complex control components
- Scalability as new controls are added
## Decision
### Feature Folder per Control
Each control resides in `Controls/{ControlName}/` directory:
```
Controls/
├── Button/
│ ├── Button.cs
│ └── Button.xaml
├── Card/
│ ├── Card.cs
│ └── Card.xaml
├── NavigationView/
│ ├── NavigationView.Base.cs
│ ├── NavigationView.Properties.cs
│ ├── NavigationView.Events.cs
│ ├── NavigationView.Navigation.cs
│ ├── NavigationView.TemplateParts.cs
│ ├── NavigationView.AttachedProperties.cs
│ ├── NavigationView.xaml
│ ├── NavigationViewItem.cs
│ ├── NavigationViewItemHeader.cs
│ └── NavigationViewItemSeparator.cs
```
**One control = one folder** with all related files.
## Structure Patterns
### Simple Controls
**Pattern:** Single .cs + .xaml pair
```
Badge/
├── Badge.cs # Control implementation
└── Badge.xaml # Implicit style
```
**Applies to:** Button, Badge, Card, InfoBar, TextBox, etc. (50+ controls)
### Complex Controls with Partial Classes
**Pattern:** Multiple partial class files organized by concern
```
NavigationView/
├── NavigationView.Base.cs # Core control logic
├── NavigationView.Properties.cs # 27 dependency properties
├── NavigationView.Events.cs # 7 routed events
├── NavigationView.Navigation.cs # Page navigation logic
├── NavigationView.TemplateParts.cs # Template part fields/binding
├── NavigationView.AttachedProperties.cs # Attached property definitions
└── NavigationView.xaml # Implicit style + template
```
**Partial class naming:** `{ControlName}.{Concern}.cs`
**Applies to:** NavigationView, ContentDialog, TitleBar
### Controls with Related Types
**Pattern:** Additional related controls in same folder
```
NavigationView/
├── NavigationView*.cs # Main control (6 files)
├── NavigationView.xaml
├── NavigationViewItem.cs # Item container
├── NavigationViewItemHeader.cs # Header item
├── NavigationViewItemSeparator.cs # Separator
├── NavigationViewContentPresenter.cs # Content host
├── INavigationView.cs # Interface
└── INavigationViewItem.cs # Item interface
```
**Rationale:** Tightly coupled types that are only used together.
### Controls with Supporting Subdirectories
**Pattern:** Subfolder for supporting types
```
ContentDialog/
├── ContentDialog.cs
├── ContentDialog.FocusBehavior.cs
├── ContentDialog.xaml
├── ContentDialogHost.cs
├── ContentDialogHostBehavior.cs
└── EventArgs/
├── ContentDialogButtonClickEventArgs.cs
├── ContentDialogClosingEventArgs.cs
└── ContentDialogClosedEventArgs.cs
```
**When to use subfolder:**
- 3+ related types (EventArgs, Converters, Enums)
- Clear sub-component (e.g., EventArgs)
## Flat Namespace Strategy
**All controls use:** `Wpf.Ui.Controls` namespace (regardless of folder depth)
```csharp
// File: Controls/NavigationView/NavigationView.cs
namespace Wpf.Ui.Controls; // Flat namespace, not Wpf.Ui.Controls.NavigationView
// ReSharper disable once CheckNamespace
```
**Trade-off:** Folder structure does not match namespace.
**IDE Configuration Required:**
```ini
# .editorconfig
dotnet_diagnostic.IDE0130.severity = none # Suppress namespace/folder mismatch
```
## Partial Class Decomposition Strategies
### By Concern
NavigationView splits by logical concern:
- **Base.cs** - Control infrastructure (template application, initialization)
- **Properties.cs** - Dependency property definitions
- **Events.cs** - Routed event definitions
- **Navigation.cs** - Page navigation logic
- **TemplateParts.cs** - Template part fields and OnApplyTemplate logic
- **AttachedProperties.cs** - Attached property definitions
### By Feature
ContentDialog splits by feature:
- **ContentDialog.cs** - Main implementation (async ShowAsync, dialog lifecycle)
- **ContentDialog.FocusBehavior.cs** - Keyboard focus management (isolated behavior)
### Guidelines for Splitting
**When to split:**
- File exceeds 500 lines
- Clear separation of concerns exists (properties vs. logic)
- Feature is self-contained and isolatable
**How to name:**
- **Base.cs** - Core control logic (template, initialization)
- **Properties.cs** - All dependency properties
- **Events.cs** - All routed events
- **{Feature}.cs** - Specific feature (FocusBehavior, Animation, etc.)
**Don't split:**
- Controls under 300 lines
- No clear separation of concerns
- When references would create circular dependencies
## File Naming Conventions
### Control Classes
```
{ControlName}.cs # Simple control
{ControlName}.{Concern}.cs # Partial class with concern
```
### XAML Styles
```
{ControlName}.xaml # Implicit style ResourceDictionary
```
### Supporting Types
```
{TypePurpose}{ControlName}.cs # e.g., NavigationViewItem
{ControlName}{Purpose}.cs # e.g., ContentDialogHost
I{ControlName}.cs # Interface
```
### Event Arguments
```
{ControlName}{EventName}EventArgs.cs
```
Examples:
- `ContentDialogButtonClickEventArgs.cs`
- `NavigatedEventArgs.cs`
## Enforcement
### MUST Follow
1. **One control = one folder** under `Controls/`
2. **Paired .cs + .xaml** with matching names
3. **Flat namespace** `Wpf.Ui.Controls` for all controls
4. **Partial class naming** `{ControlName}.{Concern}.cs`
5. **ReSharper suppress comment** when namespace doesn't match folder:
```csharp
namespace Wpf.Ui.Controls;
// ReSharper disable once CheckNamespace
```
6. **Keep related types together** in same folder when tightly coupled
### MUST NOT Do
1. **Never nest control folders** (keep flat under Controls/)
```
❌ Controls/Buttons/Button/
✅ Controls/Button/
```
2. **Never use category-specific namespace**
```csharp
❌ namespace Wpf.Ui.Controls.Buttons;
✅ namespace Wpf.Ui.Controls;
```
3. **Never split files arbitrarily** (must have clear separation of concerns)
4. **Never create more than 2 directory levels** under Controls/
```
✅ Controls/ContentDialog/EventArgs/
❌ Controls/ContentDialog/EventArgs/Closing/
```
### Verification
1. **IDE0130 suppressed** in .editorconfig for namespace/folder mismatch
2. **Code review** checks folder organization
3. **Naming conventions** enforced during PR review
## Benefits
### Developer Experience
**Easy to Find:**
```bash
# Looking for Button control?
Controls/Button/Button.cs # Immediately obvious location
```
**Clear Boundaries:**
- All Button-related code in `Controls/Button/`
- No cross-control dependencies (enforced by folder isolation)
**Scalable:**
- Adding new control = create new folder
- Doesn't affect existing controls
### Maintainability
**Isolation:**
- Changes to Button don't affect Card
- Merge conflicts localized to single control
**Discoverability:**
- New developers find controls by folder name
- Related types co-located (NavigationViewItem with NavigationView)
**Refactoring:**
- Easy to split large files (add `.Properties.cs`)
- Clear when to split (file size, concern separation)
### Build Performance
**Partial Classes:**
- Compiler can parallelize partial class compilation
- Changes to Properties.cs don't trigger recompilation of Navigation.cs
## Trade-offs
### Advantages
- ✅ Clear physical organization
- ✅ Easy to locate control files
- ✅ Enforces encapsulation (hard to reference across control folders)
- ✅ Scales to 100+ controls
- ✅ Supports complex controls with many files
### Disadvantages
- ❌ Folder/namespace mismatch requires suppression
- ❌ 77+ folders in single directory (large directory)
- ❌ No physical categorization (all controls appear equal)
- ❌ Related controls may be far apart alphabetically (Button vs. ToggleButton)
## Alternatives Considered
### Category-Based Folders
```
Controls/
├── Buttons/
│ ├── Button/
│ └── ToggleButton/
└── Navigation/
└── NavigationView/
```
**Rejected:**
- Requires category-specific namespaces or deeper mismatch
- Category classification is subjective (is ToggleSwitch a Button or Input?)
- Doesn't align with flat namespace strategy
### Single File Per Control
```
Controls/
├── Button.cs
├── Button.xaml
├── NavigationView.cs
└── NavigationView.xaml
```
**Rejected:**
- Complex controls (NavigationView 1000+ lines) unmanageable
- Supporting types have unclear location
- 150+ files in single directory
### Hybrid Categorization
```
Controls/
├── Button/
├── Input/
│ ├── TextBox/
│ └── NumberBox/
└── Navigation/
└── NavigationView/
```
**Rejected:**
- Inconsistent structure (some categories, some not)
- Unclear where new controls go
- Complicates namespace strategy
## Migration Path
### Adding New Simple Control
1. Create folder `Controls/{NewControl}/`
2. Add `{NewControl}.cs` with control class
3. Add `{NewControl}.xaml` with implicit style
4. Add ReSharper suppress comment to .cs file
### Splitting Existing Control
Example: Card becomes too large
**Before:**
```
Card/
├── Card.cs (500 lines)
└── Card.xaml
```
**After:**
```
Card/
├── Card.Base.cs (200 lines - core logic)
├── Card.Properties.cs (100 lines - dependency properties)
├── Card.Animation.cs (100 lines - animation logic)
└── Card.xaml
```
**Refactoring steps:**
1. Extract dependency properties to `Card.Properties.cs`
2. Extract animation logic to `Card.Animation.cs`
3. Keep core logic in `Card.Base.cs`
4. All files use `partial class Card`
## Documentation
### Control Folder README
Each complex control folder includes README.md:
```markdown
# NavigationView
Complex navigation container with 6 partial class files:
- **Base.cs** - Core control logic, template application
- **Properties.cs** - 27 dependency properties
- **Events.cs** - 7 routed events
- **Navigation.cs** - Page navigation, journal, back/forward
- **TemplateParts.cs** - Template part bindings
- **AttachedProperties.cs** - HeaderContent attached property
Related types:
- NavigationViewItem - Selectable item container
- NavigationViewItemHeader - Non-selectable header
- INavigationView - Public control interface
```
## References
- [Feature Folders in ASP.NET](https://docs.microsoft.com/archive/msdn-magazine/2016/september/asp-net-core-feature-slices-for-asp-net-core-mvc) (similar pattern)
- [Vertical Slice Architecture](https://jimmybogard.com/vertical-slice-architecture/)