feat: 增强MQTT服务管理功能并优化设备视图

This commit is contained in:
2025-10-05 12:11:04 +08:00
parent b85ffdc21b
commit ea18a6ac2c
6 changed files with 468 additions and 236 deletions

View File

@@ -28,10 +28,18 @@ public interface IMqttAppService
/// </summary>
Task UpdateMqttServerAsync(MqttServerDto mqttServerDto);
/// <summary>
/// 异步批量更新MQTT服务器。
/// </summary>
Task<int> UpdateMqttServersAsync(List<MqttServerDto> mqttServerDtos);
/// <summary>
/// 异步根据ID删除一个MQTT服务器。
/// </summary>
Task<int> DeleteMqttServerAsync(int id);
/// <summary>
/// 异步批量删除MQTT服务器。
/// </summary>
Task<bool> DeleteMqttServersAsync(List<int> ids);
}

View File

@@ -17,15 +17,25 @@ public interface IMqttManagementService
/// <summary>
/// 异步创建一个新的MQTT服务器。
/// </summary>
Task<int> CreateMqttServerAsync(MqttServerDto mqttServerDto);
Task<MqttServerDto> CreateMqttServerAsync(MqttServerDto mqttServerDto);
/// <summary>
/// 异步更新一个已存在的MQTT服务器。
/// </summary>
Task UpdateMqttServerAsync(MqttServerDto mqttServerDto);
Task<int> UpdateMqttServerAsync(MqttServerDto mqttServerDto);
/// <summary>
/// 异步批量更新MQTT服务器。
/// </summary>
Task<int> UpdateMqttServersAsync(List<MqttServerDto> mqttServerDtos);
/// <summary>
/// 异步删除一个MQTT服务器。
/// </summary>
Task DeleteMqttServerAsync(int id);
Task<bool> DeleteMqttServerAsync(int id);
/// <summary>
/// 异步批量删除MQTT服务器。
/// </summary>
Task<bool> DeleteMqttServersAsync(List<int> ids);
}

View File

@@ -117,4 +117,60 @@ public class MqttAppService : IMqttAppService
throw new ApplicationException($"删除MQTT服务器时发生错误操作已回滚,错误信息:{ex.Message}", ex);
}
}
/// <summary>
/// 异步批量更新MQTT服务器事务性操作
/// </summary>
/// <param name="mqttServerDtos">要更新的MQTT服务器数据传输对象列表。</param>
/// <returns>成功更新的MQTT服务器数量。</returns>
/// <exception cref="ApplicationException">如果批量更新MQTT服务器时发生错误。</exception>
public async Task<int> UpdateMqttServersAsync(List<MqttServerDto> mqttServerDtos)
{
try
{
await _repoManager.BeginTranAsync();
var count = 0;
foreach (var mqttServerDto in mqttServerDtos)
{
var mqttServer = await _repoManager.MqttServers.GetByIdAsync(mqttServerDto.Id);
if (mqttServer != null)
{
_mapper.Map(mqttServerDto, mqttServer);
await _repoManager.MqttServers.UpdateAsync(mqttServer);
count++;
}
}
await _repoManager.CommitAsync();
return count;
}
catch (Exception ex)
{
await _repoManager.RollbackAsync();
throw new ApplicationException("批量更新MQTT服务器时发生错误操作已回滚。", ex);
}
}
/// <summary>
/// 异步批量删除MQTT服务器事务性操作
/// </summary>
/// <param name="ids">要删除的MQTT服务器ID列表。</param>
/// <returns>如果删除成功则为 true否则为 false。</returns>
/// <exception cref="ApplicationException">如果批量删除MQTT服务器时发生错误。</exception>
public async Task<bool> DeleteMqttServersAsync(List<int> ids)
{
try
{
if (ids == null || !ids.Any()) return true;
await _repoManager.BeginTranAsync();
var result = await _repoManager.MqttServers.DeleteByIdsAsync(ids);
await _repoManager.CommitAsync();
return result > 0;
}
catch (Exception ex)
{
await _repoManager.RollbackAsync();
throw new ApplicationException($"批量删除MQTT服务器时发生错误操作已回滚,错误信息:{ex.Message}", ex);
}
}
}

