Phase 6: 一键多开 (工具组合) 功能
- 数据模型: ToolItem 新增 IsGroup(bool) + SubToolIds(List<string>) 字段
- 执行逻辑: ProcessExecutionService 改为 ExecuteAsync, 组合卡片遍历子工具逐一启动(500ms延迟), 孤儿ID跳过并打印警告
- 组合编辑: GroupEditViewModel + GroupEditWindow, 复选框列表勾选非组合工具
- 主界面: 标题栏新增 '+添加组合' 按钮(蓝色), 组合卡片右下角显示 📦 角标
- 右键菜单: 区分 '编辑工具' (普通) 和 '编辑组合' (IsGroup=true)
- 快捷键: HotKeyManager 适配 ExecuteAsync 异步调用
- 测试: 82 tests total (ProcessExecution 4->6, GroupEdit 5 new)
This commit is contained in:
@@ -81,6 +81,7 @@ public partial class App : System.Windows.Application
|
||||
services.AddSingleton<ViewModels.MainViewModel>();
|
||||
services.AddTransient<ViewModels.ToolEditViewModel>();
|
||||
services.AddTransient<ViewModels.CategoryEditViewModel>();
|
||||
services.AddTransient<ViewModels.GroupEditViewModel>();
|
||||
services.AddSingleton<MainWindow>();
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ public class HotKeyManager
|
||||
if (_hotkeyMap.TryGetValue(id, out var tool))
|
||||
{
|
||||
_logService.Info($"通过快捷键启动: {tool.Name}");
|
||||
_processService.Execute(tool);
|
||||
_ = _processService.ExecuteAsync(tool);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -47,4 +47,14 @@ public class ToolItem
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public bool IsValid { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 是否为组合卡片(一键多开)
|
||||
/// </summary>
|
||||
public bool IsGroup { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当 IsGroup 为 true 时,存储需批量启动的子工具 ID 列表
|
||||
/// </summary>
|
||||
public List<string> SubToolIds { get; set; } = new();
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@ namespace PersonalToolBox.Services;
|
||||
public interface IProcessExecutionService
|
||||
{
|
||||
/// <summary>
|
||||
/// 执行指定的工具项
|
||||
/// 执行指定的工具项(含组合批量启动)
|
||||
/// </summary>
|
||||
/// <param name="tool">要执行的工具项</param>
|
||||
void Execute(ToolItem tool);
|
||||
Task ExecuteAsync(ToolItem tool);
|
||||
}
|
||||
|
||||
@@ -5,17 +5,20 @@ namespace PersonalToolBox.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 进程执行服务,负责启动外部工具进程并处理异常
|
||||
/// 支持一键多开:当 IsGroup 为 true 时批量启动子工具
|
||||
/// </summary>
|
||||
public class ProcessExecutionService : IProcessExecutionService
|
||||
{
|
||||
private readonly ILogService _logService;
|
||||
private readonly IDataService _dataService;
|
||||
|
||||
public ProcessExecutionService(ILogService logService)
|
||||
public ProcessExecutionService(ILogService logService, IDataService dataService)
|
||||
{
|
||||
_logService = logService;
|
||||
_dataService = dataService;
|
||||
}
|
||||
|
||||
public void Execute(ToolItem tool)
|
||||
public async Task ExecuteAsync(ToolItem tool)
|
||||
{
|
||||
if (tool == null)
|
||||
{
|
||||
@@ -23,12 +26,63 @@ public class ProcessExecutionService : IProcessExecutionService
|
||||
return;
|
||||
}
|
||||
|
||||
// 组合卡片:遍历子工具列表逐一启动
|
||||
if (tool.IsGroup)
|
||||
{
|
||||
await ExecuteGroupAsync(tool);
|
||||
return;
|
||||
}
|
||||
|
||||
// 普通工具:直接启动
|
||||
if (!tool.IsValid)
|
||||
{
|
||||
_logService.Warning($"无法运行工具 \"{tool.Name}\",路径失效: {tool.ExecutablePath}");
|
||||
return;
|
||||
}
|
||||
|
||||
LaunchSingleTool(tool);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量启动组合中的所有子工具,每次间隔 500ms 防止系统卡顿
|
||||
/// </summary>
|
||||
private async Task ExecuteGroupAsync(ToolItem group)
|
||||
{
|
||||
var subTools = new List<ToolItem>();
|
||||
|
||||
foreach (var subId in group.SubToolIds)
|
||||
{
|
||||
var subTool = _dataService.Config.Tools.FirstOrDefault(t => t.Id == subId);
|
||||
if (subTool == null)
|
||||
{
|
||||
_logService.Warning($"组合启动跳过:找不到 ID 为 {subId} 的工具");
|
||||
continue;
|
||||
}
|
||||
subTools.Add(subTool);
|
||||
}
|
||||
|
||||
_logService.Info($"开始启动组合 \"{group.Name}\",共 {subTools.Count} 个工具");
|
||||
|
||||
foreach (var subTool in subTools)
|
||||
{
|
||||
if (!subTool.IsValid)
|
||||
{
|
||||
_logService.Warning($"组合启动跳过 \"{subTool.Name}\",路径失效: {subTool.ExecutablePath}");
|
||||
continue;
|
||||
}
|
||||
|
||||
LaunchSingleTool(subTool);
|
||||
await Task.Delay(500);
|
||||
}
|
||||
|
||||
_logService.Info($"组合 \"{group.Name}\" 启动完成");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动单个工具进程
|
||||
/// </summary>
|
||||
private void LaunchSingleTool(ToolItem tool)
|
||||
{
|
||||
try
|
||||
{
|
||||
var startInfo = new ProcessStartInfo
|
||||
|
||||
144
PersonalToolBox/ViewModels/GroupEditViewModel.cs
Normal file
144
PersonalToolBox/ViewModels/GroupEditViewModel.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using PersonalToolBox.Models;
|
||||
using PersonalToolBox.Services;
|
||||
|
||||
namespace PersonalToolBox.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// 组合编辑窗口 ViewModel,管理一键多开组合的创建与编辑
|
||||
/// </summary>
|
||||
public partial class GroupEditViewModel : ObservableObject
|
||||
{
|
||||
private readonly IDataService _dataService;
|
||||
private readonly ILogService _logService;
|
||||
private readonly ToolItem? _editingGroup;
|
||||
|
||||
// ──────── 可观察属性 ────────
|
||||
|
||||
[ObservableProperty]
|
||||
private string _windowTitle = "添加组合";
|
||||
|
||||
[ObservableProperty]
|
||||
private string _name = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _hotKey = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private Category? _selectedCategory;
|
||||
|
||||
/// <summary>
|
||||
/// 可供勾选的普通工具列表(IsGroup == false)
|
||||
/// </summary>
|
||||
public ObservableCollection<SelectableTool> AvailableTools { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 分类下拉列表
|
||||
/// </summary>
|
||||
public ObservableCollection<Category> Categories { get; } = new();
|
||||
|
||||
public Action<bool?>? CloseAction { get; set; }
|
||||
public bool Saved { get; private set; }
|
||||
|
||||
// ──────── 构造 ────────
|
||||
|
||||
public GroupEditViewModel(IDataService dataService, ILogService logService, ToolItem? groupToEdit = null)
|
||||
{
|
||||
_dataService = dataService;
|
||||
_logService = logService;
|
||||
_editingGroup = groupToEdit;
|
||||
|
||||
foreach (var cat in _dataService.Config.Categories)
|
||||
Categories.Add(cat);
|
||||
|
||||
// 加载所有普通工具(非组合)
|
||||
var selectedIds = _editingGroup?.SubToolIds ?? new List<string>();
|
||||
foreach (var tool in _dataService.Config.Tools.Where(t => !t.IsGroup))
|
||||
{
|
||||
AvailableTools.Add(new SelectableTool
|
||||
{
|
||||
Tool = tool,
|
||||
IsSelected = selectedIds.Contains(tool.Id)
|
||||
});
|
||||
}
|
||||
|
||||
if (groupToEdit != null)
|
||||
{
|
||||
WindowTitle = "编辑组合";
|
||||
Name = groupToEdit.Name;
|
||||
HotKey = groupToEdit.HotKey;
|
||||
SelectedCategory = Categories.FirstOrDefault(c => c.Id == groupToEdit.CategoryId);
|
||||
}
|
||||
}
|
||||
|
||||
// ──────── 命令 ────────
|
||||
|
||||
[RelayCommand]
|
||||
private void Save()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Name))
|
||||
{
|
||||
_logService.Warning("组合名称不能为空");
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedIds = AvailableTools
|
||||
.Where(t => t.IsSelected)
|
||||
.Select(t => t.Tool.Id)
|
||||
.ToList();
|
||||
|
||||
if (_editingGroup != null)
|
||||
{
|
||||
_editingGroup.Name = Name.Trim();
|
||||
_editingGroup.HotKey = HotKey.Trim();
|
||||
_editingGroup.CategoryId = SelectedCategory?.Id ?? string.Empty;
|
||||
_editingGroup.SubToolIds = selectedIds;
|
||||
_editingGroup.IsValid = true;
|
||||
_logService.Info($"已更新组合: {Name.Trim()}(包含 {selectedIds.Count} 个工具)");
|
||||
}
|
||||
else
|
||||
{
|
||||
_dataService.Config.Tools.Add(new ToolItem
|
||||
{
|
||||
Name = Name.Trim(),
|
||||
HotKey = HotKey.Trim(),
|
||||
CategoryId = SelectedCategory?.Id ?? string.Empty,
|
||||
IsGroup = true,
|
||||
SubToolIds = selectedIds,
|
||||
IsValid = true
|
||||
});
|
||||
_logService.Info($"已添加组合: {Name.Trim()}(包含 {selectedIds.Count} 个工具)");
|
||||
}
|
||||
|
||||
_dataService.Save();
|
||||
Saved = true;
|
||||
CloseAction?.Invoke(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logService.Error($"保存组合失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Cancel()
|
||||
{
|
||||
CloseAction?.Invoke(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 可勾选的工具项(用于 CheckBox 列表绑定)
|
||||
/// </summary>
|
||||
public partial class SelectableTool : ObservableObject
|
||||
{
|
||||
public ToolItem Tool { get; set; } = null!;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isSelected;
|
||||
}
|
||||
@@ -143,10 +143,10 @@ public partial class MainViewModel : ObservableObject
|
||||
/// 执行工具(双击卡片或右键菜单)
|
||||
/// </summary>
|
||||
[RelayCommand]
|
||||
private void ExecuteTool(ToolItem? tool)
|
||||
private async Task ExecuteTool(ToolItem? tool)
|
||||
{
|
||||
if (tool == null) return;
|
||||
_processService.Execute(tool);
|
||||
await _processService.ExecuteAsync(tool);
|
||||
}
|
||||
|
||||
// ───────────────────────────── 分类管理命令 ─────────────────────────────
|
||||
@@ -206,6 +206,38 @@ public partial class MainViewModel : ObservableObject
|
||||
RefreshData();
|
||||
}
|
||||
|
||||
// ───────────────────────────── 组合管理命令 ─────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 添加组合
|
||||
/// </summary>
|
||||
[RelayCommand]
|
||||
private void AddGroup()
|
||||
{
|
||||
var vm = _serviceProvider.GetRequiredService<GroupEditViewModel>();
|
||||
var window = new GroupEditWindow(vm);
|
||||
window.ShowDialog();
|
||||
|
||||
if (vm.Saved) RefreshData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 编辑组合
|
||||
/// </summary>
|
||||
[RelayCommand]
|
||||
private void EditGroup(ToolItem tool)
|
||||
{
|
||||
if (tool == null || !tool.IsGroup) return;
|
||||
|
||||
var dataService = _serviceProvider.GetRequiredService<IDataService>();
|
||||
var logService = _serviceProvider.GetRequiredService<ILogService>();
|
||||
var editVm = new GroupEditViewModel(dataService, logService, tool);
|
||||
var window = new GroupEditWindow(editVm);
|
||||
window.ShowDialog();
|
||||
|
||||
if (editVm.Saved) RefreshData();
|
||||
}
|
||||
|
||||
// ───────────────────────────── 数据刷新 ─────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
|
||||
100
PersonalToolBox/Views/GroupEditWindow.xaml
Normal file
100
PersonalToolBox/Views/GroupEditWindow.xaml
Normal file
@@ -0,0 +1,100 @@
|
||||
<Window x:Class="PersonalToolBox.Views.GroupEditWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="{Binding WindowTitle}" Height="500" Width="500"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
ResizeMode="NoResize"
|
||||
Background="{DynamicResource Theme.Background}">
|
||||
|
||||
<Grid Margin="16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="70"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 组合名称 -->
|
||||
<TextBlock Grid.Row="0" Grid.Column="0"
|
||||
Text="名称:" Foreground="{DynamicResource Theme.Foreground}"
|
||||
VerticalAlignment="Center" Margin="0,0,0,10"/>
|
||||
<TextBox Grid.Row="0" Grid.Column="1"
|
||||
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
|
||||
Background="{DynamicResource Theme.InputBackground}"
|
||||
Foreground="{DynamicResource Theme.Foreground}"
|
||||
BorderBrush="{DynamicResource Theme.InputBorder}"
|
||||
Height="28" Margin="0,0,0,10" Padding="6,0"/>
|
||||
|
||||
<!-- 所属分类 -->
|
||||
<TextBlock Grid.Row="1" Grid.Column="0"
|
||||
Text="分类:" Foreground="{DynamicResource Theme.Foreground}"
|
||||
VerticalAlignment="Center" Margin="0,0,0,10"/>
|
||||
<ComboBox Grid.Row="1" Grid.Column="1"
|
||||
ItemsSource="{Binding Categories}"
|
||||
SelectedItem="{Binding SelectedCategory}"
|
||||
DisplayMemberPath="Name"
|
||||
Height="28" Margin="0,0,0,10" Padding="4,0"/>
|
||||
|
||||
<!-- 快捷键 -->
|
||||
<TextBlock Grid.Row="2" Grid.Column="0"
|
||||
Text="快捷键:" Foreground="{DynamicResource Theme.Foreground}"
|
||||
VerticalAlignment="Center" Margin="0,0,0,10"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1"
|
||||
Text="{Binding HotKey, UpdateSourceTrigger=PropertyChanged}"
|
||||
Background="{DynamicResource Theme.InputBackground}"
|
||||
Foreground="{DynamicResource Theme.Foreground}"
|
||||
BorderBrush="{DynamicResource Theme.InputBorder}"
|
||||
Height="28" Margin="0,0,0,10" Padding="6,0"/>
|
||||
|
||||
<!-- 子工具选择 -->
|
||||
<TextBlock Grid.Row="3" Grid.Column="0"
|
||||
Text="包含工具:" Foreground="{DynamicResource Theme.Foreground}"
|
||||
VerticalAlignment="Top" Margin="0,4,0,0"/>
|
||||
<Border Grid.Row="3" Grid.Column="1"
|
||||
Background="{DynamicResource Theme.InputBackground}"
|
||||
BorderBrush="{DynamicResource Theme.InputBorder}"
|
||||
BorderThickness="1"
|
||||
Margin="0,0,0,10">
|
||||
<ListBox ItemsSource="{Binding AvailableTools}"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
MaxHeight="240">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<CheckBox IsChecked="{Binding IsSelected}"
|
||||
Content="{Binding Tool.Name}"
|
||||
Foreground="{DynamicResource Theme.Foreground}"
|
||||
Margin="4,2"/>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Border>
|
||||
|
||||
<!-- 按钮 -->
|
||||
<StackPanel Grid.Row="5" Grid.Column="1"
|
||||
Orientation="Horizontal" HorizontalAlignment="Right"
|
||||
Margin="0,10,0,0">
|
||||
<Button Content="保存"
|
||||
Command="{Binding SaveCommand}"
|
||||
Background="{DynamicResource Theme.ButtonBackground}"
|
||||
Foreground="{DynamicResource Theme.ButtonForeground}"
|
||||
BorderThickness="0"
|
||||
Width="80" Height="30" FontSize="13"
|
||||
Cursor="Hand" Margin="0,0,10,0"/>
|
||||
<Button Content="取消"
|
||||
Command="{Binding CancelCommand}"
|
||||
Background="{DynamicResource Theme.CardBackground}"
|
||||
Foreground="{DynamicResource Theme.Foreground}"
|
||||
BorderBrush="{DynamicResource Theme.CardBorder}"
|
||||
BorderThickness="1"
|
||||
Width="80" Height="30" FontSize="13"
|
||||
Cursor="Hand"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
17
PersonalToolBox/Views/GroupEditWindow.xaml.cs
Normal file
17
PersonalToolBox/Views/GroupEditWindow.xaml.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Windows;
|
||||
using PersonalToolBox.ViewModels;
|
||||
|
||||
namespace PersonalToolBox.Views;
|
||||
|
||||
public partial class GroupEditWindow : Window
|
||||
{
|
||||
public GroupEditWindow(GroupEditViewModel viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
viewModel.CloseAction = (result) => { DialogResult = result; };
|
||||
|
||||
Owner = Application.Current.MainWindow;
|
||||
DataContext = viewModel;
|
||||
}
|
||||
}
|
||||
@@ -38,17 +38,59 @@
|
||||
<MenuItem Header="运行"
|
||||
Command="{Binding PlacementTarget.Tag.ExecuteToolCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
|
||||
CommandParameter="{Binding}"/>
|
||||
<MenuItem Header="编辑"
|
||||
<MenuItem Header="编辑工具"
|
||||
Command="{Binding PlacementTarget.Tag.EditToolCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
|
||||
CommandParameter="{Binding}"/>
|
||||
CommandParameter="{Binding}">
|
||||
<MenuItem.Style>
|
||||
<Style TargetType="MenuItem">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsGroup}" Value="True">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</MenuItem.Style>
|
||||
</MenuItem>
|
||||
<MenuItem Header="编辑组合"
|
||||
Command="{Binding PlacementTarget.Tag.EditGroupCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
|
||||
CommandParameter="{Binding}">
|
||||
<MenuItem.Style>
|
||||
<Style TargetType="MenuItem">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsGroup}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</MenuItem.Style>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</Border.ContextMenu>
|
||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||
<TextBlock Text="{Binding Name, Converter={StaticResource FirstCharConverter}}"
|
||||
FontSize="28"
|
||||
Foreground="{DynamicResource Theme.Accent}"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0,0,0,6"/>
|
||||
<Grid HorizontalAlignment="Center" Margin="0,0,0,4">
|
||||
<TextBlock Text="{Binding Name, Converter={StaticResource FirstCharConverter}}"
|
||||
FontSize="28"
|
||||
Foreground="{DynamicResource Theme.Accent}"
|
||||
HorizontalAlignment="Center"/>
|
||||
<!-- 组合角标 -->
|
||||
<TextBlock Text="📦" FontSize="11"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="0,0,-12,-4">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Visibility" Value="Collapsed"/>
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsGroup}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
<TextBlock Text="{Binding Name}"
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource Theme.Foreground}"
|
||||
@@ -214,6 +256,7 @@
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="100"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox Grid.Column="0"
|
||||
@@ -236,6 +279,23 @@
|
||||
BorderThickness="0"
|
||||
FontSize="12"
|
||||
Height="32"
|
||||
Cursor="Hand"
|
||||
Margin="0,0,6,0">
|
||||
<Button.Resources>
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="CornerRadius" Value="4"/>
|
||||
</Style>
|
||||
</Button.Resources>
|
||||
</Button>
|
||||
|
||||
<Button Grid.Column="2"
|
||||
Content="+ 添加组合"
|
||||
Command="{Binding AddGroupCommand}"
|
||||
Background="{DynamicResource Theme.Accent}"
|
||||
Foreground="{DynamicResource Theme.ButtonForeground}"
|
||||
BorderThickness="0"
|
||||
FontSize="12"
|
||||
Height="32"
|
||||
Cursor="Hand">
|
||||
<Button.Resources>
|
||||
<Style TargetType="Border">
|
||||
|
||||
Reference in New Issue
Block a user