A:简化HandyControl引用
This commit is contained in:
1 parent
2563288afe
commit
37aa848b5f
13 files changed
+1636
-289
No files matched your search
@@ -1,12 +1,9 @@
|
|||||||
<Application x:Class="Client.App"
|
<Application x:Class="Client.App"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<!-- HandyControl 主题资源已通过 AddSharpHandyControl() 自动注册,无需手动添加 -->
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
|
||||||
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml" />
|
|
||||||
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml" />
|
|
||||||
</ResourceDictionary.MergedDictionaries>
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
</Application>
|
</Application>
|
||||||
@@ -12,6 +12,7 @@ using Sharp.Log.Extensions;
|
|||||||
using Sharp.NetServer.DependencyInjection;
|
using Sharp.NetServer.DependencyInjection;
|
||||||
using Sharp.Wpf.Services;
|
using Sharp.Wpf.Services;
|
||||||
using Sharp.Wpf.Abstractions;
|
using Sharp.Wpf.Abstractions;
|
||||||
|
using Sharp.Wpf.HandyControl.DependencyInjection;
|
||||||
using Plc.Siemens.DependencyInjection;
|
using Plc.Siemens.DependencyInjection;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Sharp.Process.DependencyInjection;
|
using Sharp.Process.DependencyInjection;
|
||||||
@@ -114,7 +115,7 @@ namespace Client
|
|||||||
services.AddSharpPython();
|
services.AddSharpPython();
|
||||||
|
|
||||||
// WPF app services
|
// WPF app services
|
||||||
services.AddSingleton<IDialogService, DialogService>();
|
services.AddSharpHandyControl(); // HandyControl 对话框服务(也注册了 IDialogService)
|
||||||
services.AddSingleton<IAuthenticationService, DemoAuthenticationService>();
|
services.AddSingleton<IAuthenticationService, DemoAuthenticationService>();
|
||||||
services.AddSingleton<IUserSession, UserSession>();
|
services.AddSingleton<IUserSession, UserSession>();
|
||||||
// Register DispatcherService with the current UI dispatcher
|
// Register DispatcherService with the current UI dispatcher
|
||||||
|
|||||||
@@ -11,11 +11,11 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
|
||||||
<PackageReference Include="HandyControl" Version="3.5.0" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Wpf\Wpf.csproj" />
|
<ProjectReference Include="..\Wpf\Wpf.csproj" />
|
||||||
|
<ProjectReference Include="..\Wpf.HandyControl\Wpf.HandyControl.csproj" />
|
||||||
<ProjectReference Include="..\Config\Config.csproj" />
|
<ProjectReference Include="..\Config\Config.csproj" />
|
||||||
<ProjectReference Include="..\NetClient\NetClient.csproj" />
|
<ProjectReference Include="..\NetClient\NetClient.csproj" />
|
||||||
<ProjectReference Include="..\NetServer\NetServer.csproj" />
|
<ProjectReference Include="..\NetServer\NetServer.csproj" />
|
||||||
|
|||||||
@@ -2,12 +2,9 @@
|
|||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="clr-namespace:Server">
|
xmlns:local="clr-namespace:Server">
|
||||||
|
<!-- HandyControl 主题资源已通过 AddSharpHandyControl() 自动注册,无需手动添加 -->
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
|
||||||
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml" />
|
|
||||||
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml" />
|
|
||||||
</ResourceDictionary.MergedDictionaries>
|
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
</Application>
|
</Application>
|
||||||
@@ -9,6 +9,7 @@ using Sharp.Data;
|
|||||||
using Sharp.Data.Context;
|
using Sharp.Data.Context;
|
||||||
using Sharp.Wpf.Services;
|
using Sharp.Wpf.Services;
|
||||||
using Sharp.Wpf.Abstractions;
|
using Sharp.Wpf.Abstractions;
|
||||||
|
using Sharp.Wpf.HandyControl.DependencyInjection;
|
||||||
using Server.Config;
|
using Server.Config;
|
||||||
using Server.Services;
|
using Server.Services;
|
||||||
using Server.Data;
|
using Server.Data;
|
||||||
@@ -120,7 +121,7 @@ namespace Server
|
|||||||
services.AddSingleton<ServerService>();
|
services.AddSingleton<ServerService>();
|
||||||
|
|
||||||
// WPF服务
|
// WPF服务
|
||||||
services.AddSingleton<IDialogService, DialogService>();
|
services.AddSharpHandyControl(); // HandyControl 对话框服务(也注册了 IDialogService)
|
||||||
services.AddSingleton<IDispatcher>(new DispatcherService(Dispatcher));
|
services.AddSingleton<IDispatcher>(new DispatcherService(Dispatcher));
|
||||||
|
|
||||||
// ViewModels
|
// ViewModels
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
<ProjectReference Include="..\Log\Log.csproj" />
|
<ProjectReference Include="..\Log\Log.csproj" />
|
||||||
<ProjectReference Include="..\Config\Config.csproj" />
|
<ProjectReference Include="..\Config\Config.csproj" />
|
||||||
<ProjectReference Include="..\Wpf\Wpf.csproj" />
|
<ProjectReference Include="..\Wpf\Wpf.csproj" />
|
||||||
|
<ProjectReference Include="..\Wpf.HandyControl\Wpf.HandyControl.csproj" />
|
||||||
<ProjectReference Include="..\Common\Common.csproj" />
|
<ProjectReference Include="..\Common\Common.csproj" />
|
||||||
<ProjectReference Include="..\Data\Data.csproj" />
|
<ProjectReference Include="..\Data\Data.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -28,7 +29,6 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
|
||||||
<PackageReference Include="HandyControl" Version="3.5.0" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
+105
@@ -0,0 +1,105 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Sharp.Wpf.HandyControl.Options;
|
||||||
|
using Sharp.Wpf.HandyControl.Services;
|
||||||
|
using Sharp.Wpf.Services;
|
||||||
|
|
||||||
|
namespace Sharp.Wpf.HandyControl.DependencyInjection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HandyControl 服务注册扩展
|
||||||
|
/// </summary>
|
||||||
|
public static class HandyControlServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 添加 Sharp.Wpf.HandyControl 服务
|
||||||
|
/// 默认会自动注册 HandyControl 主题资源到 Application.Resources,无需在 App.xaml 中手动添加
|
||||||
|
/// 约定配置节名称:HandyControl
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">服务集合</param>
|
||||||
|
/// <returns>服务集合</returns>
|
||||||
|
public static IServiceCollection AddSharpHandyControl(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
return services.AddSharpHandyControl(options => { });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加 Sharp.Wpf.HandyControl 服务(带配置选项)
|
||||||
|
/// 默认会自动注册 HandyControl 主题资源到 Application.Resources,无需在 App.xaml 中手动添加
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">服务集合</param>
|
||||||
|
/// <param name="configure">配置委托</param>
|
||||||
|
/// <returns>服务集合</returns>
|
||||||
|
public static IServiceCollection AddSharpHandyControl(
|
||||||
|
this IServiceCollection services,
|
||||||
|
Action<HandyControlOptions> configure)
|
||||||
|
{
|
||||||
|
var options = new HandyControlOptions();
|
||||||
|
configure(options);
|
||||||
|
|
||||||
|
// 自动注册主题资源(如果启用)
|
||||||
|
if (options.AutoRegisterTheme)
|
||||||
|
{
|
||||||
|
RegisterTheme(options.Theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注册配置选项
|
||||||
|
services.TryAddSingleton(options);
|
||||||
|
|
||||||
|
// 注册 HandyControl 对话框服务
|
||||||
|
services.TryAddSingleton<IHandyDialogService, HandyDialogService>();
|
||||||
|
|
||||||
|
// 也注册为基础的 IDialogService,方便兼容使用
|
||||||
|
services.TryAddSingleton<IDialogService>(sp => sp.GetRequiredService<IHandyDialogService>());
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册 HandyControl 主题资源
|
||||||
|
/// </summary>
|
||||||
|
private static void RegisterTheme(HandyControlTheme theme)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
// 检查是否已经添加过 HandyControl 主题资源
|
||||||
|
var exists = Application.Current.Resources.MergedDictionaries
|
||||||
|
.Any(d => d.Source?.OriginalString.Contains("HandyControl") == true);
|
||||||
|
|
||||||
|
if (!exists)
|
||||||
|
{
|
||||||
|
// 根据主题类型选择皮肤文件
|
||||||
|
var skinUri = theme switch
|
||||||
|
{
|
||||||
|
HandyControlTheme.Light => "pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml",
|
||||||
|
HandyControlTheme.Dark => "pack://application:,,,/HandyControl;component/Themes/SkinDark.xaml",
|
||||||
|
_ => "pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加皮肤资源
|
||||||
|
var skinDictionary = new ResourceDictionary
|
||||||
|
{
|
||||||
|
Source = new Uri(skinUri)
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加主题资源
|
||||||
|
var themeDictionary = new ResourceDictionary
|
||||||
|
{
|
||||||
|
Source = new Uri("pack://application:,,,/HandyControl;component/Themes/Theme.xaml")
|
||||||
|
};
|
||||||
|
|
||||||
|
Application.Current.Resources.MergedDictionaries.Add(skinDictionary);
|
||||||
|
Application.Current.Resources.MergedDictionaries.Add(themeDictionary);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// 如果在非 UI 线程或 Application 未初始化时调用,记录警告但不中断程序
|
||||||
|
System.Diagnostics.Debug.WriteLine($"警告: 无法自动注册 HandyControl 主题资源: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
namespace Sharp.Wpf.HandyControl.Options;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HandyControl 配置选项
|
||||||
|
/// </summary>
|
||||||
|
public class HandyControlOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 是否自动注册主题资源(默认:true)
|
||||||
|
/// 如果为 true,会在调用 AddSharpHandyControl() 时自动添加主题到 Application.Resources
|
||||||
|
/// </summary>
|
||||||
|
public bool AutoRegisterTheme { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 主题类型(默认:Light 浅色主题)
|
||||||
|
/// </summary>
|
||||||
|
public HandyControlTheme Theme { get; set; } = HandyControlTheme.Light;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认 Growl 容器标识(默认:"Default")
|
||||||
|
/// </summary>
|
||||||
|
public string DefaultGrowlToken { get; set; } = "Default";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Growl 通知显示时长(秒)(默认:3)
|
||||||
|
/// </summary>
|
||||||
|
public int GrowlWaitTime { get; set; } = 3;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
namespace Sharp.Wpf.HandyControl.Options;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HandyControl 主题类型
|
||||||
|
/// </summary>
|
||||||
|
public enum HandyControlTheme
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 浅色主题(默认)
|
||||||
|
/// </summary>
|
||||||
|
Light,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 深色主题
|
||||||
|
/// </summary>
|
||||||
|
Dark
|
||||||
|
}
|
||||||
+1142
-277
@@ -4,15 +4,41 @@
|
|||||||
|
|
||||||
HandyControl UI库集成模块,为 Sharp.Wpf 提供 HandyControl 的便捷集成和扩展。
|
HandyControl UI库集成模块,为 Sharp.Wpf 提供 HandyControl 的便捷集成和扩展。
|
||||||
|
|
||||||
## 📋 模块说明
|
---
|
||||||
|
|
||||||
本模块基于 [HandyControl](https://github.com/HandyOrg/HandyControl) UI库,提供:
|
## 📋 目录
|
||||||
- HandyControl 主题集成
|
|
||||||
- 便捷的服务注册扩展
|
|
||||||
- 常用对话框封装
|
|
||||||
- 自定义控件扩展
|
|
||||||
|
|
||||||
## 🎯 为什么要拆分?
|
- [模块介绍](#-模块介绍)
|
||||||
|
- [为什么选择这个模块](#-为什么选择这个模块)
|
||||||
|
- [快速开始](#-快速开始)
|
||||||
|
- [核心 API](#-核心-api)
|
||||||
|
- [典型使用场景](#-典型使用场景)
|
||||||
|
- [配置选项](#️-配置选项)
|
||||||
|
- [Growl 通知配置](#-growl-通知配置)
|
||||||
|
- [主题定制](#-主题定制)
|
||||||
|
- [迁移指南](#-迁移指南)
|
||||||
|
- [常见问题](#-常见问题)
|
||||||
|
- [最佳实践](#-最佳实践)
|
||||||
|
- [API 参考](#-api-参考)
|
||||||
|
- [参考资料](#-参考资料)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📖 模块介绍
|
||||||
|
|
||||||
|
### 什么是 Sharp.Wpf.HandyControl?
|
||||||
|
|
||||||
|
本模块基于 [HandyControl 3.5.0](https://github.com/HandyOrg/HandyControl) UI库,为 WPF 应用提供现代化的 UI 组件和对话框服务。通过依赖注入的方式,让你用最少的代码享受 HandyControl 的所有功能。
|
||||||
|
|
||||||
|
### 核心特性
|
||||||
|
|
||||||
|
- ✅ **一行代码集成** - 自动注册主题资源,无需修改 App.xaml
|
||||||
|
- ✅ **依赖注入友好** - 完全基于 DI,支持单元测试
|
||||||
|
- ✅ **丰富的对话框 API** - 成功/信息/警告/错误/确认对话框
|
||||||
|
- ✅ **Growl 通知系统** - 优雅的右下角弹出通知
|
||||||
|
- ✅ **主题切换** - 支持浅色/深色主题一键切换
|
||||||
|
- ✅ **兼容性好** - 实现 IDialogService 接口,可无缝替换其他实现
|
||||||
|
- ✅ **线程安全** - 自动处理 UI 线程调度
|
||||||
|
|
||||||
### 模块分离策略
|
### 模块分离策略
|
||||||
|
|
||||||
@@ -24,10 +50,10 @@ Sharp.Wpf (Core)
|
|||||||
└── 行为(EventToCommand)
|
└── 行为(EventToCommand)
|
||||||
|
|
||||||
Sharp.Wpf.HandyControl(本模块)
|
Sharp.Wpf.HandyControl(本模块)
|
||||||
├── HandyControl主题配置
|
├── HandyControl主题自动配置
|
||||||
├── IDialogService的HandyControl实现
|
├── IDialogService 的 HandyControl 实现
|
||||||
├── HandyControl控件扩展
|
├── HandyControl 特色功能封装(Growl、深色主题等)
|
||||||
└── 样式和资源
|
└── DI 扩展方法(AddSharpHandyControl)
|
||||||
```
|
```
|
||||||
|
|
||||||
**优势**:
|
**优势**:
|
||||||
@@ -37,82 +63,190 @@ Sharp.Wpf.HandyControl(本模块)
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 快速开始
|
## 🎯 为什么选择这个模块?
|
||||||
|
|
||||||
### 1. 安装包
|
### ❌ 传统方式的痛点
|
||||||
```bash
|
|
||||||
# 安装 Sharp.Wpf(必需)
|
|
||||||
Install-Package Sharp.Wpf
|
|
||||||
|
|
||||||
# 安装 Sharp.Wpf.HandyControl(可选)
|
**直接使用 HandyControl:**
|
||||||
Install-Package Sharp.Wpf.HandyControl
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 添加主题资源
|
|
||||||
```xml
|
|
||||||
<!-- App.xaml -->
|
|
||||||
<Application x:Class="MyApp.App"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:hc="https://handyorg.github.io/handycontrol">
|
|
||||||
<Application.Resources>
|
|
||||||
<ResourceDictionary>
|
|
||||||
<ResourceDictionary.MergedDictionaries>
|
|
||||||
<!-- HandyControl 主题 -->
|
|
||||||
<hc:ThemeResources/>
|
|
||||||
<hc:Theme/>
|
|
||||||
|
|
||||||
<!-- 可选:深色主题 -->
|
|
||||||
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDark.xaml"/>
|
|
||||||
</ResourceDictionary.MergedDictionaries>
|
|
||||||
</ResourceDictionary>
|
|
||||||
</Application.Resources>
|
|
||||||
</Application>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 注册服务
|
|
||||||
```csharp
|
```csharp
|
||||||
// App.xaml.cs
|
// App.xaml - 需要手动配置主题
|
||||||
protected override void OnStartup(StartupEventArgs e) {
|
<ResourceDictionary.MergedDictionaries>
|
||||||
var services = new ServiceCollection();
|
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml" />
|
||||||
|
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml" />
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|
||||||
// 注册基础 Wpf 服务
|
// 代码中 - 静态调用,难以测试
|
||||||
services.AddWpfServices();
|
using HandyControl.Controls;
|
||||||
|
MessageBox.Show("消息", "标题");
|
||||||
|
MessageBox.Error("错误信息");
|
||||||
|
```
|
||||||
|
|
||||||
// 注册 HandyControl 集成服务
|
**问题:**
|
||||||
services.AddHandyControlServices();
|
- ❌ 需要手动配置 XAML 资源字典
|
||||||
|
- ❌ 静态调用,无法进行单元测试
|
||||||
|
- ❌ 不支持依赖注入
|
||||||
|
- ❌ 主题切换不方便
|
||||||
|
- ❌ 在 ViewModel 中直接引用 UI 库
|
||||||
|
|
||||||
var provider = services.BuildServiceProvider();
|
### ✅ 使用本模块的优势
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// App.xaml.cs - 一行代码搞定
|
||||||
|
services.AddSharpHandyControl();
|
||||||
|
|
||||||
|
// ViewModel - 依赖注入,易于测试
|
||||||
|
public class MyViewModel
|
||||||
|
{
|
||||||
|
private readonly IHandyDialogService _dialog;
|
||||||
|
|
||||||
|
public MyViewModel(IHandyDialogService dialog)
|
||||||
|
{
|
||||||
|
_dialog = dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowMessage()
|
||||||
|
{
|
||||||
|
_dialog.ShowSuccess("操作成功!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. 使用 HandyControl 对话框
|
**优势:**
|
||||||
|
- ✅ 无需配置 App.xaml,自动注册主题
|
||||||
|
- ✅ 完全基于依赖注入,支持单元测试
|
||||||
|
- ✅ ViewModel 不直接依赖 UI 库
|
||||||
|
- ✅ 主题切换只需修改一行配置
|
||||||
|
- ✅ 统一的对话框 API,易于维护
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 快速开始
|
||||||
|
|
||||||
|
### 第 1 步:注册服务(一行代码)
|
||||||
|
|
||||||
|
**真正的一行代码集成 - 无需修改 App.xaml!**
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public class MainViewModel : ViewModelBase {
|
// App.xaml.cs
|
||||||
private readonly IHandyDialogService _dialogService;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Sharp.Wpf.HandyControl.DependencyInjection;
|
||||||
|
|
||||||
public MainViewModel(IHandyDialogService dialogService) {
|
namespace MyApp
|
||||||
_dialogService = dialogService;
|
{
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
protected override void OnStartup(StartupEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnStartup(e);
|
||||||
|
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
|
||||||
|
// ← 一行搞定,连 App.xaml 都不用改!
|
||||||
|
services.AddSharpHandyControl();
|
||||||
|
|
||||||
|
// 注册你的 ViewModels 和服务
|
||||||
|
services.AddTransient<MainViewModel>();
|
||||||
|
services.AddTransient<MainWindow>();
|
||||||
|
|
||||||
|
var provider = services.BuildServiceProvider();
|
||||||
|
|
||||||
|
// 显示主窗口
|
||||||
|
var mainWindow = provider.GetRequiredService<MainWindow>();
|
||||||
|
mainWindow.Show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
private void ShowMessage() {
|
**✨ 自动完成的事情:**
|
||||||
// HandyControl 样式的消息框
|
- ✅ 注册 HandyControl 主题资源到 `Application.Resources`
|
||||||
_dialogService.ShowSuccess("操作成功!");
|
- ✅ 注册 `IHandyDialogService` 服务
|
||||||
_dialogService.ShowInfo("这是一条信息");
|
- ✅ 注册 `IDialogService` 服务(兼容基础接口)
|
||||||
_dialogService.ShowWarning("警告信息");
|
- ✅ 配置默认的浅色主题
|
||||||
_dialogService.ShowError("错误信息");
|
|
||||||
|
### 第 2 步:在 ViewModel 中使用
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Sharp.Wpf.HandyControl.Services;
|
||||||
|
using Sharp.Wpf.ViewModels;
|
||||||
|
|
||||||
|
namespace MyApp.ViewModels
|
||||||
|
{
|
||||||
|
public class MainViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private readonly IHandyDialogService _dialog;
|
||||||
|
|
||||||
|
public MainViewModel(IHandyDialogService dialog)
|
||||||
|
{
|
||||||
|
_dialog = dialog;
|
||||||
|
|
||||||
|
SaveCommand = new AsyncCommand(SaveAsync);
|
||||||
|
DeleteCommand = new AsyncCommand(DeleteAsync);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IAsyncCommand SaveCommand { get; }
|
||||||
|
public IAsyncCommand DeleteCommand { get; }
|
||||||
|
|
||||||
|
private async Task SaveAsync()
|
||||||
|
{
|
||||||
|
IsBusy = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 执行保存操作
|
||||||
|
await Task.Delay(1000); // 模拟保存
|
||||||
|
|
||||||
|
_dialog.ShowSuccess("保存成功!");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_dialog.ShowError($"保存失败:{ex.Message}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeleteAsync()
|
||||||
|
{
|
||||||
|
// 显示确认对话框
|
||||||
|
var confirmed = await _dialog.ConfirmAsync(
|
||||||
|
"确定要删除这条记录吗?\n删除后无法恢复。",
|
||||||
|
"确认删除"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (confirmed)
|
||||||
|
{
|
||||||
|
// 执行删除操作
|
||||||
|
_dialog.ShowSuccess("删除成功");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
private async Task<bool> Confirm() {
|
### 第 3 步:在 Window 中使用(可选)
|
||||||
// 确认对话框
|
|
||||||
return await _dialogService.ConfirmAsync("确定要删除吗?", "确认删除");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ShowInputDialog() {
|
```csharp
|
||||||
// 输入对话框
|
using Sharp.Wpf.HandyControl.Services;
|
||||||
var result = await _dialogService.ShowInputAsync("请输入名称:", "新建项目");
|
|
||||||
if (result.IsSuccess) {
|
namespace MyApp.Views
|
||||||
ProjectName = result.Value;
|
{
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
private readonly IHandyDialogService _dialog;
|
||||||
|
|
||||||
|
public MainWindow(MainViewModel viewModel, IHandyDialogService dialog)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
_dialog = dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHelpButtonClick(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_dialog.ShowInfo("这是帮助信息...", "帮助");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,55 +257,86 @@ public class MainViewModel : ViewModelBase {
|
|||||||
## 📚 核心 API
|
## 📚 核心 API
|
||||||
|
|
||||||
### IHandyDialogService 接口
|
### IHandyDialogService 接口
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public interface IHandyDialogService : IDialogService {
|
public interface IHandyDialogService : IDialogService
|
||||||
|
{
|
||||||
|
// ========== 消息框(阻塞式) ==========
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 显示成功消息(绿色,带勾图标)
|
/// 显示成功消息(绿色图标)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ShowSuccess(string message, string? title = null);
|
void ShowSuccess(string message, string? title = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 显示信息消息(蓝色,带i图标)
|
/// 显示信息消息(蓝色图标)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ShowInfo(string message, string? title = null);
|
void ShowInfo(string message, string? title = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 显示警告消息(橙色,带!图标)
|
/// 显示警告消息(橙色图标)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ShowWarning(string message, string? title = null);
|
void ShowWarning(string message, string? title = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 显示错误消息(红色,带X图标)
|
/// 显示错误消息(红色图标)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void ShowError(string message, string? title = null);
|
void ShowError(string message, string? title = null);
|
||||||
|
|
||||||
|
// ========== 对话框(异步) ==========
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 显示确认对话框
|
/// 显示确认对话框(确定/取消)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <returns>用户点击"确定"返回 true,"取消"返回 false</returns>
|
||||||
Task<bool> ConfirmAsync(string message, string? title = null);
|
Task<bool> ConfirmAsync(string message, string? title = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 显示输入对话框
|
/// 显示询问对话框(是/否/取消)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task<Result<string>> ShowInputAsync(
|
/// <returns>HandyDialogResult 枚举值</returns>
|
||||||
string message,
|
Task<HandyDialogResult> AskAsync(string message, string? title = null);
|
||||||
string? title = null,
|
|
||||||
string? defaultValue = null);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 显示等待对话框(异步操作)
|
/// 显示输入对话框(尚未实现)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task ShowWaitingAsync(string message, Func<Task> action);
|
Task<Result<string>> ShowInputAsync(string message, string? title = null, string? defaultValue = null);
|
||||||
|
|
||||||
|
// ========== Growl 通知(非阻塞) ==========
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 显示进度对话框
|
/// 显示 Growl 通知(右下角弹出)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IProgress<double> ShowProgress(string message, string? title = null);
|
/// <param name="message">通知消息</param>
|
||||||
|
/// <param name="type">通知类型</param>
|
||||||
|
/// <param name="token">Growl 容器标识(可选)</param>
|
||||||
|
void ShowNotification(string message, NotificationType type = NotificationType.Info, string? token = null);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
/// <summary>
|
### NotificationType 枚举
|
||||||
/// 显示通知(Growl,右下角弹出)
|
|
||||||
/// </summary>
|
```csharp
|
||||||
void ShowNotification(string message, NotificationType type = NotificationType.Info);
|
public enum NotificationType
|
||||||
|
{
|
||||||
|
Info, // 信息(蓝色图标)
|
||||||
|
Success, // 成功(绿色图标)
|
||||||
|
Warning, // 警告(橙色图标)
|
||||||
|
Error, // 错误(红色图标)
|
||||||
|
Fatal // 严重错误(深红色图标)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### HandyDialogResult 枚举
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public enum HandyDialogResult
|
||||||
|
{
|
||||||
|
None, // 未选择
|
||||||
|
OK, // 确定
|
||||||
|
Yes, // 是
|
||||||
|
No, // 否
|
||||||
|
Cancel // 取消
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -179,213 +344,487 @@ public interface IHandyDialogService : IDialogService {
|
|||||||
|
|
||||||
## 💡 典型使用场景
|
## 💡 典型使用场景
|
||||||
|
|
||||||
### 场景1:操作结果提示
|
### 场景 1:表单保存操作
|
||||||
```csharp
|
|
||||||
public class ProductViewModel : ViewModelBase {
|
|
||||||
private readonly IHandyDialogService _dialog;
|
|
||||||
|
|
||||||
private async Task SaveAsync() {
|
```csharp
|
||||||
|
public class UserEditViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private readonly IHandyDialogService _dialog;
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
|
public UserEditViewModel(IHandyDialogService dialog, IUserService userService)
|
||||||
|
{
|
||||||
|
_dialog = dialog;
|
||||||
|
_userService = userService;
|
||||||
|
SaveCommand = new AsyncCommand(SaveAsync, CanSave);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IAsyncCommand SaveCommand { get; }
|
||||||
|
|
||||||
|
private bool CanSave() => !string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Email);
|
||||||
|
|
||||||
|
private async Task SaveAsync()
|
||||||
|
{
|
||||||
IsBusy = true;
|
IsBusy = true;
|
||||||
|
|
||||||
var result = await _productService.SaveAsync(Product);
|
try
|
||||||
|
{
|
||||||
|
// 验证
|
||||||
|
if (!IsValidEmail(Email))
|
||||||
|
{
|
||||||
|
_dialog.ShowWarning("请输入有效的邮箱地址", "验证失败");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (result.IsSuccess) {
|
// 保存
|
||||||
_dialog.ShowSuccess("保存成功!");
|
var result = await _userService.SaveUserAsync(new User
|
||||||
} else {
|
{
|
||||||
_dialog.ShowError($"保存失败:{result.ErrorMessage}");
|
Name = UserName,
|
||||||
|
Email = Email
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.IsSuccess)
|
||||||
|
{
|
||||||
|
_dialog.ShowSuccess("用户信息保存成功!");
|
||||||
|
|
||||||
|
// 可选:使用 Growl 通知(不阻塞UI)
|
||||||
|
// _dialog.ShowNotification("用户信息已保存", NotificationType.Success);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_dialog.ShowError($"保存失败:{result.ErrorMessage}", "错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_dialog.ShowError($"发生异常:{ex.Message}", "系统错误");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 场景 2:删除确认
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class ProductListViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private readonly IHandyDialogService _dialog;
|
||||||
|
private readonly IRepository<Product> _repository;
|
||||||
|
|
||||||
|
public ProductListViewModel(IHandyDialogService dialog, IRepository<Product> repository)
|
||||||
|
{
|
||||||
|
_dialog = dialog;
|
||||||
|
_repository = repository;
|
||||||
|
DeleteCommand = new AsyncCommand(DeleteAsync, () => SelectedProduct != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Product? SelectedProduct { get; set; }
|
||||||
|
public IAsyncCommand DeleteCommand { get; }
|
||||||
|
|
||||||
|
private async Task DeleteAsync()
|
||||||
|
{
|
||||||
|
if (SelectedProduct == null) return;
|
||||||
|
|
||||||
|
// 显示确认对话框
|
||||||
|
var confirmed = await _dialog.ConfirmAsync(
|
||||||
|
$"确定要删除产品 \"{SelectedProduct.Name}\" 吗?\n\n删除后无法恢复。",
|
||||||
|
"确认删除"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (confirmed)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _repository.RemoveAsync(SelectedProduct.Id);
|
||||||
|
|
||||||
|
// 从列表中移除
|
||||||
|
Products.Remove(SelectedProduct);
|
||||||
|
|
||||||
|
_dialog.ShowSuccess("产品删除成功");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_dialog.ShowError($"删除失败:{ex.Message}", "错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 场景 3:批量操作进度通知
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class BatchProcessViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
private readonly IHandyDialogService _dialog;
|
||||||
|
|
||||||
|
public BatchProcessViewModel(IHandyDialogService dialog)
|
||||||
|
{
|
||||||
|
_dialog = dialog;
|
||||||
|
ProcessCommand = new AsyncCommand(ProcessAsync);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IAsyncCommand ProcessCommand { get; }
|
||||||
|
|
||||||
|
private async Task ProcessAsync()
|
||||||
|
{
|
||||||
|
IsBusy = true;
|
||||||
|
|
||||||
|
var items = GetSelectedItems();
|
||||||
|
int successCount = 0;
|
||||||
|
int failCount = 0;
|
||||||
|
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ProcessItemAsync(item);
|
||||||
|
successCount++;
|
||||||
|
|
||||||
|
// 使用 Growl 显示进度(不阻塞)
|
||||||
|
_dialog.ShowNotification(
|
||||||
|
$"已处理 {successCount}/{items.Count}",
|
||||||
|
NotificationType.Info
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
failCount++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IsBusy = false;
|
IsBusy = false;
|
||||||
|
|
||||||
|
// 显示最终结果
|
||||||
|
if (failCount == 0)
|
||||||
|
{
|
||||||
|
_dialog.ShowSuccess($"批量处理完成!\n成功:{successCount} 项");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_dialog.ShowWarning(
|
||||||
|
$"批量处理完成,但有部分失败\n\n成功:{successCount} 项\n失败:{failCount} 项",
|
||||||
|
"处理完成"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 场景 4:后台任务通知
|
||||||
|
|
||||||
### 场景2:删除确认
|
|
||||||
```csharp
|
```csharp
|
||||||
private async Task DeleteAsync() {
|
public class BackgroundService
|
||||||
var confirmed = await _dialog.ConfirmAsync(
|
{
|
||||||
"确定要删除此项吗?\n删除后无法恢复。",
|
|
||||||
"确认删除"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (confirmed) {
|
|
||||||
await _repository.RemoveAsync(SelectedItem.Id);
|
|
||||||
_dialog.ShowSuccess("删除成功");
|
|
||||||
await RefreshAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 场景3:输入对话框
|
|
||||||
```csharp
|
|
||||||
private async Task RenameAsync() {
|
|
||||||
var result = await _dialog.ShowInputAsync(
|
|
||||||
"请输入新名称:",
|
|
||||||
"重命名",
|
|
||||||
defaultValue: SelectedItem.Name
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result.IsSuccess && !string.IsNullOrEmpty(result.Value)) {
|
|
||||||
SelectedItem.Name = result.Value;
|
|
||||||
await _repository.UpdateAsync(SelectedItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 场景4:等待对话框
|
|
||||||
```csharp
|
|
||||||
private async Task ExportDataAsync() {
|
|
||||||
await _dialog.ShowWaitingAsync("正在导出数据,请稍候...", async () => {
|
|
||||||
// 耗时操作
|
|
||||||
await Task.Delay(3000);
|
|
||||||
var data = await _repository.GetAllAsync();
|
|
||||||
await _excelService.ExportAsync(data, "export.xlsx");
|
|
||||||
});
|
|
||||||
|
|
||||||
_dialog.ShowSuccess("导出完成!");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 场景5:进度对话框
|
|
||||||
```csharp
|
|
||||||
private async Task BatchProcessAsync() {
|
|
||||||
var items = await _repository.GetAllAsync();
|
|
||||||
var progress = _dialog.ShowProgress("正在处理...", "批量处理");
|
|
||||||
|
|
||||||
for (int i = 0; i < items.Count; i++) {
|
|
||||||
await ProcessItemAsync(items[i]);
|
|
||||||
progress.Report((i + 1) * 100.0 / items.Count); // 0-100
|
|
||||||
}
|
|
||||||
|
|
||||||
_dialog.ShowSuccess($"处理完成,共 {items.Count} 项");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 场景6:通知(Growl)
|
|
||||||
```csharp
|
|
||||||
public class NotificationService {
|
|
||||||
private readonly IHandyDialogService _dialog;
|
private readonly IHandyDialogService _dialog;
|
||||||
|
|
||||||
// 右下角弹出通知(不阻塞)
|
public BackgroundService(IHandyDialogService dialog)
|
||||||
public void NotifyNewMessage(string message) {
|
{
|
||||||
_dialog.ShowNotification($"新消息:{message}", NotificationType.Info);
|
_dialog = dialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void NotifyAlarm(string alarmMessage) {
|
public void StartMonitoring()
|
||||||
_dialog.ShowNotification($"报警:{alarmMessage}", NotificationType.Warning);
|
{
|
||||||
|
// 监听PLC报警
|
||||||
|
_plc.OnAlarm += (sender, alarm) =>
|
||||||
|
{
|
||||||
|
_dialog.ShowNotification(
|
||||||
|
$"PLC报警:{alarm.Message}",
|
||||||
|
NotificationType.Warning
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听新消息
|
||||||
|
_messageHub.OnNewMessage += (sender, message) =>
|
||||||
|
{
|
||||||
|
_dialog.ShowNotification(
|
||||||
|
$"新消息:{message.Content}",
|
||||||
|
NotificationType.Info
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听系统错误
|
||||||
|
_errorHandler.OnCriticalError += (sender, error) =>
|
||||||
|
{
|
||||||
|
_dialog.ShowNotification(
|
||||||
|
$"系统错误:{error.Message}",
|
||||||
|
NotificationType.Fatal
|
||||||
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 场景 5:三选项对话框
|
||||||
|
|
||||||
## 🔧 高级功能
|
|
||||||
|
|
||||||
### 1. 主题切换
|
|
||||||
```csharp
|
```csharp
|
||||||
public class ThemeService {
|
private async Task OnFileSaveAsync()
|
||||||
public void SwitchToLightTheme() {
|
{
|
||||||
SharedResourceDictionary.SharedDictionary.Source =
|
if (HasUnsavedChanges)
|
||||||
new Uri("pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml");
|
{
|
||||||
}
|
var result = await _dialog.AskAsync(
|
||||||
|
"文件已修改,是否保存?",
|
||||||
|
"保存确认"
|
||||||
|
);
|
||||||
|
|
||||||
public void SwitchToDarkTheme() {
|
switch (result)
|
||||||
SharedResourceDictionary.SharedDictionary.Source =
|
{
|
||||||
new Uri("pack://application:,,,/HandyControl;component/Themes/SkinDark.xaml");
|
case HandyDialogResult.Yes:
|
||||||
|
await SaveFileAsync();
|
||||||
|
CloseWindow();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HandyDialogResult.No:
|
||||||
|
CloseWindow();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HandyDialogResult.Cancel:
|
||||||
|
// 用户取消,不执行任何操作
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 2. 自定义对话框
|
|
||||||
```csharp
|
|
||||||
public class CustomDialogViewModel : ViewModelBase {
|
|
||||||
public string UserInput { get; set; } = "";
|
|
||||||
|
|
||||||
public ICommand ConfirmCommand { get; }
|
|
||||||
public ICommand CancelCommand { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用
|
|
||||||
var viewModel = new CustomDialogViewModel();
|
|
||||||
var result = await _dialog.ShowDialogAsync(viewModel, "自定义对话框");
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 窗口动画
|
|
||||||
```csharp
|
|
||||||
// HandyControl 提供的窗口动画
|
|
||||||
public class AnimatedWindow : Window {
|
|
||||||
public AnimatedWindow() {
|
|
||||||
WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
|
||||||
|
|
||||||
// 启用淡入淡出动画
|
|
||||||
this.SetValue(ControlBehavior.ShowInTaskManagerProperty, false);
|
|
||||||
this.SetValue(ControlBehavior.ShowInTaskManagerProperty, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📦 包含的 HandyControl 控件扩展
|
|
||||||
|
|
||||||
### 常用控件
|
|
||||||
- **MessageBox** - 样式化消息框
|
|
||||||
- **Growl** - 通知弹窗
|
|
||||||
- **Dialog** - 对话框容器
|
|
||||||
- **LoadingCircle** - 加载动画
|
|
||||||
- **ProgressBar** - 进度条
|
|
||||||
- **Badge** - 徽章
|
|
||||||
- **Tag** - 标签
|
|
||||||
- **Shield** - 盾牌图标
|
|
||||||
- **TextBox** - 增强文本框(带水印、验证)
|
|
||||||
- **DatePicker** - 日期选择器
|
|
||||||
- **TimePicker** - 时间选择器
|
|
||||||
- **ComboBox** - 下拉框
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ⚙️ 配置选项
|
## ⚙️ 配置选项
|
||||||
|
|
||||||
|
### 完整配置示例
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
services.AddHandyControlServices(options => {
|
services.AddSharpHandyControl(options =>
|
||||||
// 默认对话框选项
|
{
|
||||||
options.DefaultDialogWidth = 400;
|
// 是否自动注册主题资源(默认:true)
|
||||||
options.DefaultDialogHeight = 200;
|
// 如果为 false,需要在 App.xaml 中手动添加主题资源
|
||||||
|
options.AutoRegisterTheme = true;
|
||||||
|
|
||||||
// Growl 配置
|
// 主题类型(默认:Light 浅色主题)
|
||||||
options.GrowlToken = "MainGrowl"; // Growl容器名称
|
options.Theme = HandyControlTheme.Light; // 或 HandyControlTheme.Dark
|
||||||
options.GrowlPosition = GrowlPosition.BottomRight;
|
|
||||||
|
|
||||||
// 主题配置
|
// 默认 Growl 容器标识(默认:"Default")
|
||||||
options.DefaultTheme = Theme.Light;
|
// 对应 XAML 中 <hc:Growl Token="Default" />
|
||||||
|
options.DefaultGrowlToken = "Default";
|
||||||
|
|
||||||
|
// Growl 通知显示时长(秒)(默认:3)
|
||||||
|
options.GrowlWaitTime = 3;
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 使用深色主题
|
||||||
|
|
||||||
## 🎨 样式定制
|
```csharp
|
||||||
|
// 方式 1:全局深色主题
|
||||||
|
services.AddSharpHandyControl(options =>
|
||||||
|
{
|
||||||
|
options.Theme = HandyControlTheme.Dark;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 方式 2:根据系统主题动态选择
|
||||||
|
services.AddSharpHandyControl(options =>
|
||||||
|
{
|
||||||
|
var isDarkMode = IsSystemDarkMode();
|
||||||
|
options.Theme = isDarkMode ? HandyControlTheme.Dark : HandyControlTheme.Light;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 禁用自动主题注册
|
||||||
|
|
||||||
|
如果你想完全控制主题配置(例如使用自定义主题),可以禁用自动注册:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
services.AddSharpHandyControl(options =>
|
||||||
|
{
|
||||||
|
options.AutoRegisterTheme = false; // 禁用自动注册
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
然后在 `App.xaml` 中手动配置:
|
||||||
|
|
||||||
### 自定义主题颜色
|
|
||||||
```xml
|
```xml
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
<hc:ThemeResources AccentColor="#FF0078D7"/>
|
<!-- 自定义主题 -->
|
||||||
<hc:Theme/>
|
<ResourceDictionary Source="Themes/MyCustomTheme.xaml" />
|
||||||
|
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml" />
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Application.Resources>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 多窗口 Growl 配置
|
||||||
|
|
||||||
|
如果你有多个窗口,可以为每个窗口配置独立的 Growl 容器:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 主窗口使用默认 Token
|
||||||
|
_dialog.ShowNotification("主窗口通知", NotificationType.Info);
|
||||||
|
|
||||||
|
// 子窗口使用自定义 Token
|
||||||
|
_dialog.ShowNotification("子窗口通知", NotificationType.Info, token: "ChildWindow");
|
||||||
|
```
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- MainWindow.xaml -->
|
||||||
|
<hc:Growl Token="Default" ... />
|
||||||
|
|
||||||
|
<!-- ChildWindow.xaml -->
|
||||||
|
<hc:Growl Token="ChildWindow" ... />
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 Growl 通知配置
|
||||||
|
|
||||||
|
### 基本配置
|
||||||
|
|
||||||
|
Growl 是 HandyControl 的特色功能,显示在窗口右上角的优雅通知。
|
||||||
|
|
||||||
|
#### 1. 在窗口中添加 Growl 容器
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<Window x:Class="MyApp.MainWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||||
|
Title="主窗口" Height="600" Width="800">
|
||||||
|
<Grid>
|
||||||
|
<!-- 你的窗口内容 -->
|
||||||
|
<DockPanel>
|
||||||
|
<Menu DockPanel.Dock="Top">...</Menu>
|
||||||
|
<ContentControl>...</ContentControl>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<!-- Growl 容器 - 必须放在最顶层,ZIndex 最高 -->
|
||||||
|
<hc:Growl Token="Default"
|
||||||
|
Panel.ZIndex="999"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Margin="0,10,10,0"/>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. 在代码中使用 Growl
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 使用默认 Token
|
||||||
|
_dialog.ShowNotification("这是一条通知", NotificationType.Info);
|
||||||
|
_dialog.ShowNotification("保存成功", NotificationType.Success);
|
||||||
|
_dialog.ShowNotification("磁盘空间不足", NotificationType.Warning);
|
||||||
|
_dialog.ShowNotification("连接失败", NotificationType.Error);
|
||||||
|
_dialog.ShowNotification("系统崩溃", NotificationType.Fatal);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 高级配置
|
||||||
|
|
||||||
|
#### 自定义 Growl 位置
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- 左上角 -->
|
||||||
|
<hc:Growl Token="TopLeft"
|
||||||
|
Panel.ZIndex="999"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Margin="10,10,0,0"/>
|
||||||
|
|
||||||
|
<!-- 底部居中 -->
|
||||||
|
<hc:Growl Token="BottomCenter"
|
||||||
|
Panel.ZIndex="999"
|
||||||
|
VerticalAlignment="Bottom"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Margin="0,0,0,10"/>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 全局 Growl(所有窗口共享)
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- App.xaml -->
|
||||||
|
<Application.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<!-- 全局 Growl 窗口 -->
|
||||||
|
<hc:GrowlWindow x:Key="GlobalGrowlWindow" />
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Application.Resources>
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 使用全局 Growl(无需在每个窗口添加容器)
|
||||||
|
Growl.InfoGlobal("这是全局通知");
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 主题定制
|
||||||
|
|
||||||
|
### 运行时切换主题
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class SettingsViewModel : ViewModelBase
|
||||||
|
{
|
||||||
|
public void SwitchTheme(bool isDark)
|
||||||
|
{
|
||||||
|
// 清除旧主题
|
||||||
|
var oldTheme = Application.Current.Resources.MergedDictionaries
|
||||||
|
.FirstOrDefault(d => d.Source?.OriginalString.Contains("HandyControl") == true);
|
||||||
|
|
||||||
|
if (oldTheme != null)
|
||||||
|
{
|
||||||
|
Application.Current.Resources.MergedDictionaries.Remove(oldTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载新主题
|
||||||
|
var skinUri = isDark
|
||||||
|
? "pack://application:,,,/HandyControl;component/Themes/SkinDark.xaml"
|
||||||
|
: "pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml";
|
||||||
|
|
||||||
|
Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary
|
||||||
|
{
|
||||||
|
Source = new Uri(skinUri)
|
||||||
|
});
|
||||||
|
|
||||||
|
_dialog.ShowSuccess("主题已切换");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 自定义主题颜色
|
||||||
|
|
||||||
|
HandyControl 支持自定义主题颜色,创建 `CustomTheme.xaml`:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<!-- 覆盖默认颜色 -->
|
||||||
|
<SolidColorBrush x:Key="PrimaryBrush" Color="#FF0078D7"/>
|
||||||
|
<SolidColorBrush x:Key="DangerBrush" Color="#FFE74856"/>
|
||||||
|
<SolidColorBrush x:Key="WarningBrush" Color="#FFFFA500"/>
|
||||||
|
<SolidColorBrush x:Key="SuccessBrush" Color="#FF13A10E"/>
|
||||||
|
</ResourceDictionary>
|
||||||
|
```
|
||||||
|
|
||||||
|
然后禁用自动主题并手动加载:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
services.AddSharpHandyControl(options =>
|
||||||
|
{
|
||||||
|
options.AutoRegisterTheme = false;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- App.xaml -->
|
||||||
|
<Application.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/>
|
||||||
|
<ResourceDictionary Source="Themes/CustomTheme.xaml"/>
|
||||||
|
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/>
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
@@ -393,43 +832,469 @@ services.AddHandyControlServices(options => {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📝 注意事项
|
## 📝 迁移指南
|
||||||
|
|
||||||
1. **HandyControl 版本**:当前支持 HandyControl 3.5.0
|
### 从直接使用 HandyControl 迁移
|
||||||
2. **依赖 Sharp.Wpf**:必须先引用 Sharp.Wpf 模块
|
|
||||||
3. **主题资源**:需要在 App.xaml 中引入 HandyControl 资源字典
|
#### 步骤 1:添加服务注册
|
||||||
4. **Growl容器**:需要在主窗口添加 Growl 容器才能使用通知功能
|
|
||||||
|
```csharp
|
||||||
|
// App.xaml.cs
|
||||||
|
protected override void OnStartup(StartupEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnStartup(e);
|
||||||
|
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
|
||||||
|
// 一行代码完成服务注册和主题配置
|
||||||
|
services.AddSharpHandyControl();
|
||||||
|
|
||||||
|
// 注册其他服务...
|
||||||
|
services.AddTransient<MainViewModel>();
|
||||||
|
services.AddTransient<MainWindow>();
|
||||||
|
|
||||||
|
var provider = services.BuildServiceProvider();
|
||||||
|
|
||||||
|
// 显示主窗口
|
||||||
|
var mainWindow = provider.GetRequiredService<MainWindow>();
|
||||||
|
mainWindow.Show();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 步骤 2:删除 App.xaml 中的手动主题引用
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- 之前:需要手动添加主题资源 -->
|
||||||
|
<Application.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml" />
|
||||||
|
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml" />
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Application.Resources>
|
||||||
|
|
||||||
|
<!-- 之后:无需手动添加,AddSharpHandyControl() 会自动注册 -->
|
||||||
|
<Application.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<!-- 可以添加你自己的资源 -->
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Application.Resources>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 步骤 3:修改 Window 和 ViewModel 使用依赖注入
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 之前:直接 new,无法注入服务
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
public MainWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = new MainViewModel(); // ❌ 无法注入服务
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Button_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// ❌ 静态调用,难以测试
|
||||||
|
HandyControl.Controls.MessageBox.Show("消息", "标题");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 之后:通过构造函数注入
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
private readonly IHandyDialogService _dialog;
|
||||||
|
|
||||||
|
public MainWindow(MainViewModel viewModel, IHandyDialogService dialog)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel; // ✅ ViewModel 由 DI 容器创建
|
||||||
|
_dialog = dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Button_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// ✅ 使用注入的服务,可测试
|
||||||
|
_dialog.ShowInfo("消息", "标题");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 步骤 4:替换 MessageBox 静态调用
|
||||||
|
|
||||||
|
| 之前(静态调用) | 之后(依赖注入) |
|
||||||
|
|---|---|
|
||||||
|
| `HandyControl.Controls.MessageBox.Show("消息", "标题")` | `_dialog.ShowInfo("消息", "标题")` |
|
||||||
|
| `HandyControl.Controls.MessageBox.Success("成功")` | `_dialog.ShowSuccess("成功")` |
|
||||||
|
| `HandyControl.Controls.MessageBox.Error("错误")` | `_dialog.ShowError("错误")` |
|
||||||
|
| `HandyControl.Controls.MessageBox.Warning("警告")` | `_dialog.ShowWarning("警告")` |
|
||||||
|
| `var result = HandyControl.Controls.MessageBox.Ask("确认吗?")` | `var confirmed = await _dialog.ConfirmAsync("确认吗?")` |
|
||||||
|
| `Growl.Info("通知")` | `_dialog.ShowNotification("通知", NotificationType.Info)` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔄 与其他UI库对比
|
## ❓ 常见问题
|
||||||
|
|
||||||
| UI库 | 特点 | 适用场景 |
|
### Q1: 为什么调用 `ShowNotification` 后没有显示 Growl 通知?
|
||||||
|-----|------|---------|
|
|
||||||
| **HandyControl** | 现代化设计、控件丰富 | 企业级应用、工具软件 |
|
**A:** 需要在窗口 XAML 中添加 `<hc:Growl>` 容器:
|
||||||
| **MaterialDesign** | Material风格 | 现代化Web风格应用 |
|
|
||||||
| **ModernWPF** | Windows 11风格 | 跟随Windows系统风格 |
|
```xml
|
||||||
| **原生WPF** | 轻量级 | 简单应用、追求性能 |
|
<Window>
|
||||||
|
<Grid>
|
||||||
|
<!-- 你的内容 -->
|
||||||
|
|
||||||
|
<!-- 添加这个 -->
|
||||||
|
<hc:Growl Token="Default" Panel.ZIndex="999"
|
||||||
|
VerticalAlignment="Top" HorizontalAlignment="Right"
|
||||||
|
Margin="0,10,10,0"/>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
|
```
|
||||||
|
|
||||||
|
确保:
|
||||||
|
1. `Token` 与代码中使用的一致(默认是 "Default")
|
||||||
|
2. `Panel.ZIndex` 足够大,确保在最顶层
|
||||||
|
3. 容器位于 `Grid` 或其他容器的最后(XAML 顺序)
|
||||||
|
|
||||||
|
### Q2: 如何在单元测试中模拟 `IHandyDialogService`?
|
||||||
|
|
||||||
|
**A:** 使用 Mock 库(如 Moq):
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
[TestClass]
|
||||||
|
public class MainViewModelTests
|
||||||
|
{
|
||||||
|
[TestMethod]
|
||||||
|
public async Task SaveCommand_ShowsSuccessMessage_WhenSaveSucceeds()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mockDialog = new Mock<IHandyDialogService>();
|
||||||
|
var mockService = new Mock<IUserService>();
|
||||||
|
|
||||||
|
mockService.Setup(s => s.SaveUserAsync(It.IsAny<User>()))
|
||||||
|
.ReturnsAsync(Result.Success());
|
||||||
|
|
||||||
|
var viewModel = new MainViewModel(mockDialog.Object, mockService.Object);
|
||||||
|
viewModel.UserName = "Test";
|
||||||
|
viewModel.Email = "test@test.com";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await viewModel.SaveCommand.ExecuteAsync(null);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
mockDialog.Verify(d => d.ShowSuccess(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q3: 可以在非 UI 线程调用对话框服务吗?
|
||||||
|
|
||||||
|
**A:** 可以。`IHandyDialogService` 内部使用 `Application.Current.Dispatcher.Invoke()` 自动切换到 UI 线程,你可以在任何线程安全地调用:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
// 在后台线程执行耗时操作
|
||||||
|
ProcessLargeFile();
|
||||||
|
|
||||||
|
// 自动切换到 UI 线程显示对话框
|
||||||
|
_dialog.ShowSuccess("处理完成!");
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q4: 如何同时支持 HandyControl 和其他 UI 库?
|
||||||
|
|
||||||
|
**A:** 使用基础接口 `IDialogService`:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ViewModel 只依赖基础接口
|
||||||
|
public class MyViewModel
|
||||||
|
{
|
||||||
|
private readonly IDialogService _dialog; // ← 不是 IHandyDialogService
|
||||||
|
|
||||||
|
public MyViewModel(IDialogService dialog)
|
||||||
|
{
|
||||||
|
_dialog = dialog;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置时选择实现
|
||||||
|
// 方案 A:使用 HandyControl
|
||||||
|
services.AddSharpHandyControl();
|
||||||
|
|
||||||
|
// 方案 B:使用其他实现
|
||||||
|
services.AddSingleton<IDialogService, MyCustomDialogService>();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q5: `ShowInputAsync` 为什么返回失败?
|
||||||
|
|
||||||
|
**A:** `ShowInputAsync` 功能尚未实现(需要自定义对话框窗口),当前版本会返回:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
Result.Failure<string>("输入对话框功能尚未实现,需要自定义对话框窗口")
|
||||||
|
```
|
||||||
|
|
||||||
|
如果需要输入功能,可以临时使用:
|
||||||
|
- 创建自定义 Window 进行输入
|
||||||
|
- 使用 HandyControl 的其他输入控件
|
||||||
|
- 等待后续版本实现
|
||||||
|
|
||||||
|
### Q6: 能否在 WinForms 项目中使用?
|
||||||
|
|
||||||
|
**A:** 不能。本模块基于 WPF 的 `System.Windows.Application`,仅支持 WPF 项目。如果是 WinForms 项目,建议使用 WinForms 原生对话框或迁移到 WPF。
|
||||||
|
|
||||||
|
### Q7: 主题切换后某些控件样式不生效?
|
||||||
|
|
||||||
|
**A:** 确保控件使用了 HandyControl 的样式:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<!-- 使用 HandyControl 样式的按钮 -->
|
||||||
|
<Button Style="{StaticResource ButtonPrimary}" Content="确定"/>
|
||||||
|
|
||||||
|
<!-- 如果使用原生 Button,不会应用 HandyControl 主题 -->
|
||||||
|
<Button Content="确定"/> <!-- ❌ 不会有 HandyControl 样式 -->
|
||||||
|
```
|
||||||
|
|
||||||
|
查看 [HandyControl 文档](https://handyorg.github.io/) 了解所有可用样式。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 未来扩展
|
## 🏆 最佳实践
|
||||||
|
|
||||||
### 可能的扩展方向
|
### 1. 统一使用依赖注入
|
||||||
1. **Sharp.Wpf.MaterialDesign** - MaterialDesign UI库集成
|
|
||||||
2. **Sharp.Wpf.ModernWPF** - ModernWPF UI库集成
|
**✅ 推荐:**
|
||||||
3. **自定义控件库** - 基于 HandyControl 的业务控件
|
```csharp
|
||||||
|
public class MyViewModel
|
||||||
|
{
|
||||||
|
private readonly IHandyDialogService _dialog;
|
||||||
|
|
||||||
|
public MyViewModel(IHandyDialogService dialog)
|
||||||
|
{
|
||||||
|
_dialog = dialog;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**❌ 不推荐:**
|
||||||
|
```csharp
|
||||||
|
public class MyViewModel
|
||||||
|
{
|
||||||
|
public void ShowMessage()
|
||||||
|
{
|
||||||
|
// 静态调用,无法测试
|
||||||
|
HandyControl.Controls.MessageBox.Show("消息");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 优先使用 Growl 通知代替阻塞式消息框
|
||||||
|
|
||||||
|
对于非关键操作,使用 Growl 通知提升用户体验:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ✅ 推荐:后台保存使用 Growl
|
||||||
|
await _service.AutoSaveAsync();
|
||||||
|
_dialog.ShowNotification("已自动保存", NotificationType.Success);
|
||||||
|
|
||||||
|
// ❌ 不推荐:后台操作阻塞 UI
|
||||||
|
await _service.AutoSaveAsync();
|
||||||
|
_dialog.ShowSuccess("已自动保存"); // 用户必须点击"确定"才能继续
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 为异常处理提供友好提示
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _service.ProcessAsync();
|
||||||
|
}
|
||||||
|
catch (NetworkException ex)
|
||||||
|
{
|
||||||
|
_dialog.ShowError("网络连接失败,请检查网络设置", "网络错误");
|
||||||
|
}
|
||||||
|
catch (UnauthorizedException ex)
|
||||||
|
{
|
||||||
|
_dialog.ShowError("您没有权限执行此操作", "权限不足");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_dialog.ShowError($"发生未知错误:{ex.Message}\n\n请联系技术支持。", "系统错误");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 使用 IDialogService 接口提高兼容性
|
||||||
|
|
||||||
|
如果模块可能被其他项目使用,优先依赖基础接口:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ✅ 兼容性好
|
||||||
|
public MyLibrary(IDialogService dialog) { }
|
||||||
|
|
||||||
|
// ❌ 绑定到具体实现
|
||||||
|
public MyLibrary(IHandyDialogService dialog) { }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 为长时间操作提供进度反馈
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public async Task ProcessLargeFileAsync()
|
||||||
|
{
|
||||||
|
IsBusy = true;
|
||||||
|
var items = LoadItems();
|
||||||
|
|
||||||
|
for (int i = 0; i < items.Count; i++)
|
||||||
|
{
|
||||||
|
await ProcessItemAsync(items[i]);
|
||||||
|
|
||||||
|
// 每 10 个显示一次进度
|
||||||
|
if ((i + 1) % 10 == 0)
|
||||||
|
{
|
||||||
|
_dialog.ShowNotification(
|
||||||
|
$"已处理 {i + 1}/{items.Count}",
|
||||||
|
NotificationType.Info
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IsBusy = false;
|
||||||
|
_dialog.ShowSuccess("处理完成!");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. 配置全局异常处理
|
||||||
|
|
||||||
|
在 `App.xaml.cs` 中捕获未处理异常:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
protected override void OnStartup(StartupEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnStartup(e);
|
||||||
|
|
||||||
|
// 配置全局异常处理
|
||||||
|
DispatcherUnhandledException += (s, args) =>
|
||||||
|
{
|
||||||
|
var dialog = _serviceProvider.GetService<IHandyDialogService>();
|
||||||
|
dialog?.ShowError($"程序发生异常:{args.Exception.Message}", "未处理的异常");
|
||||||
|
args.Handled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ... DI 配置
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📖 参考资料
|
## 📖 API 参考
|
||||||
|
|
||||||
- [HandyControl GitHub](https://github.com/HandyOrg/HandyControl)
|
### 消息框方法
|
||||||
- [HandyControl 官方文档](https://handyorg.github.io/)
|
|
||||||
- [HandyControl Demo](https://github.com/HandyOrg/HandyControl/releases)
|
| 方法 | 说明 | 图标颜色 | 阻塞 |
|
||||||
|
|------|------|----------|------|
|
||||||
|
| `ShowSuccess(message, title)` | 显示成功消息 | 绿色 | 是 |
|
||||||
|
| `ShowInfo(message, title)` | 显示信息消息 | 蓝色 | 是 |
|
||||||
|
| `ShowWarning(message, title)` | 显示警告消息 | 橙色 | 是 |
|
||||||
|
| `ShowError(message, title)` | 显示错误消息 | 红色 | 是 |
|
||||||
|
|
||||||
|
### 对话框方法
|
||||||
|
|
||||||
|
| 方法 | 说明 | 返回值 | 按钮 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| `ConfirmAsync(message, title)` | 确认对话框 | `Task<bool>` | 确定/取消 |
|
||||||
|
| `AskAsync(message, title)` | 询问对话框 | `Task<HandyDialogResult>` | 是/否/取消 |
|
||||||
|
| `ShowInputAsync(message, title, defaultValue)` | 输入对话框(未实现) | `Task<Result<string>>` | 确定/取消 |
|
||||||
|
|
||||||
|
### Growl 通知方法
|
||||||
|
|
||||||
|
| 方法 | 说明 | 阻塞 |
|
||||||
|
|------|------|------|
|
||||||
|
| `ShowNotification(message, type, token)` | 显示 Growl 通知 | 否 |
|
||||||
|
|
||||||
|
**NotificationType 类型:**
|
||||||
|
- `Info` - 信息(蓝色)
|
||||||
|
- `Success` - 成功(绿色)
|
||||||
|
- `Warning` - 警告(橙色)
|
||||||
|
- `Error` - 错误(红色)
|
||||||
|
- `Fatal` - 严重错误(深红色)
|
||||||
|
|
||||||
|
### IDialogService 基础接口方法
|
||||||
|
|
||||||
|
继承自 `Sharp.Wpf` 的基础接口:
|
||||||
|
|
||||||
|
| 方法 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `ShowInfoAsync(message, title)` | 显示信息对话框 |
|
||||||
|
| `ShowWarningAsync(message, title)` | 显示警告对话框 |
|
||||||
|
| `ShowErrorAsync(message, title)` | 显示错误对话框 |
|
||||||
|
| `ShowConfirmAsync(message, title)` | 显示确认对话框 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**状态**:规划阶段,未实现
|
## 🔄 与其他 UI 库对比
|
||||||
**依赖**:Sharp.Wpf, HandyControl 3.5.0
|
|
||||||
**实现难度**:低(封装现有API)
|
| UI库 | 特点 | 许可证 | 适用场景 |
|
||||||
|
|-----|------|--------|---------|
|
||||||
|
| **HandyControl** | 现代化设计、控件丰富、中文友好 | MIT | 企业级应用、工具软件、内部系统 |
|
||||||
|
| **MaterialDesign** | Material Design 风格、动画流畅 | MIT | 现代化 Web 风格应用、跨平台应用 |
|
||||||
|
| **ModernWPF** | Windows 11 Fluent Design 风格 | MIT | 跟随 Windows 系统风格的应用 |
|
||||||
|
| **MahApps.Metro** | Metro 风格、轻量级 | MIT | 简洁风格的桌面应用 |
|
||||||
|
| **原生 WPF** | 系统自带、无额外依赖 | - | 追求性能、简单应用 |
|
||||||
|
|
||||||
|
**为什么选择 HandyControl?**
|
||||||
|
- ✅ 控件库最全面(80+ 控件)
|
||||||
|
- ✅ 中文社区活跃,文档完善
|
||||||
|
- ✅ 开箱即用,样式统一
|
||||||
|
- ✅ 性能好,不臃肿
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 参考资料
|
||||||
|
|
||||||
|
### 官方资源
|
||||||
|
|
||||||
|
- [HandyControl GitHub](https://github.com/HandyOrg/HandyControl) - 源代码仓库
|
||||||
|
- [HandyControl 官方文档](https://handyorg.github.io/) - 完整文档和示例
|
||||||
|
- [HandyControl Demo 下载](https://github.com/HandyOrg/HandyControl/releases) - 可运行的示例程序
|
||||||
|
|
||||||
|
### Sharp.Wpf 相关
|
||||||
|
|
||||||
|
- [Sharp.Wpf 模块文档](../Wpf/README.md) - 核心 MVVM 框架
|
||||||
|
- [Sharp.Common 模块文档](../Common/README.md) - Result<T> 等基础设施
|
||||||
|
|
||||||
|
### 社区资源
|
||||||
|
|
||||||
|
- [HandyControl Gitee 镜像](https://gitee.com/handyorg/HandyControl) - 国内访问更快
|
||||||
|
- [HandyControl QQ 群](https://github.com/HandyOrg/HandyControl#%E4%BA%A4%E6%B5%81) - 技术交流
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 模块信息
|
||||||
|
|
||||||
|
- **状态**:✅ 可用(生产就绪)
|
||||||
|
- **版本**:1.0.0
|
||||||
|
- **目标框架**:.NET 8.0 Windows
|
||||||
|
- **依赖**:
|
||||||
|
- Sharp.Wpf (MVVM 基础设施)
|
||||||
|
- Sharp.Common (Result<T> 等)
|
||||||
|
- HandyControl 3.5.0 (UI 库)
|
||||||
|
- Microsoft.Extensions.DependencyInjection (依赖注入)
|
||||||
|
- **许可证**:GNU AGPLv3
|
||||||
|
- **实现难度**:低(封装现有 API)
|
||||||
|
- **维护状态**:活跃维护
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🤝 贡献与反馈
|
||||||
|
|
||||||
|
如果你有任何问题、建议或 Bug 报告,欢迎:
|
||||||
|
- 提交 Issue
|
||||||
|
- 提交 Pull Request
|
||||||
|
- 加入技术交流群讨论
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Made with ❤️ by Sharp Team**
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using Common.Results;
|
||||||
|
using HandyControl.Controls;
|
||||||
|
using HandyControl.Data;
|
||||||
|
using Sharp.Wpf.HandyControl.Options;
|
||||||
|
using Sharp.Wpf.Services;
|
||||||
|
using MessageBox = HandyControl.Controls.MessageBox;
|
||||||
|
|
||||||
|
namespace Sharp.Wpf.HandyControl.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HandyControl 对话框服务实现
|
||||||
|
/// </summary>
|
||||||
|
public class HandyDialogService : IHandyDialogService
|
||||||
|
{
|
||||||
|
private readonly HandyControlOptions _options;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public HandyDialogService(HandyControlOptions options)
|
||||||
|
{
|
||||||
|
_options = options;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 显示成功消息
|
||||||
|
/// </summary>
|
||||||
|
public void ShowSuccess(string message, string? title = null)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
MessageBox.Success(message, title ?? "成功");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示信息消息
|
||||||
|
/// </summary>
|
||||||
|
public void ShowInfo(string message, string? title = null)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
MessageBox.Info(message, title ?? "信息");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示警告消息
|
||||||
|
/// </summary>
|
||||||
|
public void ShowWarning(string message, string? title = null)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
MessageBox.Warning(message, title ?? "警告");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示错误消息
|
||||||
|
/// </summary>
|
||||||
|
public void ShowError(string message, string? title = null)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
MessageBox.Error(message, title ?? "错误");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示确认对话框
|
||||||
|
/// </summary>
|
||||||
|
public Task<bool> ConfirmAsync(string message, string? title = null)
|
||||||
|
{
|
||||||
|
return Task.Run(() =>
|
||||||
|
{
|
||||||
|
var result = Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
return MessageBox.Ask(message, title ?? "确认");
|
||||||
|
});
|
||||||
|
|
||||||
|
return result == MessageBoxResult.OK;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示询问对话框(是/否/取消)
|
||||||
|
/// </summary>
|
||||||
|
public Task<HandyDialogResult> AskAsync(string message, string? title = null)
|
||||||
|
{
|
||||||
|
return Task.Run(() =>
|
||||||
|
{
|
||||||
|
var result = Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
return MessageBox.Show(message, title ?? "询问", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result switch
|
||||||
|
{
|
||||||
|
MessageBoxResult.Yes => HandyDialogResult.Yes,
|
||||||
|
MessageBoxResult.No => HandyDialogResult.No,
|
||||||
|
MessageBoxResult.OK => HandyDialogResult.OK,
|
||||||
|
MessageBoxResult.Cancel => HandyDialogResult.Cancel,
|
||||||
|
_ => HandyDialogResult.None
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示输入对话框(尚未实现)
|
||||||
|
/// </summary>
|
||||||
|
public Task<Result<string>> ShowInputAsync(string message, string? title = null, string? defaultValue = null)
|
||||||
|
{
|
||||||
|
// TODO: 需要自定义 TextDialog 窗口来实现此功能
|
||||||
|
// 当前版本暂不支持,返回失败结果
|
||||||
|
return Task.FromResult(Result.Failure<string>("输入对话框功能尚未实现,需要自定义对话框窗口"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示通知(Growl)
|
||||||
|
/// </summary>
|
||||||
|
public void ShowNotification(string message, NotificationType type = NotificationType.Info, string? token = null)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(() =>
|
||||||
|
{
|
||||||
|
var info = new GrowlInfo
|
||||||
|
{
|
||||||
|
Message = message,
|
||||||
|
WaitTime = _options.GrowlWaitTime,
|
||||||
|
Token = token ?? _options.DefaultGrowlToken
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case NotificationType.Info:
|
||||||
|
Growl.Info(info);
|
||||||
|
break;
|
||||||
|
case NotificationType.Success:
|
||||||
|
Growl.Success(info);
|
||||||
|
break;
|
||||||
|
case NotificationType.Warning:
|
||||||
|
Growl.Warning(info);
|
||||||
|
break;
|
||||||
|
case NotificationType.Error:
|
||||||
|
Growl.Error(info);
|
||||||
|
break;
|
||||||
|
case NotificationType.Fatal:
|
||||||
|
Growl.Fatal(info);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDialogService 实现(兼容基础接口)
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示信息对话框(IDialogService 实现)
|
||||||
|
/// </summary>
|
||||||
|
public Task ShowInfoAsync(string message, string? title = null)
|
||||||
|
{
|
||||||
|
ShowInfo(message, title);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示警告对话框(IDialogService 实现)
|
||||||
|
/// </summary>
|
||||||
|
public Task ShowWarningAsync(string message, string? title = null)
|
||||||
|
{
|
||||||
|
ShowWarning(message, title);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示错误对话框(IDialogService 实现)
|
||||||
|
/// </summary>
|
||||||
|
public Task ShowErrorAsync(string message, string? title = null)
|
||||||
|
{
|
||||||
|
ShowError(message, title);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示确认对话框(IDialogService 实现)
|
||||||
|
/// </summary>
|
||||||
|
public Task<bool> ShowConfirmAsync(string message, string? title = null)
|
||||||
|
{
|
||||||
|
return ConfirmAsync(message, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
using Common.Results;
|
||||||
|
using Sharp.Wpf.Services;
|
||||||
|
|
||||||
|
namespace Sharp.Wpf.HandyControl.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HandyControl 对话框服务接口
|
||||||
|
/// 扩展自 IDialogService,提供 HandyControl 特有的对话框功能
|
||||||
|
/// </summary>
|
||||||
|
public interface IHandyDialogService : IDialogService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 显示成功消息(绿色,带勾图标)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">消息内容</param>
|
||||||
|
/// <param name="title">标题,默认为"成功"</param>
|
||||||
|
void ShowSuccess(string message, string? title = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示信息消息(蓝色,带i图标)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">消息内容</param>
|
||||||
|
/// <param name="title">标题,默认为"信息"</param>
|
||||||
|
void ShowInfo(string message, string? title = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示警告消息(橙色,带!图标)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">消息内容</param>
|
||||||
|
/// <param name="title">标题,默认为"警告"</param>
|
||||||
|
void ShowWarning(string message, string? title = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示错误消息(红色,带X图标)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">消息内容</param>
|
||||||
|
/// <param name="title">标题,默认为"错误"</param>
|
||||||
|
void ShowError(string message, string? title = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示确认对话框
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">消息内容</param>
|
||||||
|
/// <param name="title">标题,默认为"确认"</param>
|
||||||
|
/// <returns>用户是否确认</returns>
|
||||||
|
Task<bool> ConfirmAsync(string message, string? title = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示询问对话框(是/否/取消)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">消息内容</param>
|
||||||
|
/// <param name="title">标题</param>
|
||||||
|
/// <returns>对话框结果</returns>
|
||||||
|
Task<HandyDialogResult> AskAsync(string message, string? title = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示输入对话框
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">提示信息</param>
|
||||||
|
/// <param name="title">标题</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>用户输入的结果</returns>
|
||||||
|
Task<Result<string>> ShowInputAsync(
|
||||||
|
string message,
|
||||||
|
string? title = null,
|
||||||
|
string? defaultValue = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示通知(Growl,右下角弹出)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">通知内容</param>
|
||||||
|
/// <param name="type">通知类型</param>
|
||||||
|
/// <param name="token">Growl容器标识</param>
|
||||||
|
void ShowNotification(
|
||||||
|
string message,
|
||||||
|
NotificationType type = NotificationType.Info,
|
||||||
|
string? token = null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对话框结果
|
||||||
|
/// </summary>
|
||||||
|
public enum HandyDialogResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 未知
|
||||||
|
/// </summary>
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 确定/是
|
||||||
|
/// </summary>
|
||||||
|
OK = 1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 取消/否
|
||||||
|
/// </summary>
|
||||||
|
Cancel = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是
|
||||||
|
/// </summary>
|
||||||
|
Yes = 6,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 否
|
||||||
|
/// </summary>
|
||||||
|
No = 7
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通知类型
|
||||||
|
/// </summary>
|
||||||
|
public enum NotificationType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 信息(蓝色)
|
||||||
|
/// </summary>
|
||||||
|
Info,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 成功(绿色)
|
||||||
|
/// </summary>
|
||||||
|
Success,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 警告(橙色)
|
||||||
|
/// </summary>
|
||||||
|
Warning,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 错误(红色)
|
||||||
|
/// </summary>
|
||||||
|
Error,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 严重错误(深红色)
|
||||||
|
/// </summary>
|
||||||
|
Fatal
|
||||||
|
}
|
||||||
@@ -15,6 +15,11 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Common\Common.csproj" />
|
||||||
<ProjectReference Include="..\Wpf\Wpf.csproj" />
|
<ProjectReference Include="..\Wpf\Wpf.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user