View File

@@ -1,3 +1,5 @@
using System.Collections.Concurrent;
using AutoMapper;
using DMS.Application.DTOs;
using DMS.Application.Events;
using DMS.Application.Interfaces;
@@ -15,12 +17,20 @@ public class MqttManagementService : IMqttManagementService
private readonly IMqttAppService _mqttAppService;
private readonly IAppDataStorageService _appDataStorageService;
private readonly IEventService _eventService;
private readonly IMapper _mapper;
private readonly IDataProcessingService _dataProcessingService;
public MqttManagementService(IMqttAppService mqttAppService, IAppDataStorageService appDataStorageService, IEventService eventService)
public MqttManagementService(IMqttAppService mqttAppService,
IAppDataStorageService appDataStorageService,
IEventService eventService,
IMapper mapper,
IDataProcessingService dataProcessingService)
{
_mqttAppService = mqttAppService;
_appDataStorageService = appDataStorageService;
_eventService = eventService;
_mapper = mapper;
_dataProcessingService = dataProcessingService;
}
/// <summary>
@@ -42,7 +52,7 @@ public class MqttManagementService : IMqttManagementService
/// <summary>
/// 异步创建一个新的MQTT服务器。
/// </summary>
public async Task<int> CreateMqttServerAsync(MqttServerDto mqttServerDto)
public async Task<MqttServerDto> CreateMqttServerAsync(MqttServerDto mqttServerDto)
{
var result = await _mqttAppService.CreateMqttServerAsync(mqttServerDto);
@@ -52,42 +62,165 @@ public class MqttManagementService : IMqttManagementService
mqttServerDto.Id = result; // 假设返回的ID是新创建的
if (_appDataStorageService.MqttServers.TryAdd(mqttServerDto.Id, mqttServerDto))
{
_eventService.RaiseMqttServerChanged(this, new MqttServerChangedEventArgs(ActionChangeType.Added, mqttServerDto));
_eventService.RaiseMqttServerChanged(
this, new MqttServerChangedEventArgs(ActionChangeType.Added, mqttServerDto));
}
}
return mqttServerDto;
}
/// <summary>
/// 异步更新一个已存在的MQTT服务器。
/// </summary>
public async Task<int> UpdateMqttServerAsync(MqttServerDto mqttServerDto)
{
return await UpdateMqttServersAsync(new List<MqttServerDto>() { mqttServerDto });
}
/// <summary>
/// 异步批量更新MQTT服务器。
/// </summary>
public async Task<int> UpdateMqttServersAsync(List<MqttServerDto> mqttServerDtos)
{
var result = await _mqttAppService.UpdateMqttServersAsync(mqttServerDtos);
// 批量更新成功后更新内存中的MQTT服务器
if (result > 0 && mqttServerDtos != null)
{
foreach (var mqttServerDto in mqttServerDtos)
{
if (_appDataStorageService.MqttServers.TryGetValue(mqttServerDto.Id, out var mMqttServerDto))
{
// 比较旧值和新值,确定哪个属性发生了变化
var changedProperties = GetChangedProperties(mMqttServerDto, mqttServerDto);
// 更新内存中的MQTT服务器
_mapper.Map(mqttServerDto, mMqttServerDto);
// 为每个发生变化的属性触发事件
foreach (var property in changedProperties)
{
_eventService.RaiseMqttServerChanged(
this, new MqttServerChangedEventArgs(ActionChangeType.Updated, mMqttServerDto, property));
}
// 如果没有任何属性发生变化,至少触发一次更新事件
if (changedProperties.Count == 0)
{
_eventService.RaiseMqttServerChanged(
this, new MqttServerChangedEventArgs(ActionChangeType.Updated, mMqttServerDto, MqttServerPropertyType.All));
}
}
else
{
// 如果内存中不存在该MQTT服务器则直接添加
_appDataStorageService.MqttServers.TryAdd(mqttServerDto.Id, mqttServerDto);
_eventService.RaiseMqttServerChanged(
this, new MqttServerChangedEventArgs(ActionChangeType.Added, mqttServerDto, MqttServerPropertyType.All));
}
}
}
return result;
}
/// <summary>
/// 异步更新一个已存在的MQTT服务器。
/// </summary>
public async Task UpdateMqttServerAsync(MqttServerDto mqttServerDto)
{
await _mqttAppService.UpdateMqttServerAsync(mqttServerDto);
// 更新成功后更新内存中的MQTT服务器
_appDataStorageService.MqttServers.AddOrUpdate(mqttServerDto.Id, mqttServerDto, (key, oldValue) => mqttServerDto);
_eventService.RaiseMqttServerChanged(this, new MqttServerChangedEventArgs(ActionChangeType.Updated, mqttServerDto));
}
/// <summary>
/// 异步删除一个MQTT服务器。
/// </summary>
public async Task DeleteMqttServerAsync(int id)
public async Task<bool> DeleteMqttServerAsync(int id)
{
var mqttServer = await _mqttAppService.GetMqttServerByIdAsync(id); // 获取MQTT服务器信息用于内存删除
var result = await _mqttAppService.DeleteMqttServerAsync(id);
var result = await _mqttAppService.DeleteMqttServerAsync(id) > 0;
// 删除成功后从内存中移除MQTT服务器
if (result>0)
if (result && mqttServer != null)
{
if (_appDataStorageService.MqttServers.TryRemove(id, out var mqttServerDto))
{
_eventService.RaiseMqttServerChanged(this, new MqttServerChangedEventArgs(ActionChangeType.Deleted, mqttServerDto));
_eventService.RaiseMqttServerChanged(
this, new MqttServerChangedEventArgs(ActionChangeType.Deleted, mqttServerDto));
}
}
return result;
}
/// <summary>
/// 异步批量删除MQTT服务器。
/// </summary>
public async Task<bool> DeleteMqttServersAsync(List<int> ids)
{
var result = await _mqttAppService.DeleteMqttServersAsync(ids);
// 批量删除成功后从内存中移除MQTT服务器
if (result && ids != null)
{
foreach (var id in ids)
{
if (_appDataStorageService.MqttServers.TryRemove(id, out var mqttServerDto))
{
_eventService.RaiseMqttServerChanged(
this, new MqttServerChangedEventArgs(ActionChangeType.Deleted, mqttServerDto));
}
}
}
return result;
}
/// <summary>
/// 获取发生变化的属性列表
/// </summary>
/// <param name="oldMqttServer">旧MQTT服务器值</param>
/// <param name="newMqttServer">新MQTT服务器值</param>
/// <returns>发生变化的属性列表</returns>
private List<MqttServerPropertyType> GetChangedProperties(MqttServerDto oldMqttServer, MqttServerDto newMqttServer)
{
var changedProperties = new List<MqttServerPropertyType>();
if (oldMqttServer.ServerName != newMqttServer.ServerName)
changedProperties.Add(MqttServerPropertyType.ServerName);
if (oldMqttServer.ServerUrl != newMqttServer.ServerUrl)
changedProperties.Add(MqttServerPropertyType.ServerUrl);
if (oldMqttServer.Port != newMqttServer.Port)
changedProperties.Add(MqttServerPropertyType.Port);
if (oldMqttServer.IsConnect != newMqttServer.IsConnect)
changedProperties.Add(MqttServerPropertyType.IsConnect);
if (oldMqttServer.Username != newMqttServer.Username)
changedProperties.Add(MqttServerPropertyType.Username);
if (oldMqttServer.Password != newMqttServer.Password)
changedProperties.Add(MqttServerPropertyType.Password);
if (oldMqttServer.IsActive != newMqttServer.IsActive)
changedProperties.Add(MqttServerPropertyType.IsActive);
if (oldMqttServer.SubscribeTopic != newMqttServer.SubscribeTopic)
changedProperties.Add(MqttServerPropertyType.SubscribeTopic);
if (oldMqttServer.PublishTopic != newMqttServer.PublishTopic)
changedProperties.Add(MqttServerPropertyType.PublishTopic);
if (oldMqttServer.ClientId != newMqttServer.ClientId)
changedProperties.Add(MqttServerPropertyType.ClientId);
if (oldMqttServer.MessageFormat != newMqttServer.MessageFormat)
changedProperties.Add(MqttServerPropertyType.MessageFormat);
if (oldMqttServer.MessageHeader != newMqttServer.MessageHeader)
changedProperties.Add(MqttServerPropertyType.MessageHeader);
if (oldMqttServer.MessageContent != newMqttServer.MessageContent)
changedProperties.Add(MqttServerPropertyType.MessageContent);
if (oldMqttServer.MessageFooter != newMqttServer.MessageFooter)
changedProperties.Add(MqttServerPropertyType.MessageFooter);
return changedProperties;
}
}

