From 47b4d3837bd2c3f337e3998fed8acbdf112c9e4c Mon Sep 17 00:00:00 2001 From: hsc Date: Thu, 27 Aug 2026 14:26:22 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=B9=E7=AA=97=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ADP/App.xaml.cs | 54 ++++++++++++++++ ADP/ViewModels/Dialogs/MessageBoxViewModel.cs | 23 +++++++ Command/CommandDialog.cs | 62 +++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 Command/CommandDialog.cs diff --git a/ADP/App.xaml.cs b/ADP/App.xaml.cs index bc61c49..6537b9d 100644 --- a/ADP/App.xaml.cs +++ b/ADP/App.xaml.cs @@ -1,4 +1,5 @@ using Common; +using Command; using ADP.ViewModels; using ADP.ViewModels.Dialogs; using ADP.Views; @@ -12,6 +13,8 @@ using Service.Interface; using System.Configuration; using System.Data; using System.Reflection; +using System.Threading; +using System.Threading.Tasks; using System.Windows; using UIShare.PubEvent; using static System.Runtime.InteropServices.JavaScript.JSType; @@ -23,6 +26,7 @@ using DeviceCommand.Base; using AutoMapper; using Microsoft.Extensions.Logging.Abstractions; using ADP.Profiles; +using Prism.Dialogs; using 加密狗; namespace ADP @@ -60,6 +64,56 @@ namespace ADP } protected override void OnInitialized() { + // 向命令库 CommandDialog 注入弹窗能力(命令库是纯 .NET 类库,不引用 WPF,这里通过委托实现依赖倒置) + var dialogService = Container.Resolve(); + CommandDialog.弹窗处理器 = (弹窗类型, 弹窗详细, 是否阻塞, 自动关闭秒数, ct) => + { + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + CancellationTokenRegistration registration = default; + if (是否阻塞) + { + registration = ct.Register(() => tcs.TrySetCanceled(ct)); + } + // 命令可能在后台线程执行,统一切回 UI 线程弹窗 + Application.Current.Dispatcher.Invoke(() => + { + var param = new DialogParameters + { + { "Title", 弹窗类型 switch + { + CommandDialog.DialogType.Warning => "警告", + CommandDialog.DialogType.Error => "错误", + _ => "信息提示" + } }, + { "Message", 弹窗详细 ?? string.Empty }, + { "Icon", 弹窗类型 switch + { + CommandDialog.DialogType.Warning => "warn", + CommandDialog.DialogType.Error => "error", + _ => "info" + } }, + { "ShowOk", true }, + { "AutoCloseSeconds", (double)自动关闭秒数 } + }; + if (是否阻塞) + { + // 阻塞:用户手动关闭或自动关闭后才继续执行步骤 + dialogService.ShowDialog("MessageBox", param, _ => + { + registration.Dispose(); + tcs.TrySetResult(true); + }); + } + else + { + // 非阻塞:弹出后步骤立即继续(不等关闭回调) + dialogService.Show("MessageBox", param, _ => { }); + tcs.TrySetResult(true); + } + }); + return tcs.Task; + }; + // 配置全局日志分发器:按 CurrentScope 路由到对应 LogArea var globalInfo = Container.Resolve(); LoggerHelper.Progress = new ScopeLogDispatcher(globalInfo); diff --git a/ADP/ViewModels/Dialogs/MessageBoxViewModel.cs b/ADP/ViewModels/Dialogs/MessageBoxViewModel.cs index fe380c9..74cf6ae 100644 --- a/ADP/ViewModels/Dialogs/MessageBoxViewModel.cs +++ b/ADP/ViewModels/Dialogs/MessageBoxViewModel.cs @@ -1,7 +1,9 @@ using UIShare.PubEvent; using UIShare.ViewModelBase; +using System; using System.Windows.Input; using System.Windows.Media; +using System.Windows.Threading; namespace ADP.ViewModels.Dialogs { @@ -69,6 +71,11 @@ namespace ADP.ViewModels.Dialogs public DialogCloseListener RequestClose { get; set; } + /// + /// 自动关闭定时器:自动关闭秒数大于 0 时启动,到时自动关闭弹窗(同时解除阻塞等待) + /// + private DispatcherTimer _autoCloseTimer; + public MessageBoxViewModel(IContainerProvider containerProvider):base(containerProvider) { YesCommand = new DelegateCommand(OnYes); @@ -93,6 +100,7 @@ namespace ADP.ViewModels.Dialogs public override void OnDialogClosed() { + _autoCloseTimer?.Stop(); _eventAggregator.GetEvent().Publish(false); } @@ -117,6 +125,21 @@ namespace ADP.ViewModels.Dialogs ShowNo = parameters.GetValue("ShowNo"); ShowOk = parameters.GetValue("ShowOk"); ShowCancel = parameters.GetValue("ShowCancel"); + + // 自动关闭:秒数大于 0 时启动定时器,到时自动关闭(供命令库 弹窗 命令的自动关闭参数使用) + if (parameters.TryGetValue("AutoCloseSeconds", out double autoCloseSeconds) && autoCloseSeconds > 0) + { + _autoCloseTimer = new DispatcherTimer + { + Interval = TimeSpan.FromSeconds(autoCloseSeconds) + }; + _autoCloseTimer.Tick += (s, e) => + { + _autoCloseTimer.Stop(); + RequestClose.Invoke(new DialogResult(ButtonResult.OK)); + }; + _autoCloseTimer.Start(); + } } #endregion } diff --git a/Command/CommandDialog.cs b/Command/CommandDialog.cs new file mode 100644 index 0000000..e747feb --- /dev/null +++ b/Command/CommandDialog.cs @@ -0,0 +1,62 @@ +using Common.Attributes; +using System; +using System.ComponentModel; +using System.Threading; +using System.Threading.Tasks; + +namespace Command +{ + /// + /// 对话框命令:在测试流程中弹出提示窗口。 + /// 命令库为纯 .NET 类库,不直接引用 WPF 程序集; + /// 实际的弹窗能力由宿主程序(ADP 外壳)在启动时注入到 委托中实现(依赖倒置)。 + /// + [ADPCommand] + public static class CommandDialog + { + /// + /// 弹窗类型 + /// + public enum DialogType + { + /// + /// 信息提示 + /// + Info, + /// + /// 警告提示 + /// + Warning, + /// + /// 错误提示 + /// + Error + } + + /// + /// 弹窗处理委托(参数依次为:弹窗类型、弹窗详细、是否阻塞、自动关闭秒数、取消令牌)。 + /// 由宿主程序启动时注入实现;未注入时弹窗命令降级为仅输出日志,不会抛异常。 + /// + [Browsable(false)] + public static Func 弹窗处理器; + + /// + /// 弹窗:在界面上显示一个提示对话框。 + /// + /// 弹窗样式:Info 信息 / Warning 警告 / Error 错误 + /// 弹窗中显示的详细内容 + /// true = 步骤暂停,等待用户关闭(或自动关闭)后才继续;false = 弹出后步骤立即继续 + /// 大于 0 时,弹窗在指定秒数后自动关闭;小于等于 0 时不自动关闭,需用户手动关闭 + /// 异步取消令牌 + public static async Task 弹窗(DialogType 弹窗类型, string 弹窗详细, bool 是否阻塞, float 自动关闭秒数, CancellationToken ct) + { + var handler = 弹窗处理器; + if (handler == null) + { + Console.WriteLine($"[弹窗命令] 宿主未注入弹窗处理器,跳过弹窗:{弹窗详细}"); + return; + } + await handler(弹窗类型, 弹窗详细, 是否阻塞, 自动关闭秒数, ct); + } + } +}