181 lines
5.8 KiB
C#
181 lines
5.8 KiB
C#
using System;
|
|
using System.ComponentModel;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Interop;
|
|
using XP.Common.GeneralForm.ViewModels;
|
|
using XP.Common.Logging.Interfaces;
|
|
|
|
namespace XP.Common.GeneralForm.Views
|
|
{
|
|
/// <summary>
|
|
/// 通用进度条模态窗口,支持线程安全的进度更新和关闭操作
|
|
/// </summary>
|
|
public partial class ProgressWindow : Window
|
|
{
|
|
#region Win32 API
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
|
|
|
|
private const uint SC_CLOSE = 0xF060;
|
|
private const uint MF_BYCOMMAND = 0x00000000;
|
|
private const uint MF_GRAYED = 0x00000001;
|
|
private const uint MF_ENABLED = 0x00000000;
|
|
|
|
#endregion
|
|
|
|
private readonly ProgressWindowViewModel _viewModel;
|
|
private readonly ILoggerService? _logger;
|
|
private bool _isClosingByCode;
|
|
|
|
/// <summary>
|
|
/// 构造函数
|
|
/// </summary>
|
|
/// <param name="title">窗口标题</param>
|
|
/// <param name="message">提示信息</param>
|
|
/// <param name="isCancelable">是否允许用户关闭窗口</param>
|
|
/// <param name="logger">日志服务(可选)</param>
|
|
public ProgressWindow(
|
|
string title = "操作进行中",
|
|
string message = "请稍候...",
|
|
bool isCancelable = true,
|
|
ILoggerService? logger = null)
|
|
{
|
|
// 创建 ViewModel 并设置为 DataContext
|
|
_viewModel = new ProgressWindowViewModel(title, message, 0, isCancelable, logger);
|
|
DataContext = _viewModel;
|
|
|
|
// 保存日志服务引用,用于 View 层日志记录
|
|
_logger = logger?.ForModule<ProgressWindow>();
|
|
|
|
InitializeComponent();
|
|
|
|
// 继承主窗口图标
|
|
if (Application.Current?.MainWindow != null)
|
|
{
|
|
Icon = Application.Current.MainWindow.Icon;
|
|
}
|
|
|
|
// 订阅 Closing 事件,拦截用户手动关闭
|
|
Closing += OnWindowClosing;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 更新进度和提示信息(线程安全,可从任意线程调用)
|
|
/// </summary>
|
|
/// <param name="message">提示信息文本</param>
|
|
/// <param name="progress">进度值</param>
|
|
public void UpdateProgress(string message, double progress)
|
|
{
|
|
try
|
|
{
|
|
if (Dispatcher.CheckAccess())
|
|
{
|
|
_viewModel.UpdateProgress(message, progress);
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.Invoke(() => _viewModel.UpdateProgress(message, progress));
|
|
}
|
|
}
|
|
catch (TaskCanceledException)
|
|
{
|
|
// 窗口已关闭时 Dispatcher 调度可能抛出此异常,静默处理
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// 其他调度异常静默处理,避免影响调用方
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 线程安全关闭窗口(使用 new 关键字隐藏基类 Close 方法,因为 Window.Close() 不是虚方法)
|
|
/// </summary>
|
|
public new void Close()
|
|
{
|
|
try
|
|
{
|
|
_isClosingByCode = true;
|
|
|
|
if (Dispatcher.CheckAccess())
|
|
{
|
|
_logger?.Info("进度窗口正在关闭:Title={Title}", _viewModel.Title);
|
|
base.Close();
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.Invoke(() =>
|
|
{
|
|
_logger?.Info("进度窗口正在关闭:Title={Title}", _viewModel.Title);
|
|
base.Close();
|
|
});
|
|
}
|
|
}
|
|
catch (TaskCanceledException)
|
|
{
|
|
// 窗口已关闭时 Dispatcher 调度可能抛出此异常,静默处理
|
|
}
|
|
catch (Exception)
|
|
{
|
|
// 其他调度异常静默处理,避免影响调用方
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 窗口源初始化时,根据 IsCancelable 控制关闭按钮状态
|
|
/// </summary>
|
|
protected override void OnSourceInitialized(EventArgs e)
|
|
{
|
|
base.OnSourceInitialized(e);
|
|
|
|
if (!_viewModel.IsCancelable)
|
|
{
|
|
DisableCloseButton();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 拦截窗口关闭事件
|
|
/// </summary>
|
|
private void OnWindowClosing(object? sender, CancelEventArgs e)
|
|
{
|
|
// 当不可取消且非程序主动关闭时,阻止关闭
|
|
if (!_viewModel.IsCancelable && !_isClosingByCode)
|
|
{
|
|
e.Cancel = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 通过 Win32 API 禁用窗口关闭按钮(灰色不可点击)
|
|
/// </summary>
|
|
private void DisableCloseButton()
|
|
{
|
|
var hwnd = new WindowInteropHelper(this).Handle;
|
|
var hMenu = GetSystemMenu(hwnd, false);
|
|
if (hMenu != IntPtr.Zero)
|
|
{
|
|
EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 通过 Win32 API 启用窗口关闭按钮
|
|
/// </summary>
|
|
private void EnableCloseButton()
|
|
{
|
|
var hwnd = new WindowInteropHelper(this).Handle;
|
|
var hMenu = GetSystemMenu(hwnd, false);
|
|
if (hMenu != IntPtr.Zero)
|
|
{
|
|
EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | MF_ENABLED);
|
|
}
|
|
}
|
|
}
|
|
}
|