View File

@@ -23,6 +23,7 @@ public class DataEventService : IDataEventService
private readonly IMapper _mapper;
private readonly IDataStorageService _dataStorageService;
private readonly IEventService _eventService;
private readonly INotificationService _notificationService;
private readonly IAppDataCenterService _appDataCenterService;
private readonly IWPFDataService _wpfDataService;
private readonly ILogger<DataEventService> _logger;
@@ -33,6 +34,7 @@ public class DataEventService : IDataEventService
public DataEventService(IMapper mapper,
IDataStorageService dataStorageService,
IEventService eventService,
INotificationService notificationService,
IAppDataCenterService appDataCenterService,
IWPFDataService wpfDataService,
ILogger<DataEventService> logger)
@@ -40,6 +42,7 @@ public class DataEventService : IDataEventService
_mapper = mapper;
_dataStorageService = dataStorageService;
_eventService = eventService;
_notificationService = notificationService;
_appDataCenterService = appDataCenterService;
_wpfDataService = wpfDataService;
_logger = logger;
@@ -79,6 +82,7 @@ public class DataEventService : IDataEventService
break;
case MqttServerPropertyType.IsConnect:
mqttServerItem.IsConnect=e.MqttServer.IsConnect;
_notificationService.ShowSuccess($"MQTT服务器{mqttServerItem.ServerName},连接发生了变化,状态:{e.MqttServer.IsConnect}");
break;
case MqttServerPropertyType.Username:
break;

