You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
216 lines
7.1 KiB
216 lines
7.1 KiB
|
1 month ago
|
# MainWindow 关闭确认框实现分析
|
||
|
|
|
||
|
|
## 当前实现方式
|
||
|
|
|
||
|
|
当前实现将 `DialogHost` 包裹整个 `MainWindow.axaml`,结构如下:
|
||
|
|
|
||
|
|
```xml
|
||
|
|
<reactive:ReactiveWindow>
|
||
|
|
<dialogHost:DialogHost IsOpen="{Binding IsCloseConfirmDialogOpen}">
|
||
|
|
<dialogHost:DialogHost.DialogContent>
|
||
|
|
<!-- 对话框内容 -->
|
||
|
|
</dialogHost:DialogHost.DialogContent>
|
||
|
|
|
||
|
|
<Grid>
|
||
|
|
<!-- 窗口主要内容 -->
|
||
|
|
</Grid>
|
||
|
|
</dialogHost:DialogHost>
|
||
|
|
</reactive:ReactiveWindow>
|
||
|
|
```
|
||
|
|
|
||
|
|
## 主要缺点分析
|
||
|
|
|
||
|
|
### 1. **架构设计问题**
|
||
|
|
|
||
|
|
#### ❌ 职责混淆
|
||
|
|
- **问题**:`MainWindowViewModel` 既负责窗口导航和标签页管理,又负责关闭确认对话框的 UI 状态
|
||
|
|
- **影响**:违反了单一职责原则,ViewModel 职责过多
|
||
|
|
- **示例**:`MainWindowViewModel` 中包含了 8 个关闭确认框相关的属性
|
||
|
|
|
||
|
|
#### ❌ 窗口级别污染
|
||
|
|
- **问题**:DialogHost 包裹整个窗口,窗口的所有内容都成为 DialogHost 的子元素
|
||
|
|
- **影响**:窗口结构被对话框控件"绑架",不利于后续扩展
|
||
|
|
|
||
|
|
### 2. **可扩展性问题**
|
||
|
|
|
||
|
|
#### ❌ 无法添加多个对话框
|
||
|
|
- **问题**:一个 DialogHost 只能管理一个对话框内容
|
||
|
|
- **影响**:如果未来需要在窗口级别添加其他对话框(如设置对话框、关于对话框等),需要创建多个 DialogHost,结构会变得复杂
|
||
|
|
|
||
|
|
#### ❌ 对话框内容硬编码
|
||
|
|
- **问题**:对话框的 UI 结构直接写在 MainWindow.axaml 中
|
||
|
|
- **影响**:无法在其他地方复用相同的对话框组件
|
||
|
|
|
||
|
|
### 3. **可维护性问题**
|
||
|
|
|
||
|
|
#### ❌ XAML 文件过长
|
||
|
|
- **问题**:MainWindow.axaml 现在包含窗口布局(400+ 行)和对话框定义(50+ 行)
|
||
|
|
- **影响**:文件可读性下降,维护成本增加
|
||
|
|
|
||
|
|
#### ❌ 对话框逻辑分散
|
||
|
|
- **问题**:对话框的状态管理在 ViewModel,UI 定义在 View,事件处理在 Code-Behind
|
||
|
|
- **影响**:逻辑分散,难以追踪和维护
|
||
|
|
|
||
|
|
### 4. **代码质量问题**
|
||
|
|
|
||
|
|
#### ❌ 事件处理方式不够优雅
|
||
|
|
```csharp
|
||
|
|
// 当前实现
|
||
|
|
ViewModel.CloseWindowRequested += OnCloseWindowRequested;
|
||
|
|
```
|
||
|
|
- **问题**:使用传统的事件机制,而不是 ReactiveUI 的响应式编程模式
|
||
|
|
- **影响**:与项目中其他地方的 ReactiveCommand 风格不一致
|
||
|
|
|
||
|
|
#### ❌ 标志位管理
|
||
|
|
```csharp
|
||
|
|
private bool _isClosingConfirmed = false;
|
||
|
|
```
|
||
|
|
- **问题**:使用标志位来区分用户确认关闭和普通关闭
|
||
|
|
- **影响**:状态管理不够清晰,容易出现逻辑错误
|
||
|
|
|
||
|
|
### 5. **性能问题**
|
||
|
|
|
||
|
|
#### ❌ 不必要的属性绑定
|
||
|
|
- **问题**:关闭确认框的所有属性(标题、消息、图标、颜色等)都在 ViewModel 中定义,即使对话框很少显示
|
||
|
|
- **影响**:增加了 ViewModel 的内存占用和属性通知开销
|
||
|
|
|
||
|
|
#### ❌ DialogHost 始终存在
|
||
|
|
- **问题**:DialogHost 包裹整个窗口,即使对话框不显示时也存在
|
||
|
|
- **影响**:虽然影响很小,但理论上可以优化
|
||
|
|
|
||
|
|
### 6. **测试性问题**
|
||
|
|
|
||
|
|
#### ❌ 难以单元测试
|
||
|
|
- **问题**:对话框逻辑与窗口逻辑耦合,无法单独测试对话框功能
|
||
|
|
- **影响**:测试覆盖困难
|
||
|
|
|
||
|
|
### 7. **与项目其他部分不一致**
|
||
|
|
|
||
|
|
#### ❌ 风格不一致
|
||
|
|
- **问题**:`DialogHostPageView` 中 DialogHost 包裹的是页面内容,而 `MainWindow` 中包裹的是窗口内容
|
||
|
|
- **影响**:虽然都能工作,但结构不一致,可能造成理解困难
|
||
|
|
|
||
|
|
## 改进建议
|
||
|
|
|
||
|
|
### 方案 1:使用独立的对话框服务(推荐)
|
||
|
|
|
||
|
|
**优点**:
|
||
|
|
- 职责分离清晰
|
||
|
|
- 可复用性强
|
||
|
|
- 易于测试和维护
|
||
|
|
|
||
|
|
**实现**:
|
||
|
|
```csharp
|
||
|
|
// 创建 IDialogService 接口
|
||
|
|
public interface IDialogService
|
||
|
|
{
|
||
|
|
Task<bool> ShowConfirmDialogAsync(string title, string message);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 在 ViewModel 中使用
|
||
|
|
var result = await _dialogService.ShowConfirmDialogAsync("确认关闭", "确定要退出程序吗?");
|
||
|
|
if (result) Close();
|
||
|
|
```
|
||
|
|
|
||
|
|
### 方案 2:使用 UserControl 封装对话框
|
||
|
|
|
||
|
|
**优点**:
|
||
|
|
- 对话框内容可复用
|
||
|
|
- XAML 文件更简洁
|
||
|
|
|
||
|
|
**实现**:
|
||
|
|
```xml
|
||
|
|
<!-- 创建 CloseConfirmDialog.axaml -->
|
||
|
|
<UserControl>
|
||
|
|
<Border>
|
||
|
|
<!-- 对话框内容 -->
|
||
|
|
</Border>
|
||
|
|
</UserControl>
|
||
|
|
|
||
|
|
<!-- MainWindow.axaml -->
|
||
|
|
<dialogHost:DialogHost>
|
||
|
|
<dialogHost:DialogHost.DialogContent>
|
||
|
|
<local:CloseConfirmDialog />
|
||
|
|
</dialogHost:DialogHost.DialogContent>
|
||
|
|
<!-- 窗口内容 -->
|
||
|
|
</dialogHost:DialogHost>
|
||
|
|
```
|
||
|
|
|
||
|
|
### 方案 3:使用 ReactiveUI 的交互(推荐用于简单场景)
|
||
|
|
|
||
|
|
**优点**:
|
||
|
|
- 符合 ReactiveUI 最佳实践
|
||
|
|
- 代码更简洁
|
||
|
|
|
||
|
|
**实现**:
|
||
|
|
```csharp
|
||
|
|
// ViewModel
|
||
|
|
public Interaction<CloseConfirmViewModel, bool> CloseConfirmInteraction { get; }
|
||
|
|
|
||
|
|
// View
|
||
|
|
this.WhenActivated(disposables =>
|
||
|
|
{
|
||
|
|
ViewModel.CloseConfirmInteraction
|
||
|
|
.RegisterHandler(async interaction =>
|
||
|
|
{
|
||
|
|
var dialog = new CloseConfirmDialog { DataContext = interaction.Input };
|
||
|
|
var result = await dialog.ShowDialog<bool>(this);
|
||
|
|
interaction.SetOutput(result);
|
||
|
|
})
|
||
|
|
.DisposeWith(disposables);
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### 方案 4:将对话框移到窗口顶层(最小改动)
|
||
|
|
|
||
|
|
**优点**:
|
||
|
|
- 改动最小
|
||
|
|
- 保持当前架构
|
||
|
|
|
||
|
|
**实现**:
|
||
|
|
```xml
|
||
|
|
<!-- 将 DialogHost 放在 Grid 内部,而不是包裹整个 Grid -->
|
||
|
|
<Grid>
|
||
|
|
<!-- 窗口内容 -->
|
||
|
|
|
||
|
|
<!-- 对话框放在最上层 -->
|
||
|
|
<dialogHost:DialogHost IsOpen="{Binding IsCloseConfirmDialogOpen}"
|
||
|
|
Panel.ZIndex="9999">
|
||
|
|
<!-- 对话框内容 -->
|
||
|
|
</dialogHost:DialogHost>
|
||
|
|
</Grid>
|
||
|
|
```
|
||
|
|
|
||
|
|
## 对比总结
|
||
|
|
|
||
|
|
| 维度 | 当前实现 | 方案1(服务) | 方案2(UserControl) | 方案3(Interaction) | 方案4(顶层) |
|
||
|
|
|------|---------|-------------|---------------------|---------------------|--------------|
|
||
|
|
| 职责分离 | ❌ 差 | ✅ 优秀 | ⚠️ 一般 | ✅ 优秀 | ⚠️ 一般 |
|
||
|
|
| 可扩展性 | ❌ 差 | ✅ 优秀 | ⚠️ 一般 | ⚠️ 一般 | ⚠️ 一般 |
|
||
|
|
| 可维护性 | ❌ 差 | ✅ 优秀 | ✅ 良好 | ✅ 良好 | ⚠️ 一般 |
|
||
|
|
| 代码质量 | ⚠️ 一般 | ✅ 优秀 | ✅ 良好 | ✅ 优秀 | ⚠️ 一般 |
|
||
|
|
| 实现难度 | ✅ 简单 | ⚠️ 中等 | ✅ 简单 | ⚠️ 中等 | ✅ 简单 |
|
||
|
|
| 与项目一致性 | ⚠️ 一般 | ✅ 优秀 | ✅ 良好 | ✅ 优秀 | ✅ 良好 |
|
||
|
|
|
||
|
|
## 推荐方案
|
||
|
|
|
||
|
|
**短期(快速改进)**:使用方案 4,将 DialogHost 移到 Grid 内部,减少对现有架构的影响
|
||
|
|
|
||
|
|
**长期(最佳实践)**:采用方案 1 或方案 3,创建独立的对话框服务或使用 ReactiveUI Interaction,实现更好的职责分离和代码复用
|
||
|
|
|
||
|
|
## 当前实现的优点
|
||
|
|
|
||
|
|
虽然存在上述缺点,但当前实现也有其优点:
|
||
|
|
|
||
|
|
1. ✅ **快速实现**:改动最小,快速实现功能
|
||
|
|
2. ✅ **功能完整**:能够正常工作,满足需求
|
||
|
|
3. ✅ **视觉一致**:与 DialogHostPageView 的对话框样式保持一致
|
||
|
|
4. ✅ **简单直观**:代码结构相对简单,容易理解
|
||
|
|
|
||
|
|
## 结论
|
||
|
|
|
||
|
|
当前实现虽然可以工作,但在架构设计、可扩展性和可维护性方面存在明显不足。建议根据项目的发展阶段和团队规模,选择合适的改进方案。
|
||
|
|
|
||
|
|
对于小型项目或快速原型,当前实现可以接受;但对于需要长期维护和扩展的项目,建议采用更优雅的架构方案。
|
||
|
|
|