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 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(); } 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; // 更新UI(CheckBox) 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 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 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); } } } }