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

5.2 KiB
Raw Blame History

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 + Cache
  • AddSharpWpf() - 自动注册 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() 包装可能出错的代码。

📚 延伸阅读

❓ 常见问题

Q: 为什么 MainWindow 不需要手动设置 DataContext? A: 通过 DI 容器获取 Window 时,会自动注入对应的 ViewModel。

Q: IsBusy 是在哪里定义的? A: IsBusy 是 ViewModelBase 的内置属性,用于显示加载状态。

Q: 可以不用 DI 吗? A: 可以,但不推荐。使用 DI 可以更好地管理依赖关系,便于测试。


开始编码吧! 🚀