Files
AddinManager/AddInManager/Wpf/MainWindow.xaml.cs
2026-01-02 11:14:44 +08:00

1302 lines
46 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.Win32;
namespace AddInManager.Wpf
{
public partial class MainWindow : Window
{
private readonly AIM m_aim;
private List<TreeViewItem> m_allCommandItems; // 存储所有命令项用于搜索
public MainWindow(AIM aim)
{
aim.AddinManager.Commands?.AddinDict?.Clear();
aim.AddinManager.Applications?.AddinDict?.Clear();
aim.AddinManager.ReadAddinsFromAimIni();
InitializeComponent();
m_aim = aim;
Title = Properties.Resources.AppName;
Loaded += MainWindow_Loaded;
m_allCommandItems = new List<TreeViewItem>();
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
CommandsTreeView_RefreshData();
ApplicationsTreeView_RefreshData();
DisableControl();
removeButton.IsEnabled = false;
if (searchTextBox.Template.FindName("PART_ClearButton", searchTextBox) is Button clearButton)
{
clearButton.Click += ClearSearchButton_Click;
}
if (commandsTreeView.Items.Count > 0)
{
var firstItem = commandsTreeView.Items[0] as TreeViewItem;
if (firstItem != null)
{
firstItem.IsSelected = true;
firstItem.BringIntoView();
firstItem.Focus(); // 确保获得焦点
}
}
}
#region
#region
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var searchText = searchTextBox.Text?.Trim().ToLower();
if (string.IsNullOrEmpty(searchText))
{
// 如果搜索框为空,恢复所有项的初始状态
RestoreAllItems();
}
else
{
// 执行搜索过滤
FilterCommandsTreeView(searchText);
}
}
private void ClearSearchButton_Click(object sender, RoutedEventArgs e)
{
searchTextBox.Clear();
searchTextBox.Focus();
RestoreAllItems();
}
// (重写) 核心过滤逻辑
private void FilterCommandsTreeView(string searchText)
{
foreach (TreeViewItem parentItem in commandsTreeView.Items)
{
var parentShouldBeVisible = false;
// 1. 检查父节点自身是否匹配
var parentText = GetHeaderText(parentItem);
var parentMatches = !string.IsNullOrEmpty(parentText) && parentText.ToLower().Contains(searchText);
// 2. 遍历所有子节点
foreach (TreeViewItem childItem in parentItem.Items)
{
var childText = GetHeaderText(childItem);
var childMatches = !string.IsNullOrEmpty(childText) && childText.ToLower().Contains(searchText);
// 如果子节点匹配,或者父节点匹配,则该子节点可见
if (childMatches || parentMatches)
{
childItem.Visibility = Visibility.Visible;
parentShouldBeVisible = true; // 只要有任何一个子节点可见,父节点就必须可见
}
else
{
childItem.Visibility = Visibility.Collapsed;
}
}
// 3. 根据自身匹配或子节点可见性,来决定父节点的最终状态
if (parentShouldBeVisible || parentMatches)
{
parentItem.Visibility = Visibility.Visible;
parentItem.IsExpanded = true; // 展开以显示匹配的子项
}
else
{
parentItem.Visibility = Visibility.Collapsed;
}
}
}
// (重写) 恢复所有项的可见性
private void RestoreAllItems()
{
foreach (TreeViewItem parentItem in commandsTreeView.Items)
{
parentItem.Visibility = Visibility.Visible;
// 注意:这里我们不再强制展开所有项,除非您希望如此。
// 如果需要恢复时全部展开,可以取消下面这行的注释。
// parentItem.IsExpanded = true;
foreach (TreeViewItem childItem in parentItem.Items)
{
childItem.Visibility = Visibility.Visible;
}
}
}
// (新增) 辅助方法用于从Header(StackPanel)中获取真实的文本
private string GetHeaderText(TreeViewItem item)
{
if (item?.Header is StackPanel stackPanel)
{
// 遍历StackPanel中的所有子元素
foreach (var child in stackPanel.Children)
{
// 找到TextBlock并返回其文本
if (child is TextBlock textBlock)
{
return textBlock.Text;
}
}
}
// 如果Header不是预期的格式返回空字符串
return string.Empty;
}
#endregion
#endregion
#region
private void SelectAllButton_Click(object sender, RoutedEventArgs e)
{
SetAllItemsSelection(commandsTreeView, true);
}
private void SelectNoneButton_Click(object sender, RoutedEventArgs e)
{
SetAllItemsSelection(commandsTreeView, false);
}
private void ExpandAllButton_Click(object sender, RoutedEventArgs e)
{
SetAllItemsExpanded(commandsTreeView, true);
}
private void CollapseAllButton_Click(object sender, RoutedEventArgs e)
{
SetAllItemsExpanded(commandsTreeView, false);
}
private void AppSelectAllButton_Click(object sender, RoutedEventArgs e)
{
SetAllItemsSelection(applicationsTreeView, true);
}
private void AppSelectNoneButton_Click(object sender, RoutedEventArgs e)
{
SetAllItemsSelection(applicationsTreeView, false);
}
private void AppExpandAllButton_Click(object sender, RoutedEventArgs e)
{
SetAllItemsExpanded(applicationsTreeView, true);
}
private void AppCollapseAllButton_Click(object sender, RoutedEventArgs e)
{
SetAllItemsExpanded(applicationsTreeView, false);
}
private void SetAllItemsExpanded(TreeView treeView, bool isExpanded)
{
foreach (TreeViewItem item in treeView.Items)
{
SetTreeViewItemExpanded(item, isExpanded);
}
}
private void SetTreeViewItemExpanded(TreeViewItem item, bool isExpanded)
{
if (item == null) return;
item.IsExpanded = isExpanded;
// 递归处理子项
foreach (TreeViewItem childItem in item.Items)
{
SetTreeViewItemExpanded(childItem, isExpanded);
}
}
private void SetAllItemsSelection(TreeView treeView, bool isSelected)
{
foreach (TreeViewItem parentItem in treeView.Items)
{
if (parentItem.Visibility == Visibility.Visible)
{
// 设置父节点CheckBox
SetItemCheckBox(parentItem, isSelected);
if (parentItem.Tag is Addin addin)
{
addin.Save = isSelected;
}
foreach (TreeViewItem childItem in parentItem.Items)
{
if (childItem.Visibility == Visibility.Visible)
{
// 设置子节点CheckBox
SetItemCheckBox(childItem, isSelected);
if (childItem.Tag is AddinItem addinItem)
{
addinItem.Save = isSelected;
}
}
}
}
}
// 保存更改
m_aim.AddinManager.SaveToAimIni();
ShowStatusLabel(isSelected ? "已选择所有可见项目" : "已取消选择所有项目");
}
private void InvertAllItemsSelection()
{
foreach (TreeViewItem parentItem in commandsTreeView.Items)
{
if (parentItem.Visibility == Visibility.Visible)
{
// 反转父节点CheckBox
var currentState = GetItemCheckBoxState(parentItem);
SetItemCheckBox(parentItem, !currentState);
if (parentItem.Tag is Addin addin)
{
addin.Save = !addin.Save;
}
foreach (TreeViewItem childItem in parentItem.Items)
{
if (childItem.Visibility == Visibility.Visible)
{
// 反转子节点CheckBox
var childCurrentState = GetItemCheckBoxState(childItem);
SetItemCheckBox(childItem, !childCurrentState);
if (childItem.Tag is AddinItem addinItem)
{
addinItem.Save = !addinItem.Save;
}
}
}
}
}
// 保存更改
m_aim.AddinManager.SaveToAimIni();
ShowStatusLabel("已反转所有可见项目的选择状态");
}
private void SetItemCheckBox(TreeViewItem item, bool isChecked)
{
var checkBox = FindCheckBoxInTreeViewItem(item);
if (checkBox != null)
{
checkBox.IsChecked = isChecked;
}
}
private bool GetItemCheckBoxState(TreeViewItem item)
{
var checkBox = FindCheckBoxInTreeViewItem(item);
return checkBox?.IsChecked == true;
}
private CheckBox FindCheckBoxInTreeViewItem(TreeViewItem item)
{
if (item?.Header is StackPanel stackPanel)
{
foreach (var child in stackPanel.Children)
{
if (child is CheckBox checkBox)
{
return checkBox;
}
}
}
return null;
}
private void TreeViewCheckBox_Changed(object sender, RoutedEventArgs e)
{
if (!(sender is CheckBox checkBox)) return;
var item = FindTreeViewItemFromCheckBox(checkBox);
if (item?.Tag == null) return;
// 区分是父节点还是子节点
if (item.Tag is Addin addin)
{
// --- 父节点逻辑 ---
var newState = checkBox.IsChecked;
// 仅当父节点被用户明确点击为“选中”或“未选中”时,才更新所有子节点
// 如果状态变为 null不确定则不执行任何操作因为它是由子节点更新引起的
if (newState.HasValue)
{
var isChecked = newState.Value;
addin.Save = isChecked;
UpdateChildrenCheckBoxes(item, isChecked);
}
else
{
// 当状态为不确定时,我们认为它仍然有需要保存的项
addin.Save = true;
}
}
else if (item.Tag is AddinItem addinItem)
{
// --- 子节点逻辑 ---
var isChecked = checkBox.IsChecked == true;
addinItem.Save = isChecked;
// 更新父节点的状态
UpdateParentCheckBoxState(item);
}
// 保存更改到配置文件
m_aim.AddinManager.SaveToAimIni();
}
private void UpdateChildrenCheckBoxes(TreeViewItem parentItem, bool isChecked)
{
// 遍历所有子项并更新它们的状态
foreach (TreeViewItem childItem in parentItem.Items)
{
if (childItem.Tag is AddinItem addinItem)
{
// 更新数据模型
addinItem.Save = isChecked;
// 更新UICheckBox
var childCheckBox = FindCheckBoxInTreeViewItem(childItem);
if (childCheckBox != null)
{
// 临时移除事件处理程序,防止无限递归
childCheckBox.Checked -= TreeViewCheckBox_Changed;
childCheckBox.Unchecked -= TreeViewCheckBox_Changed;
childCheckBox.IsChecked = isChecked;
// 重新附加事件处理程序
childCheckBox.Checked += TreeViewCheckBox_Changed;
childCheckBox.Unchecked += TreeViewCheckBox_Changed;
}
}
}
}
private void UpdateParentCheckBoxState(TreeViewItem childItem)
{
if (childItem.Parent is TreeViewItem parentItem)
{
var allChecked = true;
var anyChecked = false;
// 检查所有子节点的状态
foreach (TreeViewItem sibling in parentItem.Items)
{
var siblingCheckBox = FindCheckBoxInTreeViewItem(sibling);
if (siblingCheckBox != null)
{
var isChecked = siblingCheckBox.IsChecked == true;
if (isChecked)
{
anyChecked = true;
}
else
{
allChecked = false;
}
}
}
// 更新父节点CheckBox状态
var parentCheckBox = FindCheckBoxInTreeViewItem(parentItem);
if (parentCheckBox != null)
{
// 临时移除事件处理,避免递归调用
parentCheckBox.Checked -= TreeViewCheckBox_Changed;
parentCheckBox.Unchecked -= TreeViewCheckBox_Changed;
if (allChecked)
{
parentCheckBox.IsChecked = true;
}
else if (anyChecked)
{
parentCheckBox.IsChecked = null; // 部分选中状态
}
else
{
parentCheckBox.IsChecked = false;
}
// 重新添加事件处理
parentCheckBox.Checked += TreeViewCheckBox_Changed;
parentCheckBox.Unchecked += TreeViewCheckBox_Changed;
}
// 更新父节点数据模型
if (parentItem.Tag is Addin parentAddin)
{
parentAddin.Save = allChecked || anyChecked;
}
}
}
#endregion
private void LoadButton_Click(object sender, RoutedEventArgs e)
{
var openFileDialog = new OpenFileDialog
{
Filter = Properties.Resources.LoadFileFilter
};
if (openFileDialog.ShowDialog() != true)
{
ShowStatusError(Properties.Resources.LoadCancelled);
return;
}
var fileName = openFileDialog.FileName;
var addinType = m_aim.AddinManager.LoadAddin(fileName);
if (addinType == AddinType.Invalid)
{
ShowStatusError(Properties.Resources.LoadFailed + fileName);
return;
}
ShowStatusLabel(Properties.Resources.LoadSucceed + fileName);
m_aim.AddinManager.SaveToAimIni();
CommandsTreeView_RefreshData();
ApplicationsTreeView_RefreshData();
switch (addinType)
{
case AddinType.Command:
case AddinType.Mixed:
externalToolsTabControl.SelectedItem = commandsTabPage;
commandsTreeView.Focus();
break;
case AddinType.Application:
externalToolsTabControl.SelectedItem = applicationsTabPage;
applicationsTreeView.Focus();
break;
}
notesTextBox.Text = string.Empty;
RemoveButton_RefreshData();
}
private void CommandsTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var item = e.NewValue as TreeViewItem;
if (item?.Tag == null)
{
m_aim.ActiveCmd = null;
m_aim.ActiveCmdItem = null;
RefreshData();
return;
}
if (item.Tag is Addin addin)
{
m_aim.ActiveCmd = addin;
m_aim.ActiveCmdItem = null;
notesTextBox.Text = m_aim.ActiveCmd.FilePath;
}
else if (item.Tag is AddinItem addinItem && item.Parent is TreeViewItem parentItem)
{
if (parentItem.Tag is Addin parentAddin)
{
m_aim.ActiveCmd = parentAddin;
m_aim.ActiveCmdItem = addinItem;
notesTextBox.Text = m_aim.ActiveCmd.FilePath;
}
}
RefreshData();
RemoveButton_RefreshData();
}
private void ApplicationsTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
var item = e.NewValue as TreeViewItem;
if (item?.Tag == null)
{
m_aim.ActiveApp = null;
m_aim.ActiveAppItem = null;
DisableControl();
RemoveButton_RefreshData();
return;
}
if (item.Tag is Addin addin)
{
m_aim.ActiveApp = addin;
m_aim.ActiveAppItem = null;
notesTextBox.Text = m_aim.ActiveApp.FilePath;
}
else if (item.Tag is AddinItem addinItem && item.Parent is TreeViewItem parentItem)
{
if (parentItem.Tag is Addin parentAddin)
{
m_aim.ActiveApp = parentAddin;
m_aim.ActiveAppItem = addinItem;
notesTextBox.Text = m_aim.ActiveApp.FilePath;
}
}
DisableControl();
RemoveButton_RefreshData();
}
private void ApplicationsTreeView_MouseUp(object sender, MouseButtonEventArgs e)
{
RemoveButton_RefreshData();
}
private void ApplicationsTreeView_LostFocus(object sender, RoutedEventArgs e)
{
// 检查焦点是否转移到了removeButton
var focusedElement = Keyboard.FocusedElement as FrameworkElement;
if (focusedElement != removeButton)
{
DisableControl();
removeButton.IsEnabled = false;
}
}
private void DisableControl()
{
nametextBox.Text = "";
descriptionTextBox.Text = "";
nametextBox.IsEnabled = false;
descriptionTextBox.IsEnabled = false;
runButton.IsEnabled = false;
}
private void ExternalToolsTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (externalToolsTabControl.SelectedIndex == 1)
{
DisableControl();
applicationsTreeView.Focus();
RemoveButton_RefreshData();
return;
}
commandsTreeView.Focus();
RefreshData();
RemoveButton_RefreshData(); // 添加这行
}
private void RefreshData()
{
var selectedItem = commandsTreeView.SelectedItem as TreeViewItem;
if (selectedItem != null && !HasChildren(selectedItem))
{
if (m_aim.ActiveCmdItem != null)
{
nametextBox.Text = m_aim.ActiveCmdItem.Name;
descriptionTextBox.Text = m_aim.ActiveCmdItem.Description;
}
nametextBox.IsEnabled = true;
descriptionTextBox.IsEnabled = true;
runButton.IsEnabled = true;
}
else
{
DisableControl();
}
RemoveButton_RefreshData();
}
private bool HasChildren(TreeViewItem item)
{
return item.Items.Count > 0;
}
private void RunButton_Click(object sender, RoutedEventArgs e)
{
Run();
}
private void Run()
{
try
{
if (m_aim.ActiveCmdItem == null)
{
ShowStatusError("没有选中可执行的命令");
return;
}
// 设置对话框结果为成功这样AIM会知道用户选择了要执行命令
DialogResult = true;
Close();
}
catch (Exception ex)
{
ShowStatusError($"准备执行命令时发生错误: {ex.Message}");
}
}
// (重写并修复) 移除按钮点击事件
private void RemoveButton_Click(object sender, RoutedEventArgs e)
{
TreeView activeTreeView;
if (externalToolsTabControl.SelectedIndex == 0)
{
activeTreeView = commandsTreeView;
}
else if (externalToolsTabControl.SelectedIndex == 1)
{
activeTreeView = applicationsTreeView;
}
else
{
return;
}
if (!(activeTreeView.SelectedItem is TreeViewItem selectedItem)) return;
// 1. 记录原始索引
var originalIndex = -1;
TreeViewItem parentItem = null;
var isChildNode = false;
// 判断是子节点还是父节点
var parent = VisualTreeHelper.GetParent(selectedItem);
while (parent != null && !(parent is TreeView))
{
if (parent is TreeViewItem item)
{
parentItem = item;
isChildNode = true;
break;
}
parent = VisualTreeHelper.GetParent(parent);
}
if (isChildNode) // 如果是子节点
{
originalIndex = parentItem.Items.IndexOf(selectedItem);
}
else // 如果是父节点
{
originalIndex = activeTreeView.Items.IndexOf(selectedItem);
}
// 2. 执行数据移除
if (externalToolsTabControl.SelectedIndex == 0)
{
m_aim.AddinManager.Commands.RemoveAddIn(m_aim.ActiveCmd);
m_aim.ActiveCmd = null;
m_aim.ActiveCmdItem = null;
}
else
{
m_aim.AddinManager.Applications.RemoveAddIn(m_aim.ActiveApp);
m_aim.ActiveApp = null;
m_aim.ActiveAppItem = null;
}
m_aim.AddinManager.SaveToAimIni();
// 3. 刷新UI
CommandsTreeView_RefreshData();
ApplicationsTreeView_RefreshData();
// 4. 计算新的安全索引并选中
if (activeTreeView.Items.Count > 0)
{
// 简单处理:总是尝试选中一个有效的项
var newIndex = Math.Max(0, Math.Min(originalIndex, activeTreeView.Items.Count - 1));
if (activeTreeView.Items[newIndex] is TreeViewItem itemToSelect)
{
itemToSelect.IsSelected = true;
itemToSelect.BringIntoView();
activeTreeView.Focus();
}
}
else
{
// 如果列表空了,清空备注
notesTextBox.Text = string.Empty;
}
RemoveButton_RefreshData();
}
private void RemoveButton_LostFocus(object sender, RoutedEventArgs e)
{
RemoveButton_RefreshData();
}
private void RemoveButton_RefreshData()
{
// 检查当前活动的标签页
if (externalToolsTabControl.SelectedIndex == 0) // Commands tab
{
// 检查是否有选中的项目
if (commandsTreeView.SelectedItem != null &&
(m_aim.ActiveCmd != null || m_aim.ActiveCmdItem != null))
{
removeButton.IsEnabled = true;
if (m_aim.ActiveCmd != null)
{
notesTextBox.Text = m_aim.ActiveCmd.FilePath;
}
}
else
{
removeButton.IsEnabled = false;
}
}
else if (externalToolsTabControl.SelectedIndex == 1) // Applications tab
{
// 检查是否有选中的项目
if (applicationsTreeView.SelectedItem != null &&
(m_aim.ActiveApp != null || m_aim.ActiveAppItem != null))
{
removeButton.IsEnabled = true;
if (m_aim.ActiveApp != null)
{
notesTextBox.Text = m_aim.ActiveApp.FilePath;
}
}
else
{
removeButton.IsEnabled = false;
}
}
else
{
removeButton.IsEnabled = false;
}
}
private void SaveSplitButton_Click(object sender, RoutedEventArgs e)
{
//// 检查是否有可保存项的逻辑 (如果需要)
//if (!m_aim.AddinManager.HasItemsToSave())
//{
// // 也可以选择在这里禁用按钮,而不是显示消息框
// // MessageBox.Show(...);
// return;
//}
if (m_aim.AddinManager.AppCount != 0 || m_aim.AddinManager.CmdCount != 0)
{
saveContextMenu.PlacementTarget = saveSplitButton;
saveContextMenu.IsOpen = true;
}
}
private void NameTextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (m_aim.ActiveCmdItem != null)
{
m_aim.ActiveCmdItem.Name = nametextBox.Text;
m_aim.AddinManager.SaveToAimIni();
}
}
private void DescriptionTextBox_LostFocus(object sender, RoutedEventArgs e)
{
if (m_aim.ActiveCmdItem != null)
{
m_aim.ActiveCmdItem.Description = descriptionTextBox.Text;
m_aim.AddinManager.SaveToAimIni();
}
}
private void CommandsTreeView_LostFocus(object sender, RoutedEventArgs e)
{
// 检查焦点是否转移到了相关控件
var focusedElement = Keyboard.FocusedElement as FrameworkElement;
if (focusedElement != nametextBox &&
focusedElement != descriptionTextBox &&
focusedElement != runButton &&
focusedElement != removeButton)
{
DisableControl();
removeButton.IsEnabled = false;
}
}
private void CommandsTreeView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton != MouseButton.Left) return;
// 使用辅助方法 FindTreeViewItem 查找被双击的 TreeViewItem
var clickedItem = FindTreeViewItem(e.OriginalSource as DependencyObject);
// 确保双击的是一个有效的 TreeViewItem并且它是一个子节点没有子项
if (clickedItem != null && !HasChildren(clickedItem))
{
// 确保选中的是可执行的命令项 (AddinItem)
if (clickedItem.Tag is AddinItem)
{
Run();
}
else
{
ShowStatusError("选中的项目不是可执行的命令");
}
}
// 如果双击的是空白区域或父节点,则不执行任何操作
//var selectedItem = commandsTreeView.SelectedItem as TreeViewItem;
//if (selectedItem != null && !HasChildren(selectedItem))
//{
// // 确保选中的是可执行的命令项
// if (m_aim.ActiveCmdItem != null)
// {
// Run();
// }
// else
// {
// ShowStatusError("选中的项目不是可执行的命令");
// }
//}
}
private void SaveToAddinMenuItem_Click(object sender, RoutedEventArgs e)
{
if (!m_aim.AddinManager.HasItemsToSave())
{
MessageBox.Show(Properties.Resources.NoItemsSelected, Properties.Resources.AppName, MessageBoxButton.OK, MessageBoxImage.Exclamation);
return;
}
var typeToSave = externalToolsTabControl.SelectedItem == commandsTabPage ? AddinType.Command : AddinType.Application;
m_aim.AddinManager.SaveToAllUserManifest(typeToSave);
ShowStatusLabel("保存成功,请关闭窗口加载插件");
}
private void SaveLocalMenuItem_Click(object sender, RoutedEventArgs e)
{
if (!m_aim.AddinManager.HasItemsToSave())
{
MessageBox.Show(Properties.Resources.NoItemsSelected, Properties.Resources.AppName, MessageBoxButton.OK, MessageBoxImage.Exclamation);
return;
}
var typeToSave = externalToolsTabControl.SelectedItem == commandsTabPage ? AddinType.Command : AddinType.Application;
m_aim.AddinManager.SaveToLocal(typeToSave);
ShowStatusLabel("保存成功");
}
private void ShowStatusLabel(string msg)
{
notesTextBox.Foreground = Brushes.Black;
notesTextBox.Text = msg;
}
private void ShowStatusError(string msg)
{
notesTextBox.Foreground = Brushes.Red;
notesTextBox.Text = msg;
}
private void CommandsTreeView_RefreshData()
{
RefreshTreeView(commandsTreeView, m_aim.AddinManager.Commands);
// 清除搜索框
searchTextBox.Text = string.Empty;
// 清空存储的命令项列表,防止重复
m_allCommandItems.Clear();
}
private void ApplicationsTreeView_RefreshData()
{
RefreshTreeView(applicationsTreeView, m_aim.AddinManager.Applications);
}
private void RefreshTreeView(TreeView tree, Addins addins)
{
if (addins == null) return;
// 清除现有项目,防止重复
tree.Items.Clear();
foreach (var kvp in addins.AddinDict)
{
var key = kvp.Key;
var value = kvp.Value;
var node = new TreeViewItem
{
Tag = value,
IsExpanded = true // 默认展开
};
// 创建带CheckBox的Header
var stackPanel = new StackPanel { Orientation = Orientation.Horizontal };
var checkBox = new CheckBox
{
IsChecked = value.Save,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0, 0, 5, 0),
IsThreeState = true // 支持三态(选中、未选中、部分选中)
};
checkBox.Checked += TreeViewCheckBox_Changed;
checkBox.Unchecked += TreeViewCheckBox_Changed;
checkBox.Indeterminate += TreeViewCheckBox_Changed;
var textBlock = new TextBlock
{
Text = key,
VerticalAlignment = VerticalAlignment.Center
};
stackPanel.Children.Add(checkBox);
stackPanel.Children.Add(textBlock);
node.Header = stackPanel;
// 用于确定父节点初始状态
var hasCheckedChildren = false;
var hasUncheckedChildren = false;
foreach (var addinItem in value.ItemList)
{
var childNode = new TreeViewItem
{
Tag = addinItem
};
// 创建子节点的带CheckBox的Header
var childStackPanel = new StackPanel { Orientation = Orientation.Horizontal };
var childCheckBox = new CheckBox
{
IsChecked = addinItem.Save,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0, 0, 5, 0)
};
childCheckBox.Checked += TreeViewCheckBox_Changed;
childCheckBox.Unchecked += TreeViewCheckBox_Changed;
var childTextBlock = new TextBlock
{
Text = addinItem.FullClassName,
VerticalAlignment = VerticalAlignment.Center
};
childStackPanel.Children.Add(childCheckBox);
childStackPanel.Children.Add(childTextBlock);
childNode.Header = childStackPanel;
node.Items.Add(childNode);
// 统计子节点状态
if (addinItem.Save)
{
hasCheckedChildren = true;
}
else
{
hasUncheckedChildren = true;
}
}
// 设置父节点CheckBox的初始状态
if (hasCheckedChildren && hasUncheckedChildren)
{
checkBox.IsChecked = null; // 部分选中
}
else if (hasCheckedChildren)
{
checkBox.IsChecked = true; // 全选
}
else
{
checkBox.IsChecked = false; // 全不选
}
tree.Items.Add(node);
}
if (tree.Items.Count > 0)
{
var lastItem = tree.Items[tree.Items.Count - 1] as TreeViewItem;
if (lastItem != null)
{
lastItem.IsSelected = true;
lastItem.BringIntoView();
}
}
// 刷新RemoveButton状态
RemoveButton_RefreshData();
}
// (新增) 辅助方法从一个UI元素向上遍历可视化树找到其所属的 TreeViewItem
private TreeViewItem FindTreeViewItem(DependencyObject source)
{
while (source != null && !(source is TreeViewItem))
{
source = VisualTreeHelper.GetParent(source);
}
return source as TreeViewItem;
}
// (重写并修复) 在右键菜单打开前,根据当前选中的项动态设置菜单项的可用状态
private void CommandsTreeView_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
// 1. 默认将所有菜单项禁用,这是关键!
ContextMenuRun.IsEnabled = false;
ContextMenuLoad.IsEnabled = true; // "加载" 总是可用的
ContextMenuRemove.IsEnabled = false;
ContextMenuReload.IsEnabled = false;
ContextMenuOpenInExplorer.IsEnabled = false;
ContextMenuAssemblyInfo.IsEnabled = false;
// 2. 准确查找鼠标右键点击的 TreeViewItem
var clickedItem = FindTreeViewItem(e.OriginalSource as DependencyObject);
// 3. 如果点击的是空白区域 (没有找到 TreeViewItem),则直接返回
if (clickedItem == null || clickedItem.Tag == null)
{
return;
}
// 4. 根据找到的项的类型,逐一启用对应的菜单项
if (clickedItem.Tag is Addin addin)
{
// 当右键点击父节点 (Addin) 时
ContextMenuRemove.IsEnabled = true;
ContextMenuReload.IsEnabled = File.Exists(addin.FilePath);
ContextMenuOpenInExplorer.IsEnabled = File.Exists(addin.FilePath);
}
else if (clickedItem.Tag is AddinItem addinItem)
{
// 当右键点击子节点 (AddinItem) 时
ContextMenuRun.IsEnabled = true;
ContextMenuRemove.IsEnabled = true;
ContextMenuReload.IsEnabled = File.Exists(addinItem.AssemblyPath);
ContextMenuOpenInExplorer.IsEnabled = File.Exists(addinItem.AssemblyPath);
ContextMenuAssemblyInfo.IsEnabled = File.Exists(addinItem.AssemblyPath);
}
}
// 运行
private void ContextMenuRun_Click(object sender, RoutedEventArgs e)
{
if (commandsTreeView.SelectedItem is TreeViewItem item && item.Tag is AddinItem)
{
Run();
}
}
// 加载
private void ContextMenuLoad_Click(object sender, RoutedEventArgs e)
{
// 直接调用已有的加载按钮逻辑
LoadButton_Click(sender, e);
}
// 移除
private void ContextMenuRemove_Click(object sender, RoutedEventArgs e)
{
// 直接调用已有的移除按钮逻辑
if (commandsTreeView.SelectedItem != null)
{
RemoveButton_Click(sender, e);
}
}
// (重写) 重新加载
private void ContextMenuReload_Click(object sender, RoutedEventArgs e)
{
if (commandsTreeView.SelectedItem is not TreeViewItem selectedItem) return;
Addin addinToReload = null;
// 判断选中项是Addin(父)还是AddinItem(子)
if (selectedItem.Tag is Addin addin)
{
addinToReload = addin;
}
else if (selectedItem.Tag is AddinItem)
{
// 如果是子节点,需要找到其父节点 Addin
var parent = VisualTreeHelper.GetParent(selectedItem);
while (parent != null)
{
if (parent is TreeViewItem { Tag: Addin parentAddin })
{
addinToReload = parentAddin;
break;
}
parent = VisualTreeHelper.GetParent(parent);
}
}
if (addinToReload == null)
{
ShowStatusError("无法找到插件信息进行重新加载。");
return;
}
var filePath = addinToReload.FilePath;
if (!File.Exists(filePath))
{
ShowStatusError($"插件文件不存在,无法重新加载: {filePath}");
return;
}
// 1. 从数据模型中移除
m_aim.AddinManager.Commands.RemoveAddIn(addinToReload);
m_aim.AddinManager.SaveToAimIni();
// 2. 重新加载该文件
var addinType = m_aim.AddinManager.LoadAddin(filePath);
// 3. 刷新整个UI
CommandsTreeView_RefreshData();
ApplicationsTreeView_RefreshData();
if (addinType == AddinType.Invalid)
{
ShowStatusError($"重新加载失败: {filePath}");
}
else
{
ShowStatusLabel($"重新加载成功: {filePath}");
}
}
// 在资源管理器中显示
private void ContextMenuOpenInExplorer_Click(object sender, RoutedEventArgs e)
{
var filePath = string.Empty;
if (commandsTreeView.SelectedItem is TreeViewItem item)
{
if (item.Tag is Addin addin)
{
filePath = addin.FilePath;
}
else if (item.Tag is AddinItem addinItem)
{
filePath = addinItem.AssemblyPath;
}
}
if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
{
try
{
// /select, 会打开文件夹并选中该文件
Process.Start("explorer.exe", $"/select,\"{filePath}\"");
}
catch (Exception ex)
{
ShowStatusError($"无法打开文件位置: {ex.Message}");
}
}
else
{
ShowStatusError("文件路径无效或文件不存在。");
}
}
// 查看程序集信息
private void ContextMenuAssemblyInfo_Click(object sender, RoutedEventArgs e)
{
if (commandsTreeView.SelectedItem is TreeViewItem item && item.Tag is AddinItem addinItem)
{
var filePath = addinItem.AssemblyPath;
if (!File.Exists(filePath))
{
ShowStatusError($"程序集文件不存在: {filePath}");
return;
}
try
{
// 注意LoadFrom会锁定文件。对于更复杂的场景可能需要使用不同的加载上下文。
var assembly = Assembly.LoadFrom(filePath);
var assemblyName = assembly.GetName();
var referencedAssemblies = assembly.GetReferencedAssemblies();
var info = new System.Text.StringBuilder();
info.AppendLine($"程序集: {assemblyName.Name}");
info.AppendLine($"版本: {assemblyName.Version}");
info.AppendLine($"完整名称: {assembly.FullName}");
info.AppendLine("\n--- 依赖项 ---");
foreach (var refAssembly in referencedAssemblies)
{
info.AppendLine(refAssembly.FullName);
}
// 这里我们用MessageBox来显示信息。
// 在实际项目中,您可能想创建一个新的窗口来更友好地展示这些信息。
MessageBox.Show(info.ToString(), "程序集信息", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
ShowStatusError($"无法加载程序集信息: {ex.Message}");
}
}
}
private TreeViewItem FindTreeViewItemFromCheckBox(CheckBox checkBox)
{
DependencyObject parent = checkBox;
// 向上遍历可视化树直到找到TreeViewItem
while (parent != null)
{
parent = VisualTreeHelper.GetParent(parent);
if (parent is TreeViewItem treeViewItem)
{
return treeViewItem;
}
}
// 如果通过可视化树没找到,尝试通过逻辑树查找
parent = checkBox;
while (parent != null)
{
parent = LogicalTreeHelper.GetParent(parent);
if (parent is TreeViewItem treeViewItem)
{
return treeViewItem;
}
}
return null;
}
// (新增) 在右键按下时强制选中鼠标下的TreeViewItem
private void CommandsTreeView_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
var treeView = sender as TreeView;
if (treeView == null) return;
var hitTestResult = VisualTreeHelper.HitTest(treeView, e.GetPosition(treeView));
if (hitTestResult == null) return;
var dependencyObject = hitTestResult.VisualHit;
while (dependencyObject != null)
{
if (dependencyObject is TreeViewItem item)
{
// 找到了TreeViewItem将其设为选中状态
item.IsSelected = true;
e.Handled = true; // 阻止事件继续传播,防止其他意外行为
return;
}
dependencyObject = VisualTreeHelper.GetParent(dependencyObject);
}
}
}
}