View File

@@ -1,16 +1,17 @@
<UserControl x:Class="DMS.WPF.Views.DevicesView"
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:hc="https://handyorg.github.io/handycontrol"
xmlns:localConverters="clr-namespace:DMS.WPF.Converters"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:vm="clr-namespace:DMS.WPF.ViewModels"
d:DataContext="{d:DesignInstance vm:DevicesViewModel}"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<UserControl
x:Class="DMS.WPF.Views.DevicesView"
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:hc="https://handyorg.github.io/handycontrol"
xmlns:localConverters="clr-namespace:DMS.WPF.Converters"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
xmlns:vm="clr-namespace:DMS.WPF.ViewModels"
d:DataContext="{d:DesignInstance vm:DevicesViewModel}"
d:DesignHeight="300"
d:DesignWidth="300"
mc:Ignorable="d">
<UserControl.Resources>
<localConverters:BooleanToBrushConverter x:Key="BooleanToBrushConverter" />
@@ -22,17 +23,19 @@
<!-- 设备项模板 -->
<DataTemplate x:Key="DeviceItemTemplate">
<Border Margin="5"
Padding="15"
Background="White"
BorderBrush="#E0E0E0"
BorderThickness="1"
CornerRadius="8">
<Border
Margin="5"
Padding="15"
Background="White"
BorderBrush="#E0E0E0"
BorderThickness="1"
CornerRadius="8">
<Border.Effect>
<DropShadowEffect BlurRadius="5"
Opacity="0.1"
ShadowDepth="2"
Color="#888888" />
<DropShadowEffect
BlurRadius="5"
Opacity="0.1"
ShadowDepth="2"
Color="#888888" />
</Border.Effect>
<Grid>
@@ -43,8 +46,7 @@
</Grid.RowDefinitions>
<!-- 设备基本信息区域 -->
<StackPanel Grid.Row="0"
Margin="0,0,0,15">
<StackPanel Grid.Row="0" Margin="0,0,0,15">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
@@ -53,71 +55,80 @@
<!-- 设备名称和描述 -->
<StackPanel Grid.Column="0">
<StackPanel Margin="0,0,0,5"
Orientation="Horizontal">
<ui:FontIcon Margin="0,0,8,0"
FontSize="20"
Foreground="{DynamicResource SystemAccentColorBrush}"
Glyph="&#xE975;" />
<TextBlock VerticalAlignment="Center"
FontSize="16"
FontWeight="SemiBold"
Text="{Binding Name}" />
<StackPanel Margin="0,0,0,5" Orientation="Horizontal">
<ui:FontIcon
Margin="0,0,8,0"
FontSize="20"
Foreground="{DynamicResource SystemAccentColorBrush}"
Glyph="&#xE975;" />
<TextBlock
VerticalAlignment="Center"
FontSize="16"
FontWeight="SemiBold"
Text="{Binding Name}" />
</StackPanel>
<TextBlock Margin="0,0,0,8"
FontSize="13"
Foreground="#666666"
Text="{Binding Description}"
TextWrapping="Wrap" />
<TextBlock
Margin="0,0,0,8"
FontSize="13"
Foreground="#666666"
Text="{Binding Description}"
TextWrapping="Wrap" />
<StackPanel Orientation="Horizontal">
<ui:FontIcon Margin="0,0,5,0"
FontSize="14"
Foreground="#888888"
Glyph="&#xE76C;" />
<TextBlock FontSize="12"
Foreground="#888888"
Text="{Binding DeviceType}" />
<ui:FontIcon
Margin="0,0,5,0"
FontSize="14"
Foreground="#888888"
Glyph="&#xE76C;" />
<TextBlock
FontSize="12"
Foreground="#888888"
Text="{Binding DeviceType}" />
<Rectangle Width="1"
Height="12"
Margin="10,0"
Fill="#CCCCCC" />
<Rectangle
Width="1"
Height="12"
Margin="10,0"
Fill="#CCCCCC" />
<ui:FontIcon Margin="10,0,5,0"
FontSize="14"
Foreground="#888888"
Glyph="&#xE8F4;" />
<TextBlock FontSize="12"
Foreground="#888888"
Text="{Binding IsRunning, Converter={StaticResource BoolToStringConverter}, ConverterParameter='已连接;未连接'}" />
<ui:FontIcon
Margin="10,0,5,0"
FontSize="14"
Foreground="#888888"
Glyph="&#xE8F4;" />
<TextBlock
FontSize="12"
Foreground="#888888"
Text="{Binding IsRunning, Converter={StaticResource BoolToStringConverter}, ConverterParameter='已连接;未连接'}" />
</StackPanel>
</StackPanel>
<!-- 状态指示器 -->
<StackPanel Grid.Column="1"
VerticalAlignment="Top"
Orientation="Horizontal">
<Border
Margin="0,0,10,0"
Background="{Binding IsRunning, Converter={StaticResource BoolToColorConverter}, ConverterParameter='Green;Red'}"
CornerRadius="8">
<StackPanel
Grid.Column="1"
VerticalAlignment="Top"
Orientation="Horizontal">
<Border
Margin="0,0,10,0"
Background="{Binding IsRunning, Converter={StaticResource BoolToColorConverter}, ConverterParameter='Green;Red'}"
CornerRadius="8">
<TextBlock
Margin="10 5"
Margin="10,5"
FontSize="14"
Foreground="White"
FontWeight="Bold"
Foreground="White"
Text="{Binding IsRunning, Converter={StaticResource BoolToStringConverter}, ConverterParameter='已连接;未连接'}" />
</Border>
<ToggleButton x:Name="ExpandToggleButton"
Background="Transparent"
BorderThickness="0"
Foreground="#666666"
IsChecked="True"
Style="{StaticResource DefaultToggleButtonStyle}"
ToolTip="展开/收起变量表">
<ToggleButton
x:Name="ExpandToggleButton"
Background="Transparent"
BorderThickness="0"
Foreground="#666666"
IsChecked="True"
Style="{StaticResource DefaultToggleButtonStyle}"
ToolTip="展开/收起变量表">
<ToggleButton.Content>
<StackPanel Orientation="Horizontal">
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.ChevronUp}" />
@@ -129,55 +140,58 @@
</StackPanel>
<!-- 变量表列表区域 -->
<StackPanel Grid.Row="1"
Margin="0,0,0,15">
<Border Background="#F8F8F8"
BorderBrush="#E0E0E0"
BorderThickness="1"
CornerRadius="6"
Visibility="{Binding ElementName=ExpandToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}">
<StackPanel Grid.Row="1" Margin="0,0,0,15">
<Border
Background="#F8F8F8"
BorderBrush="#E0E0E0"
BorderThickness="1"
CornerRadius="6"
Visibility="{Binding ElementName=ExpandToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}">
<StackPanel>
<!-- 变量表标题 -->
<Border Padding="12,8"
Background="#F0F0F0"
BorderBrush="#E0E0E0"
BorderThickness="0,0,0,1"
CornerRadius="6,6,0,0">
<Border
Padding="12,8"
Background="#F0F0F0"
BorderBrush="#E0E0E0"
BorderThickness="0,0,0,1"
CornerRadius="6,6,0,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
FontSize="14"
FontWeight="SemiBold"
Foreground="#333333"
Text="变量表列表" />
<TextBlock
Grid.Column="0"
FontSize="14"
FontWeight="SemiBold"
Foreground="#333333"
Text="变量表列表" />
<Button Grid.Column="1"
Width="28"
Height="28"
Padding="0"
Content="&#xE710;"
FontFamily="Segoe MDL2 Assets"
FontSize="14"
Style="{StaticResource DefaultButtonStyle}"
Command="{Binding DataContext.AddVariableTableCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
CommandParameter="{Binding}"
ToolTip="添加变量表" />
<Button
Grid.Column="1"
Width="28"
Height="28"
Padding="0"
Command="{Binding DataContext.AddVariableTableCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
CommandParameter="{Binding}"
Content="&#xE710;"
FontFamily="Segoe MDL2 Assets"
FontSize="14"
Style="{StaticResource DefaultButtonStyle}"
ToolTip="添加变量表" />
</Grid>
</Border>
<!-- 变量表列表 -->
<ItemsControl Margin="0"
ItemsSource="{Binding VariableTables}">
<ItemsControl Margin="0" ItemsSource="{Binding VariableTables}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Padding="12,10"
Background="White"
BorderBrush="#EEEEEE"
BorderThickness="0,0,0,1">
<Border
Padding="12,10"
Background="White"
BorderBrush="#EEEEEE"
BorderThickness="0,0,0,1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
@@ -185,63 +199,67 @@
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<StackPanel Margin="0,0,0,3"
Orientation="Horizontal">
<ui:FontIcon Margin="0,0,6,0"
FontSize="14"
Foreground="{DynamicResource SystemAccentColorBrush}"
Glyph="&#xE8EC;" />
<TextBlock FontSize="13"
FontWeight="SemiBold"
Text="{Binding Name}" />
<StackPanel Margin="0,0,0,3" Orientation="Horizontal">
<ui:FontIcon
Margin="0,0,6,0"
FontSize="14"
Foreground="{DynamicResource SystemAccentColorBrush}"
Glyph="&#xE8EC;" />
<TextBlock
FontSize="13"
FontWeight="SemiBold"
Text="{Binding Name}" />
</StackPanel>
<TextBlock MaxWidth="300"
FontSize="11"
Foreground="#888888"
Text="{Binding Description}"
TextWrapping="Wrap" />
<TextBlock
MaxWidth="300"
FontSize="11"
Foreground="#888888"
Text="{Binding Description}"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel Grid.Column="1"
Orientation="Horizontal">
<TextBlock Margin="0,0,5,0"
VerticalAlignment="Center"
FontSize="12"
Foreground="#666666"
Text="{Binding Variables.Count}" />
<TextBlock VerticalAlignment="Center"
FontSize="12"
Foreground="#888888"
Text="变量" />
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock
Margin="0,0,5,0"
VerticalAlignment="Center"
FontSize="12"
Foreground="#666666"
Text="{Binding Variables.Count}" />
<TextBlock
VerticalAlignment="Center"
FontSize="12"
Foreground="#888888"
Text="变量" />
<Button Width="26"
Height="26"
Margin="8,0,0,0"
Padding="0"
Style="{StaticResource DefaultButtonStyle}"
Command="{Binding DataContext.EditVariableTableCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
CommandParameter="{Binding}"
ToolTip="编辑变量表">
<Button
Width="26"
Height="26"
Margin="8,0,0,0"
Padding="0"
Command="{Binding DataContext.EditVariableTableCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
CommandParameter="{Binding}"
Style="{StaticResource DefaultButtonStyle}"
ToolTip="编辑变量表">
<Button.Content>
<StackPanel Orientation="Horizontal">
<ui:FontIcon
Icon="{x:Static ui:SegoeFluentIcons.Edit}" />
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Edit}" />
</StackPanel>
</Button.Content>
</Button>
<Button Width="26"
Height="26"
Margin="4,0,0,0"
Padding="0"
Content="&#xE74D;"
FontFamily="Segoe MDL2 Assets"
FontSize="14"
Style="{StaticResource DefaultButtonStyle}"
Command="{Binding DataContext.DeleteVariableTableCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
CommandParameter="{Binding}"
ToolTip="删除变量表" />
<Button
Width="26"
Height="26"
Margin="4,0,0,0"
Padding="0"
Command="{Binding DataContext.DeleteVariableTableCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
CommandParameter="{Binding}"
Content="&#xE74D;"
FontFamily="Segoe MDL2 Assets"
FontSize="14"
Style="{StaticResource DefaultButtonStyle}"
ToolTip="删除变量表" />
</StackPanel>
</Grid>
</Border>
@@ -250,31 +268,34 @@
</ItemsControl>
<!-- 空状态提示 -->
<TextBlock Padding="20,15"
HorizontalAlignment="Center"
FontSize="13"
Foreground="#AAAAAA"
Text="暂无变量表"
Visibility="{Binding VariableTables, Converter={StaticResource CountToVisibilityConverter}}" />
<TextBlock
Padding="20,15"
HorizontalAlignment="Center"
FontSize="13"
Foreground="#AAAAAA"
Text="暂无变量表"
Visibility="{Binding VariableTables, Converter={StaticResource CountToVisibilityConverter}}" />
</StackPanel>
</Border>
</StackPanel>
<!-- 操作按钮区域 -->
<StackPanel Grid.Row="2"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button Width="36"
Height="36"
Margin="0,0,8,0"
Padding="0"
Style="{StaticResource AccentButtonStyle}"
ToolTip="连接设备">
<StackPanel
Grid.Row="2"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button
Width="36"
Height="36"
Margin="0,0,8,0"
Padding="0"
Style="{StaticResource AccentButtonStyle}"
ToolTip="连接设备">
<!-- <Button.Effect> -->
<!-- <DropShadowEffect BlurRadius="3" -->
<!-- Opacity="0.2" -->
<!-- ShadowDepth="1" -->
<!-- Color="{DynamicResource SystemAccentColor}" /> -->
<!-- <DropShadowEffect BlurRadius="3" -->
<!-- Opacity="0.2" -->
<!-- ShadowDepth="1" -->
<!-- Color="{DynamicResource SystemAccentColor}" /> -->
<!-- </Button.Effect> -->
<Button.Content>
<StackPanel Orientation="Horizontal">
@@ -283,13 +304,14 @@
</Button.Content>
</Button>
<Button Width="36"
Height="36"
Margin="0,0,8,0"
Padding="0"
Command="{Binding DataContext.EditDeviceCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
CommandParameter="{Binding}"
ToolTip="编辑设备">
<Button
Width="36"
Height="36"
Margin="0,0,8,0"
Padding="0"
Command="{Binding DataContext.EditDeviceCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
CommandParameter="{Binding}"
ToolTip="编辑设备">
<Button.Content>
<StackPanel Orientation="Horizontal">
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Edit}" />
@@ -297,12 +319,13 @@
</Button.Content>
</Button>
<Button Width="36"
Height="36"
Margin="0,0,8,0"
Padding="0"
Style="{StaticResource DefaultButtonStyle}"
ToolTip="监控设备">
<Button
Width="36"
Height="36"
Margin="0,0,8,0"
Padding="0"
Style="{StaticResource DefaultButtonStyle}"
ToolTip="监控设备">
<Button.Content>
<StackPanel Orientation="Horizontal">
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.TVMonitor}" />
@@ -310,13 +333,14 @@
</Button.Content>
</Button>
<Button Width="36"
Height="36"
Padding="0"
Command="{Binding DataContext.DeleteDeviceCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
CommandParameter="{Binding}"
Style="{StaticResource ButtonDanger.Small}"
ToolTip="删除设备">
<Button
Width="36"
Height="36"
Padding="0"
Command="{Binding DataContext.DeleteDeviceCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
CommandParameter="{Binding}"
Style="{StaticResource ButtonDanger.Small}"
ToolTip="删除设备">
<Button.Content>
<StackPanel Orientation="Horizontal">
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Delete}" />
@@ -331,50 +355,47 @@
<StackPanel>
<!-- 操作菜单栏 -->
<ui:CommandBar DefaultLabelPosition="Right"
IsOpen="False">
<ui:CommandBar DefaultLabelPosition="Right" IsOpen="False">
<!-- 添加设备 -->
<ui:AppBarButton Command="{Binding AddDeviceCommand}"
Label="添加设备">
<ui:AppBarButton Command="{Binding AddDeviceCommand}" Label="添加设备">
<ui:AppBarButton.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Add}" />
</ui:AppBarButton.Icon>
</ui:AppBarButton>
<!-- 编辑设备 -->
<ui:AppBarButton Command="{Binding EditDeviceCommand}"
Label="编辑设备">
<ui:AppBarButton Command="{Binding EditDeviceCommand}" Label="编辑设备">
<ui:AppBarButton.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Edit}" />
</ui:AppBarButton.Icon>
</ui:AppBarButton>
<!-- 编辑设备 -->
<ui:AppBarButton Command="{Binding DeleteDeviceCommand}"
Label="删除设备">
<ui:AppBarButton Command="{Binding DeleteDeviceCommand}" Label="删除设备">
<ui:AppBarButton.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Delete}" />
</ui:AppBarButton.Icon>
</ui:AppBarButton>
<ui:AppBarButton x:Name="ShareButton"
Label="Share">
<ui:AppBarButton x:Name="ShareButton" Label="Share">
<ui:AppBarButton.Icon>
<ui:FontIcon Icon="{x:Static ui:SegoeFluentIcons.Share}" />
</ui:AppBarButton.Icon>
</ui:AppBarButton>
<ui:CommandBar.SecondaryCommands>
<ui:AppBarButton x:Name="SettingsButton"
Icon="Setting"
Label="Settings" />
<ui:AppBarButton
x:Name="SettingsButton"
Icon="Setting"
Label="Settings" />
</ui:CommandBar.SecondaryCommands>
</ui:CommandBar>
<ui:GridView x:Name="BasicGridView"
Margin="20"
IsItemClickEnabled="True"
ItemTemplate="{StaticResource DeviceItemTemplate}"
ItemsSource="{Binding Devices}"
SelectedItem="{Binding SelectedDevice}"
SelectionMode="Single">
<ui:GridView
x:Name="BasicGridView"
Margin="20"
IsItemClickEnabled="True"
ItemTemplate="{StaticResource DeviceItemTemplate}"
ItemsSource="{Binding Devices}"
SelectedItem="{Binding SelectedDevice}"
SelectionMode="Single">
<hc:Interaction.Triggers>
<hc:EventTrigger EventName="MouseDoubleClick">
<hc:InvokeCommandAction Command="{Binding NavigateToDetailCommand}" />