Files
sharp-basic/examples/01-MinimalWpfApp/README.md
T

195 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 可以更好地管理依赖关系,便于测试。
---
**开始编码吧!** 🚀