将Feature/XP.Common和Feature/XP.Hardware分支合并至Develop/XP.forHardwareAndCommon,完善XPapp注册和相关硬件类库通用类库功能。

This commit is contained in:
QI Mingxuan
2026-04-16 17:31:13 +08:00
parent 6ec4c3ddaa
commit 2bd6e566c3
581 changed files with 74600 additions and 222 deletions
@@ -0,0 +1,66 @@
<Window x:Class="XP.Common.GeneralForm.Views.InputDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
Title="{Binding Title}"
WindowStartupLocation="CenterOwner"
ResizeMode="NoResize"
Width="400" Height="180"
ShowInTaskbar="False"
Topmost="True"
WindowStyle="SingleBorderWindow">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
</Window.Resources>
<Grid Margin="20,16,20,16">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 提示文本 | Prompt text -->
<TextBlock Grid.Row="0"
Text="{Binding Prompt}"
TextWrapping="Wrap"
FontSize="13"
Foreground="#FF333333"
Margin="0,0,0,8"/>
<!-- 输入框 | Input text box -->
<telerik:RadWatermarkTextBox Grid.Row="1"
Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged}"
Height="28"
FontSize="13"
telerik:StyleManager.Theme="Crystal"
Margin="0,0,0,2"/>
<!-- 验证错误提示 | Validation error message -->
<TextBlock Grid.Row="2"
Text="{Binding ValidationError}"
Foreground="#FFD32F2F"
FontSize="11"
TextWrapping="Wrap"
Margin="0,7,0,0"
Visibility="{Binding HasValidationError, Converter={StaticResource BoolToVisibilityConverter}}"/>
<!-- 按钮区域 | Button area -->
<StackPanel Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Right">
<telerik:RadButton Content="{Binding OkText}"
Click="OnOkClick"
Width="80" Height="28"
Margin="0,0,8,0"
IsDefault="True"
telerik:StyleManager.Theme="Crystal"/>
<telerik:RadButton Content="{Binding CancelText}"
Click="OnCancelClick"
Width="80" Height="28"
IsCancel="True"
telerik:StyleManager.Theme="Crystal"/>
</StackPanel>
</Grid>
</Window>
@@ -0,0 +1,92 @@
using System;
using System.Windows;
using XP.Common.GeneralForm.ViewModels;
using XP.Common.Localization;
namespace XP.Common.GeneralForm.Views
{
/// <summary>
/// 通用输入对话框,支持单行文本输入和可选的输入验证
/// General input dialog with single-line text input and optional validation
/// </summary>
public partial class InputDialog : Window
{
private readonly InputDialogViewModel _viewModel;
/// <summary>
/// 用户输入的结果,取消时为 null | User input result, null if cancelled
/// </summary>
public string? Result { get; private set; }
/// <summary>
/// 构造函数 | Constructor
/// </summary>
/// <param name="prompt">提示文本 | Prompt text</param>
/// <param name="title">窗口标题 | Window title</param>
/// <param name="defaultValue">默认值 | Default value</param>
/// <param name="validate">可选的验证委托,返回 null 表示通过,返回错误信息则阻止确认 | Optional validation delegate</param>
public InputDialog(
string prompt,
string title,
string defaultValue = "",
Func<string, string?>? validate = null)
{
_viewModel = new InputDialogViewModel(prompt, title, defaultValue, validate);
DataContext = _viewModel;
InitializeComponent();
// 继承调用方窗口图标 | Inherit caller window icon
if (Application.Current?.MainWindow != null)
{
Icon = Application.Current.MainWindow.Icon;
}
}
/// <summary>
/// 确定按钮点击事件 | OK button click handler
/// </summary>
private void OnOkClick(object sender, RoutedEventArgs e)
{
// 执行验证 | Run validation
if (!_viewModel.Validate())
return;
Result = _viewModel.InputText;
DialogResult = true;
}
/// <summary>
/// 取消按钮点击事件 | Cancel button click handler
/// </summary>
private void OnCancelClick(object sender, RoutedEventArgs e)
{
Result = null;
DialogResult = false;
}
/// <summary>
/// 静态便捷方法,显示输入对话框并返回结果 | Static convenience method
/// </summary>
/// <param name="prompt">提示文本 | Prompt text</param>
/// <param name="title">窗口标题 | Window title</param>
/// <param name="defaultValue">默认值 | Default value</param>
/// <param name="validate">可选的验证委托 | Optional validation delegate</param>
/// <param name="owner">父窗口(可选)| Owner window (optional)</param>
/// <returns>用户输入的值,取消时返回 null | User input, null if cancelled</returns>
public static string? Show(
string prompt,
string title,
string defaultValue = "",
Func<string, string?>? validate = null,
Window? owner = null)
{
var dialog = new InputDialog(prompt, title, defaultValue, validate);
// owner 未指定时自动回退到主窗口,确保对话框有父窗口约束不会被遮挡
// Fall back to MainWindow if owner not specified, ensuring dialog stays in front
dialog.Owner = owner ?? Application.Current?.MainWindow;
dialog.ShowDialog();
return dialog.Result;
}
}
}
@@ -0,0 +1,43 @@
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" x:Class="XP.Common.GeneralForm.Views.ProgressWindow"
Title="{Binding Title}"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize"
Width="400"
Height="150"
ShowInTaskbar="False"
WindowStyle="SingleBorderWindow">
<!-- 主布局:从上到下依次为提示信息、进度条、百分比文本 -->
<Grid Margin="24,20,24,20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- 提示信息文本 -->
<TextBlock Grid.Row="0"
Text="{Binding Message}"
TextWrapping="Wrap"
FontSize="14"
Margin="0,0,0,12" />
<!-- 进度条 -->
<telerik:RadProgressBar Grid.Row="1"
Value="{Binding Progress, Mode=OneWay}"
Minimum="0"
Maximum="100"
Height="18"
Margin="0,0,0,8" telerik:StyleManager.Theme="Crystal" />
<!-- 百分比文本 -->
<TextBlock Grid.Row="2"
Text="{Binding ProgressText}"
HorizontalAlignment="Center"
FontSize="12"
Foreground="#666666" />
</Grid>
</Window>
@@ -0,0 +1,180 @@
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);
}
}
}
}