01 - 最小 WPF 应用
这是一个最简单的 Sharp WPF 应用示例,演示了框架的基础用法。
🎯 学习目标
- 了解 Sharp 框架的基本结构
- 掌握 MVVM 模式(ViewModelBase)
- 使用 Commands(RelayCommand, AsyncCommand)
- 使用对话框服务(IDialogService)
- 理解 Quick Start 配置方式
📦 使用的模块
- Sharp.Common - Result, 基础工具
- 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 # 本文件
🚀 快速运行
cd examples/01-MinimalWpfApp
dotnet run
📝 核心代码讲解
1. App.xaml.cs - 应用启动
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 + CacheAddSharpWpf()- 自动注册 WPF 服务- 无需手动配置,使用约定优于配置
2. MainViewModel.cs - ViewModel 示例
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 - 界面绑定
<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
修改 ShowDialogCommand,使用 Result.Try() 包装可能出错的代码。
📚 延伸阅读
- Sharp.Wpf README - WPF 模块完整文档
- QUICKSTART.md - 快速启动指南
- 下一个示例:02-WpfWithDatabase
❓ 常见问题
Q: 为什么 MainWindow 不需要手动设置 DataContext? A: 通过 DI 容器获取 Window 时,会自动注入对应的 ViewModel。
Q: IsBusy 是在哪里定义的?
A: IsBusy 是 ViewModelBase 的内置属性,用于显示加载状态。
Q: 可以不用 DI 吗? A: 可以,但不推荐。使用 DI 可以更好地管理依赖关系,便于测试。
开始编码吧! 🚀