将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,148 @@
<Window x:Class="XP.Common.GeneralForm.Views.RealTimeLogViewer"
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"
xmlns:loc="clr-namespace:XP.Common.Localization.Extensions"
Title="{loc:Localization LogViewer_Title}"
Width="960" Height="600"
MinWidth="700" MinHeight="400"
WindowStartupLocation="CenterScreen"
ShowInTaskbar="True"
WindowStyle="SingleBorderWindow">
<Grid>
<Grid.RowDefinitions>
<!-- 顶部工具栏 | Top toolbar -->
<RowDefinition Height="Auto"/>
<!-- 级别筛选栏 | Level filter bar -->
<RowDefinition Height="Auto"/>
<!-- 日志显示区 | Log display area -->
<RowDefinition Height="*"/>
<!-- 底部状态栏 | Bottom status bar -->
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- === 顶部工具栏 | Top Toolbar === -->
<DockPanel Grid.Row="0" Margin="8,6" LastChildFill="True">
<!-- 左侧:自动滚动开关 + 清空按钮 | Left: Auto-scroll toggle + Clear button -->
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal" VerticalAlignment="Center">
<telerik:RadToggleButton x:Name="AutoScrollToggle"
telerik:StyleManager.Theme="Crystal"
IsChecked="{Binding IsAutoScroll, Mode=TwoWay}"
Content="{loc:Localization LogViewer_AutoScroll}"
Padding="10,4" Margin="0,0,6,0"/>
<telerik:RadButton telerik:StyleManager.Theme="Crystal"
Command="{Binding ClearCommand}"
Content="{loc:Localization LogViewer_ClearLog}"
Padding="10,4" Margin="0,0,12,0"/>
</StackPanel>
<!-- 右侧:过滤输入框 | Right: Filter input -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
<TextBlock Text="{loc:Localization LogViewer_Filter}" VerticalAlignment="Center" Margin="0,0,4,0"/>
<telerik:RadWatermarkTextBox telerik:StyleManager.Theme="Crystal"
Text="{Binding FilterText, UpdateSourceTrigger=PropertyChanged, Delay=300}"
WatermarkContent="{loc:Localization LogViewer_FilterWatermark}"
Width="260" Padding="4,3"
VerticalAlignment="Center"/>
</StackPanel>
</DockPanel>
<!-- === 级别筛选栏 | Level Filter Bar === -->
<Border Grid.Row="1" Background="#FFF8F8F8" BorderBrush="#FFDDDDDD" BorderThickness="0,0,0,1" Padding="8,4">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="{loc:Localization LogViewer_LevelFilter}" VerticalAlignment="Center" Margin="0,0,8,0" Foreground="#FF666666" FontSize="12"/>
<CheckBox Content="Debug" IsChecked="{Binding ShowDebug}" VerticalAlignment="Center" Margin="0,0,10,0" Foreground="Gray"/>
<CheckBox Content="Info" IsChecked="{Binding ShowInfo}" VerticalAlignment="Center" Margin="0,0,10,0" Foreground="#FF1E1E1E"/>
<CheckBox Content="Warning" IsChecked="{Binding ShowWarning}" VerticalAlignment="Center" Margin="0,0,10,0" Foreground="#FFC88200"/>
<CheckBox Content="Error" IsChecked="{Binding ShowError}" VerticalAlignment="Center" Margin="0,0,10,0" Foreground="Red"/>
<CheckBox Content="Fatal" IsChecked="{Binding ShowFatal}" VerticalAlignment="Center" Margin="0,0,10,0" Foreground="#FFB40000"/>
</StackPanel>
</Border>
<!-- === 日志显示区(RadGridView| Log Display Area === -->
<telerik:RadGridView Grid.Row="2"
x:Name="LogGridView"
telerik:StyleManager.Theme="Crystal"
ItemsSource="{Binding FilteredEntries}"
AutoGenerateColumns="False"
IsReadOnly="True"
RowIndicatorVisibility="Collapsed"
ShowGroupPanel="False"
ShowColumnHeaders="True"
CanUserFreezeColumns="False"
CanUserReorderColumns="False"
CanUserSortColumns="False"
CanUserResizeColumns="True"
IsFilteringAllowed="False"
SelectionMode="Extended"
FontFamily="Consolas"
FontSize="12"
Margin="4,0,4,0">
<telerik:RadGridView.Columns>
<!-- 时间戳列 | Timestamp column -->
<telerik:GridViewDataColumn Header="{loc:Localization LogViewer_ColTime}"
DataMemberBinding="{Binding TimestampDisplay}"
Width="100"
IsReadOnly="True"/>
<!-- 级别列 | Level column -->
<telerik:GridViewDataColumn Header="{loc:Localization LogViewer_ColLevel}"
DataMemberBinding="{Binding Level}"
Width="60"
IsReadOnly="True">
<telerik:GridViewDataColumn.CellStyle>
<Style TargetType="telerik:GridViewCell">
<Setter Property="Foreground" Value="{Binding LevelColor}"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
</telerik:GridViewDataColumn.CellStyle>
</telerik:GridViewDataColumn>
<!-- 来源列 | Source column -->
<telerik:GridViewDataColumn Header="{loc:Localization LogViewer_ColSource}"
DataMemberBinding="{Binding Source}"
Width="200"
IsReadOnly="True">
<telerik:GridViewDataColumn.CellStyle>
<Style TargetType="telerik:GridViewCell">
<Setter Property="Foreground" Value="{Binding LevelColor}"/>
</Style>
</telerik:GridViewDataColumn.CellStyle>
</telerik:GridViewDataColumn>
<!-- 消息列 | Message column -->
<telerik:GridViewDataColumn Header="{loc:Localization LogViewer_ColMessage}"
DataMemberBinding="{Binding Message}"
Width="*"
IsReadOnly="True">
<telerik:GridViewDataColumn.CellStyle>
<Style TargetType="telerik:GridViewCell">
<Setter Property="Foreground" Value="{Binding LevelColor}"/>
</Style>
</telerik:GridViewDataColumn.CellStyle>
</telerik:GridViewDataColumn>
</telerik:RadGridView.Columns>
</telerik:RadGridView>
<!-- === 底部状态栏 | Bottom Status Bar === -->
<StatusBar Grid.Row="3" Background="#FFF0F0F0" BorderBrush="#FFCCCCCC" BorderThickness="0,1,0,0">
<StatusBarItem>
<TextBlock Text="{Binding StatusText}" Foreground="#FF666666" FontSize="12"/>
</StatusBarItem>
<Separator/>
<StatusBarItem HorizontalAlignment="Right">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{loc:Localization LogViewer_MaxLines}" Foreground="#FF999999" FontSize="11" VerticalAlignment="Center"/>
<telerik:RadNumericUpDown telerik:StyleManager.Theme="Crystal"
Value="{Binding MaxLines, Mode=TwoWay}"
Minimum="100" Maximum="10000"
NumberDecimalDigits="0"
SmallChange="500"
Width="90" Height="22"
VerticalAlignment="Center"/>
</StackPanel>
</StatusBarItem>
</StatusBar>
</Grid>
</Window>
@@ -0,0 +1,85 @@
using System;
using System.Windows;
using Serilog.Events;
using XP.Common.Logging.ViewModels;
namespace XP.Common.GeneralForm.Views
{
/// <summary>
/// 实时日志查看器窗口,订阅 Serilog 事件并实时显示
/// Real-time log viewer window, subscribes to Serilog events and displays in real-time
/// </summary>
public partial class RealTimeLogViewer : Window
{
private readonly RealTimeLogViewerViewModel _viewModel;
/// <summary>
/// 构造函数 | Constructor
/// </summary>
/// <param name="maxLines">最大行数限制,默认 2000 | Max line count, default 2000</param>
public RealTimeLogViewer(int maxLines = 2000)
{
_viewModel = new RealTimeLogViewerViewModel { MaxLines = maxLines };
DataContext = _viewModel;
InitializeComponent();
// 继承主窗口图标 | Inherit main window icon
if (Application.Current?.MainWindow != null)
{
Icon = Application.Current.MainWindow.Icon;
}
// 订阅自动滚动事件 | Subscribe to auto-scroll event
_viewModel.ScrollToBottomRequested += OnScrollToBottomRequested;
// 加载缓冲区中的历史日志 | Load buffered history logs
var history = RealTimeLogSink.Instance.GetBufferedHistory();
foreach (var logEvent in history)
{
_viewModel.AddLogEvent(logEvent);
}
// 订阅 Serilog Sink 事件(在历史加载之后,避免重复)| Subscribe after history load
RealTimeLogSink.Instance.LogEventReceived += OnLogEventReceived;
Closed += OnWindowClosed;
}
/// <summary>
/// 接收 Serilog 日志事件 | Receive Serilog log event
/// </summary>
private void OnLogEventReceived(LogEvent logEvent)
{
_viewModel.AddLogEvent(logEvent);
}
/// <summary>
/// 自动滚动到底部 | Auto-scroll to bottom
/// </summary>
private void OnScrollToBottomRequested()
{
try
{
if (LogGridView.Items.Count > 0)
{
var lastItem = LogGridView.Items[LogGridView.Items.Count - 1];
LogGridView.ScrollIntoView(lastItem);
}
}
catch
{
// 滚动失败时静默处理 | Silently handle scroll failures
}
}
/// <summary>
/// 窗口关闭时取消订阅,防止内存泄漏 | Unsubscribe on close to prevent memory leaks
/// </summary>
private void OnWindowClosed(object? sender, EventArgs e)
{
_viewModel.ScrollToBottomRequested -= OnScrollToBottomRequested;
RealTimeLogSink.Instance.LogEventReceived -= OnLogEventReceived;
}
}
}