195 lines
5.2 KiB
Markdown
195 lines
5.2 KiB
Markdown
# 01 - 最小 WPF 应用
|
||
|
||
这是一个最简单的 Sharp WPF 应用示例,演示了框架的基础用法。
|
||
|
||
## 🎯 学习目标
|
||
|
||
- 了解 Sharp 框架的基本结构
|
||
- 掌握 MVVM 模式(ViewModelBase)
|
||
- 使用 Commands(RelayCommand, AsyncCommand)
|
||
- 使用对话框服务(IDialogService)
|
||
- 理解 Quick Start 配置方式
|
||
|
||
## 📦 使用的模块
|
||
|
||
- **Sharp.Common** - Result<T>, 基础工具
|
||
- **Sharp.Config** - 配置管理
|
||
- **Sharp.Wpf** - MVVM 基础设施
|
||
|
||
## 🏗️ 项目结构
|
||
|
||
```
|
||
01-MinimalWpfApp/
|
||
├── App.xaml # 应用程序入口
|
||
├── App.xaml.cs # Quick Start 配置
|
||
├── MainWindow.xaml # 主窗口界面
|
||
├── MainWindow.xaml.cs # 主窗口代码隐藏
|
||
├── ViewModels/
|
||
│ └── MainViewModel.cs # 主窗口 ViewModel
|
||
├── appsettings.json # 配置文件
|
||
└── README.md # 本文件
|
||
```
|
||
|
||
## 🚀 快速运行
|
||
|
||
```bash
|
||
cd examples/01-MinimalWpfApp
|
||
dotnet run
|
||
```
|
||
|
||
## 📝 核心代码讲解
|
||
|
||
### 1. App.xaml.cs - 应用启动
|
||
|
||
```csharp
|
||
protected override void OnStartup(StartupEventArgs e)
|
||
{
|
||
var services = new ServiceCollection();
|
||
|
||
// 一行添加基础服务(配置、日志、缓存)
|
||
services.AddSharpEssentials();
|
||
|
||
// 一行添加 WPF 服务(对话框、调度器、导航、主题)
|
||
services.AddSharpWpf();
|
||
|
||
// 注册 ViewModel 和 Window
|
||
services.AddViewModel<MainViewModel>();
|
||
services.AddTransient<MainWindow>();
|
||
|
||
// 构建服务提供者
|
||
_serviceProvider = services.BuildServiceProvider();
|
||
|
||
// 显示主窗口
|
||
var mainWindow = _serviceProvider.GetRequiredService<MainWindow>();
|
||
mainWindow.Show();
|
||
}
|
||
```
|
||
|
||
**关键点**:
|
||
- `AddSharpEssentials()` - 自动配置 Config + Log + Cache
|
||
- `AddSharpWpf()` - 自动注册 WPF 服务
|
||
- 无需手动配置,使用约定优于配置
|
||
|
||
### 2. MainViewModel.cs - ViewModel 示例
|
||
|
||
```csharp
|
||
public class MainViewModel : ViewModelBase
|
||
{
|
||
private readonly IDialogService _dialogService;
|
||
private string _message = "Hello, Sharp!";
|
||
private int _counter = 0;
|
||
|
||
public string Message
|
||
{
|
||
get => _message;
|
||
set => SetProperty(ref _message, value);
|
||
}
|
||
|
||
public int Counter
|
||
{
|
||
get => _counter;
|
||
set => SetProperty(ref _counter, value);
|
||
}
|
||
|
||
// 同步命令
|
||
public ICommand IncrementCommand { get; }
|
||
|
||
// 异步命令
|
||
public ICommand ShowDialogCommand { get; }
|
||
|
||
public MainViewModel(IDialogService dialogService)
|
||
{
|
||
_dialogService = dialogService;
|
||
|
||
// RelayCommand - 同步操作
|
||
IncrementCommand = new RelayCommand(() => {
|
||
Counter++;
|
||
Message = $"你点击了 {Counter} 次";
|
||
});
|
||
|
||
// AsyncCommand - 异步操作
|
||
ShowDialogCommand = new AsyncCommand(async (ct) => {
|
||
IsBusy = true; // 显示加载状态
|
||
|
||
// 模拟耗时操作
|
||
await Task.Delay(1000, ct);
|
||
|
||
_dialogService.ShowMessage($"当前计数: {Counter}", "提示");
|
||
|
||
IsBusy = false;
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
**关键点**:
|
||
- 继承 `ViewModelBase` 获得 INotifyPropertyChanged
|
||
- 使用 `SetProperty()` 自动触发属性变化通知
|
||
- `IsBusy` 属性用于显示加载状态
|
||
- `RelayCommand` 用于同步操作
|
||
- `AsyncCommand` 用于异步操作(支持取消)
|
||
|
||
### 3. MainWindow.xaml - 界面绑定
|
||
|
||
```xml
|
||
<Window ...>
|
||
<Window.DataContext>
|
||
<!-- ViewModel 会通过 DI 自动注入 -->
|
||
</Window.DataContext>
|
||
|
||
<StackPanel Margin="20">
|
||
<!-- 绑定属性 -->
|
||
<TextBlock Text="{Binding Message}" FontSize="24"/>
|
||
|
||
<!-- 绑定命令 -->
|
||
<Button Content="点击我" Command="{Binding IncrementCommand}"/>
|
||
|
||
<!-- 异步命令 -->
|
||
<Button Content="显示对话框" Command="{Binding ShowDialogCommand}"/>
|
||
|
||
<!-- 显示加载状态 -->
|
||
<TextBlock Text="加载中..." Visibility="{Binding IsBusy, Converter={StaticResource BoolToVisibilityConverter}}"/>
|
||
</StackPanel>
|
||
</Window>
|
||
```
|
||
|
||
**关键点**:
|
||
- 使用 `{Binding}` 绑定 ViewModel 属性
|
||
- Command 绑定到 ICommand 属性
|
||
- 使用内置转换器(BoolToVisibilityConverter)
|
||
|
||
## 🎓 练习建议
|
||
|
||
### 练习1:添加新属性
|
||
在 MainViewModel 中添加一个 `Name` 属性,并在界面上显示。
|
||
|
||
### 练习2:添加清零按钮
|
||
添加一个 `ResetCommand`,点击后将 `Counter` 重置为 0。
|
||
|
||
### 练习3:使用对话框确认
|
||
在 ResetCommand 中,先弹出确认对话框,用户确认后再清零。
|
||
|
||
### 练习4:尝试 Result<T>
|
||
修改 ShowDialogCommand,使用 `Result.Try()` 包装可能出错的代码。
|
||
|
||
## 📚 延伸阅读
|
||
|
||
- [Sharp.Wpf README](../../src/Sharp/Wpf/README.md) - WPF 模块完整文档
|
||
- [QUICKSTART.md](../../src/Sharp/QUICKSTART.md) - 快速启动指南
|
||
- 下一个示例:[02-WpfWithDatabase](../02-WpfWithDatabase/README.md)
|
||
|
||
## ❓ 常见问题
|
||
|
||
**Q: 为什么 MainWindow 不需要手动设置 DataContext?**
|
||
A: 通过 DI 容器获取 Window 时,会自动注入对应的 ViewModel。
|
||
|
||
**Q: IsBusy 是在哪里定义的?**
|
||
A: `IsBusy` 是 `ViewModelBase` 的内置属性,用于显示加载状态。
|
||
|
||
**Q: 可以不用 DI 吗?**
|
||
A: 可以,但不推荐。使用 DI 可以更好地管理依赖关系,便于测试。
|
||
|
||
---
|
||
|
||
**开始编码吧!** 🚀
|