将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,176 @@
<Window x:Class="XP.Hardware.PLC.Sentry.Views.SentryMainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:views="clr-namespace:XP.Hardware.PLC.Sentry.Views"
xmlns:converters="clr-namespace:XP.Hardware.PLC.Sentry.Converters"
mc:Ignorable="d"
Title="PLC Sentry Monitor"
Icon="/XplorerPlane.ico"
Width="1200" Height="1000"
MinWidth="900" MinHeight="600"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
<converters:LogLevelToColorConverter x:Key="LogLevelToColorConverter"/>
<converters:ConnectionStatusToColorConverter x:Key="ConnectionStatusToColorConverter"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<!-- 工具栏 | Toolbar -->
<RowDefinition Height="Auto"/>
<!-- 信号监控区域 | Signal monitor area -->
<RowDefinition Height="*" MinHeight="200"/>
<!-- 可拖拽分隔条 | Draggable splitter -->
<RowDefinition Height="5"/>
<!-- 操作日志面板 | Operation log panel -->
<RowDefinition Height="300" MinHeight="100"/>
<!-- 状态栏 | Status bar -->
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- === 操作按钮区域 | Action buttons area === -->
<DockPanel Grid.Row="0" Margin="8,6" LastChildFill="False">
<!-- 左侧:连接、断开 | Left: Connect, Disconnect -->
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal">
<telerik:RadButton Command="{Binding ConnectCommand}"
telerik:StyleManager.Theme="Crystal"
Content="连接 PLC"
ToolTip="连接 PLC | Connect PLC"
Padding="10,4" Margin="0,0,6,0"/>
<telerik:RadButton Command="{Binding DisconnectCommand}"
telerik:StyleManager.Theme="Crystal"
Content="断开 PLC"
ToolTip="断开 PLC | Disconnect PLC"
Padding="10,4"/>
</StackPanel>
<!-- 右侧:PLC 连接管理、刷新信号定义 | Right: PLC Config, Refresh -->
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
<telerik:RadButton Command="{Binding OpenConfigEditorCommand}"
telerik:StyleManager.Theme="Crystal"
Content="PLC 信号地址定义编辑"
ToolTip="PLC 信号地址定义编辑器 | PLC Signal Address Definition Editor"
Padding="10,4" Margin="0,0,6,0"/>
<telerik:RadButton Command="{Binding RefreshSignalDefinitionsCommand}"
telerik:StyleManager.Theme="Crystal"
Content="刷新PLC信号定义"
ToolTip="刷新PLC信号定义 | Refresh PLC Signal Definitions"
Padding="10,4" Margin="0,0,6,0"/>
<telerik:RadButton Command="{Binding OpenSignalDataDemoCommand}"
telerik:StyleManager.Theme="Crystal"
Content="PLC信号读写测试"
ToolTip="打开 ISignalDataService DEMO 窗口 | Open Signal Data Demo Window"
Padding="10,4"/>
</StackPanel>
</DockPanel>
<!-- === 信号监控区域 | Signal monitor area === -->
<views:SignalMonitorView Grid.Row="1" Margin="4,4,4,0"
DataContext="{Binding}"/>
<!-- === 可拖拽分隔条 | Draggable GridSplitter === -->
<GridSplitter Grid.Row="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Height="5"
Background="#FFCCCCCC"
ResizeDirection="Rows"
ShowsPreview="True"/>
<!-- === 操作日志面板 | Operation log panel === -->
<GroupBox Grid.Row="3" Header="操作日志 | Operation Log" Margin="4,0,4,4">
<DockPanel>
<!-- 清除日志按钮 | Clear log button -->
<telerik:RadButton DockPanel.Dock="Bottom"
Content="清除日志 | Clear Log"
telerik:StyleManager.Theme="Crystal"
Command="{Binding ClearLogCommand}"
HorizontalAlignment="Right"
Width="120" Margin="0,4,0,0" Padding="5,3"/>
<!-- 日志表格 | Log grid -->
<telerik:RadGridView ItemsSource="{Binding LogEntries}"
telerik:StyleManager.Theme="Crystal"
AutoGenerateColumns="False"
IsReadOnly="True"
RowIndicatorVisibility="Collapsed"
ShowGroupPanel="False"
ShowColumnHeaders="True"
CanUserFreezeColumns="False"
CanUserReorderColumns="False"
CanUserSortColumns="False"
CanUserResizeColumns="True"
IsFilteringAllowed="False"
SelectionMode="Single"
FontFamily="Consolas"
FontSize="12"
Background="#FFF9F9F9"
BorderBrush="#FFBDBDBD"
Margin="0,5,0,0">
<telerik:RadGridView.Columns>
<!-- 时间戳列 | Timestamp column -->
<telerik:GridViewDataColumn Header="时间 | Time"
DataMemberBinding="{Binding TimestampDisplay}"
Width="110"
IsReadOnly="True"/>
<!-- 级别列 | Level column -->
<telerik:GridViewDataColumn Header="级别 | Level"
DataMemberBinding="{Binding Level}"
Width="70"
IsReadOnly="True">
<telerik:GridViewDataColumn.CellStyle>
<Style TargetType="telerik:GridViewCell">
<Setter Property="Foreground" Value="{Binding Level, Converter={StaticResource LogLevelToColorConverter}}"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
</telerik:GridViewDataColumn.CellStyle>
</telerik:GridViewDataColumn>
<!-- 消息列 | Message column -->
<telerik:GridViewDataColumn Header="消息 | Message"
DataMemberBinding="{Binding Message}"
Width="*"
IsReadOnly="True">
<telerik:GridViewDataColumn.CellStyle>
<Style TargetType="telerik:GridViewCell">
<Setter Property="Foreground" Value="{Binding Level, Converter={StaticResource LogLevelToColorConverter}}"/>
</Style>
</telerik:GridViewDataColumn.CellStyle>
</telerik:GridViewDataColumn>
</telerik:RadGridView.Columns>
</telerik:RadGridView>
</DockPanel>
</GroupBox>
<!-- === 状态栏 | Status bar === -->
<StatusBar Grid.Row="4" Background="#FFF0F0F0" BorderBrush="#FFCCCCCC" BorderThickness="0,1,0,0">
<!-- 连接状态指示灯和文本 | Connection status indicator and text -->
<StatusBarItem>
<StackPanel Orientation="Horizontal">
<!-- 连接状态指示灯(绿/灰/黄)| Connection status indicator (green/gray/yellow) -->
<Ellipse Width="12" Height="12" VerticalAlignment="Center" Margin="4,0,6,0">
<Ellipse.Fill>
<MultiBinding Converter="{StaticResource ConnectionStatusToColorConverter}">
<Binding Path="IsConnected"/>
<Binding Path="StatusText"/>
</MultiBinding>
</Ellipse.Fill>
</Ellipse>
<TextBlock Text="{Binding StatusText}" VerticalAlignment="Center"
Foreground="#FF666666" FontSize="12"/>
</StackPanel>
</StatusBarItem>
<!-- 分隔符 | Separator -->
<Separator/>
<!-- 应用信息(右侧)| Application info (right side) -->
<StatusBarItem HorizontalAlignment="Right">
<TextBlock Text="PLC Sentry Monitor v1.0" Foreground="#FF999999" FontSize="11"/>
</StatusBarItem>
</StatusBar>
</Grid>
</Window>
@@ -0,0 +1,17 @@
using System.Windows;
using XP.Hardware.PLC.Sentry.ViewModels;
namespace XP.Hardware.PLC.Sentry.Views
{
/// <summary>
/// PLC Sentry Monitor 主窗口 | PLC Sentry Monitor main window
/// </summary>
public partial class SentryMainWindow : Window
{
public SentryMainWindow(SentryMainViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}
}
@@ -0,0 +1,140 @@
<Window x:Class="XP.App.Views.SignalDataDemoWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ISignalDataService DEMO"
Width="800" Height="650"
MinWidth="700" MinHeight="550"
WindowStartupLocation="CenterScreen"
Background="#FFF5F5F5">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</Window.Resources>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 连接区 -->
<GroupBox Grid.Row="0" Header="1. 连接 PLC 并加载信号定义" Margin="0,0,0,5">
<StackPanel Margin="8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="信号定义文件:" VerticalAlignment="Center" Margin="0,0,5,0"/>
<TextBox Grid.Column="1" Text="{Binding XmlFilePath, UpdateSourceTrigger=PropertyChanged}"
VerticalContentAlignment="Center" Margin="0,0,8,0"/>
<Button Grid.Column="2" Content="连接并加载" Command="{Binding ConnectAndLoadCommand}"
Width="90" Padding="5,3" Margin="0,0,8,0"/>
<StackPanel Grid.Column="3" Orientation="Horizontal">
<Ellipse Width="12" Height="12" VerticalAlignment="Center" Margin="0,0,5,0">
<Ellipse.Style>
<Style TargetType="Ellipse">
<Setter Property="Fill" Value="#FFBDBDBD"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Fill" Value="#FF4CAF50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<TextBlock Text="{Binding StatusText}" VerticalAlignment="Center"
Foreground="#FF666666" FontSize="11" MaxWidth="200" TextTrimming="CharacterEllipsis"/>
</StackPanel>
</Grid>
</StackPanel>
</GroupBox>
<!-- 信号读取区(批量缓存/单点自动路由)-->
<GroupBox Grid.Row="1" Header="2. 信号读取 (GetValueByName - 自动路由批量缓存/单点读取)" Margin="0,0,0,5">
<Grid Margin="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="150"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="信号名称:" VerticalAlignment="Center" Margin="0,0,5,0"/>
<TextBox Grid.Column="1" Text="{Binding ReadSignalName, UpdateSourceTrigger=PropertyChanged}"
VerticalContentAlignment="Center" Margin="0,0,8,0"/>
<Button Grid.Column="2" Content="读取" Command="{Binding CacheReadCommand}"
Width="60" Padding="5,3" Margin="0,0,8,0"/>
<TextBlock Grid.Column="3" Text="结果:" VerticalAlignment="Center" Margin="0,0,5,0"/>
<TextBox Grid.Column="4" Text="{Binding ReadResult, Mode=OneWay}" IsReadOnly="True"
VerticalContentAlignment="Center" Background="#FFF0F0F0" Foreground="#FF1B5E20"/>
</Grid>
</GroupBox>
<!-- 队列写入区 -->
<GroupBox Grid.Row="2" Header="3. 队列写入 (EnqueueWrite)" Margin="0,0,0,5">
<Grid Margin="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="信号名称:" VerticalAlignment="Center" Margin="0,0,5,0"/>
<TextBox Grid.Column="1" Text="{Binding QueueSignalName, UpdateSourceTrigger=PropertyChanged}"
VerticalContentAlignment="Center" Margin="0,0,8,0"/>
<TextBlock Grid.Column="2" Text="写入值:" VerticalAlignment="Center" Margin="0,0,5,0"/>
<TextBox Grid.Column="3" Text="{Binding QueueWriteValue, UpdateSourceTrigger=PropertyChanged}"
VerticalContentAlignment="Center" Margin="0,0,8,0"/>
<Button Grid.Column="4" Content="入队写入" Command="{Binding QueueWriteCommand}"
Width="70" Padding="5,3"/>
</Grid>
</GroupBox>
<!-- 直接写入+回读校验区 -->
<GroupBox Grid.Row="3" Header="4. 直接写入+回读校验 (WriteDirectWithVerify)" Margin="0,0,0,5">
<Grid Margin="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="信号名称:" VerticalAlignment="Center" Margin="0,0,5,0"/>
<TextBox Grid.Column="1" Text="{Binding DirectSignalName, UpdateSourceTrigger=PropertyChanged}"
VerticalContentAlignment="Center" Margin="0,0,8,0"/>
<TextBlock Grid.Column="2" Text="写入值:" VerticalAlignment="Center" Margin="0,0,5,0"/>
<TextBox Grid.Column="3" Text="{Binding DirectWriteValue, UpdateSourceTrigger=PropertyChanged}"
VerticalContentAlignment="Center" Margin="0,0,8,0"/>
<Button Grid.Column="4" Content="直接写入" Command="{Binding DirectWriteCommand}"
Width="70" Padding="5,3"/>
</Grid>
</GroupBox>
<!-- 日志区 -->
<GroupBox Grid.Row="4" Header="操作日志">
<DockPanel Margin="5">
<Button DockPanel.Dock="Bottom" Content="清除日志" Command="{Binding ClearLogCommand}"
HorizontalAlignment="Right" Width="80" Margin="0,5,0,0" Padding="5,3"/>
<ListBox x:Name="LogListBox" ItemsSource="{Binding LogEntries}"
FontFamily="Consolas" FontSize="12"
Background="#FFF9F9F9" BorderBrush="#FFBDBDBD"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="2,1"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</DockPanel>
</GroupBox>
</Grid>
</Window>
@@ -0,0 +1,43 @@
using System.Collections.Specialized;
using System.Windows;
using XP.App.ViewModels;
using XP.Common.Logging.Interfaces;
using XP.Hardware.Plc.Abstractions;
using XP.Hardware.Plc.Services;
using XP.Hardware.PLC.Configs;
namespace XP.App.Views
{
/// <summary>
/// ISignalDataService DEMO 窗口
/// </summary>
public partial class SignalDataDemoWindow : Window
{
public SignalDataDemoWindow(
PlcService plcService,
ISignalDataService signalService,
ConfigLoader configLoader,
ILoggerService logger)
{
InitializeComponent();
var vm = new SignalDataDemoViewModel(plcService, signalService, configLoader, logger);
DataContext = vm;
if (Application.Current?.MainWindow != null)
Icon = Application.Current.MainWindow.Icon;
// 日志自动滚动
vm.LogEntries.CollectionChanged += (s, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add && vm.LogEntries.Count > 0)
{
Dispatcher.InvokeAsync(() =>
{
LogListBox.ScrollIntoView(vm.LogEntries[vm.LogEntries.Count - 1]);
}, System.Windows.Threading.DispatcherPriority.Background);
}
};
}
}
}
@@ -0,0 +1,193 @@
<UserControl x:Class="XP.Hardware.PLC.Sentry.Views.SignalMonitorView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:views="clr-namespace:XP.Hardware.PLC.Sentry.Views"
xmlns:converters="clr-namespace:XP.Hardware.PLC.Sentry.Converters"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
<converters:NullOrEmptyToVisibilityConverter x:Key="NullToCollapsedConverter"/>
<!-- 读取错误时的前景色样式 | Foreground style for read errors -->
<Style x:Key="ReadErrorCellStyle" TargetType="telerik:GridViewCell">
<Style.Triggers>
<DataTrigger Binding="{Binding HasReadError}" Value="True">
<Setter Property="Foreground" Value="#FFD32F2F"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</DataTrigger>
</Style.Triggers>
</Style>
<!-- === 写入控件模板 | Write control templates === -->
<!-- bool 类型:ToggleButton | Bool type: ToggleButton -->
<DataTemplate x:Key="BoolWriteTemplate">
<StackPanel Orientation="Vertical">
<ToggleButton IsChecked="{Binding WriteValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Content="{Binding WriteValue, TargetNullValue=False}"
MinWidth="60" Padding="6,2"
HorizontalAlignment="Stretch"/>
<TextBlock Text="{Binding ValidationError}"
Foreground="#FFD32F2F" FontSize="10"
Visibility="{Binding ValidationError, Converter={StaticResource NullToCollapsedConverter}}"
TextWrapping="Wrap" Margin="0,2,0,0"/>
</StackPanel>
</DataTemplate>
<!-- 整数类型:RadNumericUpDown(整数模式)| Integer type: RadNumericUpDown (integer mode) -->
<DataTemplate x:Key="IntegerWriteTemplate">
<StackPanel Orientation="Vertical">
<telerik:RadNumericUpDown Value="{Binding WriteValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
telerik:StyleManager.Theme="Crystal"
NumberDecimalDigits="0"
IsInteger="True"
MinWidth="80"
HorizontalAlignment="Stretch"
UpdateValueEvent="PropertyChanged"/>
<TextBlock Text="{Binding ValidationError}"
Foreground="#FFD32F2F" FontSize="10"
Visibility="{Binding ValidationError, Converter={StaticResource NullToCollapsedConverter}}"
TextWrapping="Wrap" Margin="0,2,0,0"/>
</StackPanel>
</DataTemplate>
<!-- 浮点类型:RadNumericUpDown(浮点模式)| Float type: RadNumericUpDown (float mode) -->
<DataTemplate x:Key="FloatWriteTemplate">
<StackPanel Orientation="Vertical">
<telerik:RadNumericUpDown Value="{Binding WriteValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
telerik:StyleManager.Theme="Crystal"
NumberDecimalDigits="4"
IsInteger="False"
MinWidth="80"
HorizontalAlignment="Stretch"
UpdateValueEvent="PropertyChanged"/>
<TextBlock Text="{Binding ValidationError}"
Foreground="#FFD32F2F" FontSize="10"
Visibility="{Binding ValidationError, Converter={StaticResource NullToCollapsedConverter}}"
TextWrapping="Wrap" Margin="0,2,0,0"/>
</StackPanel>
</DataTemplate>
<!-- 字符串类型:TextBox | String type: TextBox -->
<DataTemplate x:Key="StringWriteTemplate">
<StackPanel Orientation="Vertical">
<TextBox Text="{Binding WriteValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
MinWidth="80"
HorizontalAlignment="Stretch"
Padding="2"/>
<TextBlock Text="{Binding ValidationError}"
Foreground="#FFD32F2F" FontSize="10"
Visibility="{Binding ValidationError, Converter={StaticResource NullToCollapsedConverter}}"
TextWrapping="Wrap" Margin="0,2,0,0"/>
</StackPanel>
</DataTemplate>
<!-- 写入值模板选择器 | Write value template selector -->
<views:WriteValueTemplateSelector x:Key="WriteValueTemplateSelector"
BoolTemplate="{StaticResource BoolWriteTemplate}"
IntegerTemplate="{StaticResource IntegerWriteTemplate}"
FloatTemplate="{StaticResource FloatWriteTemplate}"
StringTemplate="{StaticResource StringWriteTemplate}"/>
</UserControl.Resources>
<Grid>
<!-- 无数据时的占位提示 | Placeholder when no data -->
<TextBlock Text="请加载信号定义文件 | Please load signal definition file"
HorizontalAlignment="Center" VerticalAlignment="Center"
Foreground="#FF999999" FontSize="14"
Visibility="{Binding HasNoGroups, Converter={StaticResource BoolToVis}}"/>
<!-- 信号分组标签页 | Signal group tabs -->
<telerik:RadTabControl ItemsSource="{Binding SignalGroups}"
telerik:StyleManager.Theme="Crystal"
DisplayMemberPath="TabHeader"
OverflowMode="Scroll"
TabStripPlacement="Top">
<telerik:RadTabControl.ContentTemplate>
<DataTemplate>
<!-- 每个 Tab 页内的信号表格 | Signal grid within each tab -->
<telerik:RadGridView ItemsSource="{Binding Signals}"
telerik:StyleManager.Theme="Crystal"
AutoGenerateColumns="False"
IsReadOnly="False"
RowIndicatorVisibility="Collapsed"
ShowGroupPanel="False"
CanUserFreezeColumns="False"
CanUserReorderColumns="True"
CanUserSortColumns="True"
CanUserResizeColumns="True"
ColumnWidth="*"
SelectionMode="Single"
IsFilteringAllowed="False"
ShowColumnSortIndexes="False">
<telerik:RadGridView.Columns>
<!-- 名称列 | Name column -->
<telerik:GridViewDataColumn Header="名称 | Name"
DataMemberBinding="{Binding Name}"
IsReadOnly="True"
Width="200"/>
<!-- 类型列 | Type column -->
<telerik:GridViewDataColumn Header="类型 | Type"
DataMemberBinding="{Binding Type}"
IsReadOnly="True"
Width="80"/>
<!-- 地址列 | Address column -->
<telerik:GridViewDataColumn Header="地址 | Address"
DataMemberBinding="{Binding AddressDisplay}"
IsReadOnly="True"
Width="120"/>
<!-- 当前值列 | Current value column -->
<telerik:GridViewDataColumn Header="当前值 | Value"
DataMemberBinding="{Binding CurrentValue}"
IsReadOnly="True"
Width="120"
CellStyle="{StaticResource ReadErrorCellStyle}"/>
<!-- 写入值列(根据类型动态选择控件)| Write value column (dynamic control by type) -->
<telerik:GridViewColumn Header="写入值 | Write" Width="140">
<telerik:GridViewColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding}"
ContentTemplateSelector="{StaticResource WriteValueTemplateSelector}"/>
</DataTemplate>
</telerik:GridViewColumn.CellTemplate>
</telerik:GridViewColumn>
<!-- 操作按钮列 | Action button column -->
<telerik:GridViewColumn Header="操作 | Action" Width="140">
<telerik:GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<telerik:RadButton Content="应用" Command="{Binding ApplyCommand}"
telerik:StyleManager.Theme="Crystal"
Width="50" Margin="0,0,4,0" Padding="4,2"
ToolTip="队列写入 | Queue Write"/>
<telerik:RadButton Content="直写" Command="{Binding DirectWriteCommand}"
telerik:StyleManager.Theme="Crystal"
Width="50" Padding="4,2"
ToolTip="直接写入+回读校验 | Direct Write with Verify"/>
</StackPanel>
</DataTemplate>
</telerik:GridViewColumn.CellTemplate>
</telerik:GridViewColumn>
<!-- 备注列 | Remark column -->
<telerik:GridViewDataColumn Header="备注 | Remark"
DataMemberBinding="{Binding Remark}"
IsReadOnly="True"
Width="*"/>
</telerik:RadGridView.Columns>
</telerik:RadGridView>
</DataTemplate>
</telerik:RadTabControl.ContentTemplate>
</telerik:RadTabControl>
</Grid>
</UserControl>
@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace XP.Hardware.PLC.Sentry.Views
{
/// <summary>
/// 信号监控视图 | Signal monitor view
/// </summary>
public partial class SignalMonitorView : UserControl
{
public SignalMonitorView()
{
InitializeComponent();
}
}
}
@@ -0,0 +1,50 @@
using System.Windows;
using System.Windows.Controls;
using XP.Hardware.PLC.Sentry.ViewModels;
namespace XP.Hardware.PLC.Sentry.Views
{
/// <summary>
/// 写入值控件模板选择器,根据信号数据类型选择不同的写入控件 | Write value template selector, selects different write controls based on signal data type
/// bool → ToggleButton, byte/short/int → RadNumericUpDown(整数), single/double → RadNumericUpDown(浮点), string → TextBox
/// </summary>
public class WriteValueTemplateSelector : DataTemplateSelector
{
/// <summary>
/// bool 类型写入模板 | Bool type write template
/// </summary>
public DataTemplate BoolTemplate { get; set; }
/// <summary>
/// 整数类型写入模板(byte, short, int| Integer type write template
/// </summary>
public DataTemplate IntegerTemplate { get; set; }
/// <summary>
/// 浮点类型写入模板(single, double| Float type write template
/// </summary>
public DataTemplate FloatTemplate { get; set; }
/// <summary>
/// 字符串类型写入模板 | String type write template
/// </summary>
public DataTemplate StringTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is SignalRowViewModel row)
{
return row.Type?.ToLowerInvariant() switch
{
"bool" => BoolTemplate,
"byte" or "short" or "int" => IntegerTemplate,
"single" or "double" => FloatTemplate,
"string" => StringTemplate,
_ => StringTemplate // 默认使用文本框 | Default to TextBox
};
}
return base.SelectTemplate(item, container);
}
}
}