/************************************************************************ *@file FrameworkLocal\UIClient\CenterViews\Core\UserControls\ParameterNodeTreeViewControl.cs * @author Su Liang * @Date 2022-08-01 * * @copyright © Sicentury Inc. * * @brief Reconstructed to support rich functions. * * @details * *****************************************************************************/ using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using Microsoft.VisualBasic.FileIO; using Sicentury.Core.Dialogs; using Sicentury.Core.Tree; namespace Sicentury.Core.UserControls { /// /// Interaction logic for ParameterNodeTreeViewControl.xaml /// public partial class ParameterNodeTreeViewControl { #region Variables private bool _clearGroupSelectionOnly = false; #endregion /// /// 父级文件夹。 /// private const string PRESET_GROUP_SUB_FOLDER_NAME = "PresetGroups"; public ParameterNodeTreeViewControl() { //InitializeComponent(); } protected override void OnInitialized(System.EventArgs e) { InitializeComponent(); base.OnInitialized(e); LoadPresetGroupFilesList(); } #region Properties public static readonly DependencyProperty TreeRootProperty = DependencyProperty.Register( nameof(TreeRoot), typeof(TreeNode), typeof(ParameterNodeTreeViewControl), new PropertyMetadata(default(TreeNode), (o, p) => { if (o is ParameterNodeTreeViewControl control) { if (p.NewValue is TreeNode tree) { tree.TerminalNodeSelectionChanged += (sender, b) => { control.RaiseEvent(new RoutedEventArgs(TerminalNodeSelectionChangedEvent, sender)); }; } } })); /// /// 设置或返回TreeView数据源。 /// public TreeNode TreeRoot { get => (TreeNode)GetValue(TreeRootProperty); set => SetValue(TreeRootProperty, value); } public static readonly DependencyProperty PresetGroupsFolderNameProperty = DependencyProperty.Register( "PresetGroupsFolderName", typeof(string), typeof(ParameterNodeTreeViewControl), new PropertyMetadata(default(string))); /// /// 设置或返回存放预设组信息的子文件夹名称。 /// public string PresetGroupsFolderName { get => (string)GetValue(PresetGroupsFolderNameProperty); set => SetValue(PresetGroupsFolderNameProperty, value); } public static readonly DependencyProperty VisibilityExcludeNodesButtonProperty = DependencyProperty.Register( nameof(VisibilityExcludeNodesButton), typeof(Visibility), typeof(ParameterNodeTreeViewControl), new PropertyMetadata(System.Windows.Visibility.Hidden)); /// /// 是否显示节点排除列表编辑按钮。 /// public Visibility VisibilityExcludeNodesButton { get => (Visibility)GetValue(VisibilityExcludeNodesButtonProperty); set => SetValue(VisibilityExcludeNodesButtonProperty, value); } // public static readonly DependencyProperty VisibilityTopToolsProperty = DependencyProperty.Register( nameof(VisibilityTopTools), typeof(Visibility), typeof(ParameterNodeTreeViewControl), new PropertyMetadata(System.Windows.Visibility.Visible)); /// /// 顶部工具栏显示属性,不需要编辑时; /// public Visibility VisibilityTopTools { get => (Visibility)GetValue(VisibilityTopToolsProperty); set => SetValue(VisibilityTopToolsProperty, value); } #endregion #region Routed Events // Register a custom routed event using the Bubble routing strategy. public static readonly RoutedEvent TerminalNodeSelectionChangedEvent = EventManager.RegisterRoutedEvent( name: nameof(TerminalNodeSelectionChanged), routingStrategy: RoutingStrategy.Bubble, handlerType: typeof(RoutedEventHandler), ownerType: typeof(ParameterNodeTreeViewControl)); // Provide CLR accessors for assigning an event handler. public event RoutedEventHandler TerminalNodeSelectionChanged { add => AddHandler(TerminalNodeSelectionChangedEvent, value); remove => RemoveHandler(TerminalNodeSelectionChangedEvent, value); } // Register a custom routed event using the Bubble routing strategy. public static readonly RoutedEvent EditExcludeNodesListEvent = EventManager.RegisterRoutedEvent( name: nameof(EditExcludeNodesList), routingStrategy: RoutingStrategy.Bubble, handlerType: typeof(RoutedEventHandler), ownerType: typeof(ParameterNodeTreeViewControl)); // Provide CLR accessors for assigning an event handler. public event RoutedEventHandler EditExcludeNodesList { add => AddHandler(EditExcludeNodesListEvent, value); remove => RemoveHandler(EditExcludeNodesListEvent, value); } #endregion #region Methods /// /// 返回组信息文件名的完整路径。 /// /// 组名称 /// private string GetPresetGroupFullFileName(string groupName) { return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, PRESET_GROUP_SUB_FOLDER_NAME, PresetGroupsFolderName, groupName + ".json"); } /// /// 返回保存组信息的文件夹名称。 /// /// private string GetPresetGroupsFolderName() { return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, PRESET_GROUP_SUB_FOLDER_NAME, PresetGroupsFolderName); } /// /// 加载指定文件夹内的所有json文件。 /// /// /// private void LoadPresetGroupFilesList(string selectedGroupName = "") { if (string.IsNullOrEmpty(PresetGroupsFolderName)) throw new ArgumentException("the preset group files folder is not set.", paramName: nameof(PresetGroupsFolderName)); var folderName = GetPresetGroupsFolderName(); var previousSelection = selectedGroupName; // 如果未指定下次待加载的项目,则保存当前的选择 if (string.IsNullOrEmpty(previousSelection)) if (cbxPresetList.SelectedItem != null) previousSelection = cbxPresetList.SelectedItem.ToString(); // 如果文件夹不存在,返回空列表 if (!Directory.Exists(folderName)) { Directory.CreateDirectory(folderName); cbxPresetList.ItemsSource = new List(); } else { var source = Directory.EnumerateFiles(folderName, "*.json") .Select(Path.GetFileNameWithoutExtension) .ToList(); cbxPresetList.ItemsSource = source; // 重新选择上次的选择 if (string.IsNullOrEmpty(previousSelection) == false) { if (source.FirstOrDefault(x => x == previousSelection) != null) cbxPresetList.SelectedItem = previousSelection; } else { // 如果TreeView还没有加载内容,则不要自动选择 if (TreeRoot != null) cbxPresetList.SelectedIndex = 0; } } } /// /// 仅清空Group下拉框的选项,而不清除TreeView的选择状态。 /// 通常用于DataGrid中删除条目后,清除Group选项。 /// public void ClearPresetGroupSelectionOnly() { _clearGroupSelectionOnly = true; cbxPresetList.SelectedIndex = -1; _clearGroupSelectionOnly = false; } #endregion #region Events /// /// 创建节点排除清单。 /// /// /// /// private void BtnExcludeNode_OnClick(object sender, RoutedEventArgs e) { this.RaiseEvent(new RoutedEventArgs(EditExcludeNodesListEvent, this)); } /// /// 重新加载组列表。 /// /// /// private void BtnRefreshPresetList_OnClick(object sender, RoutedEventArgs e) { try { LoadPresetGroupFilesList(); } catch (Exception ex) { var err = $"Unable to refresh the group list, {ex.Message}"; MessageBox.Show(err, "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } /// /// 新建组。 /// /// /// private void BtnNewGroup_OnClick(object sender, RoutedEventArgs e) { try { TreeRoot.UnselectAll(); cbxPresetList.SelectedIndex = -1; } catch (Exception ex) { var err = $"Unable to create new group, {ex.Message}"; MessageBox.Show(err, "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } /// /// 保存组。 /// /// /// private void BtnSave_OnClick(object sender, RoutedEventArgs e) { var groupName = ""; //if (TreeRoot.SelectedTerminalCount > TreeNode.QUERY_LIMIT_COUNT) //{ // MessageBox.Show($"Too many items are selected, it's limited to {TreeNode.QUERY_LIMIT_COUNT}.", // "Error", MessageBoxButton.OK, // MessageBoxImage.Error); // return; //} if (cbxPresetList.SelectedIndex == -1) { var dlg = new PresetGroupSaveDialog("Save Group", cbxPresetList.ItemsSource?.Cast()); var ret = dlg.ShowDialog(); if (ret.HasValue && ret.Value == true) { groupName = dlg.GroupName; } else { return; } } else { groupName = cbxPresetList.SelectedItem.ToString(); } try { // 保存 TreeNodeSelectionGroupInfo.SaveToJsonFile(GetPresetGroupFullFileName(groupName), TreeRoot); LoadPresetGroupFilesList(groupName); } catch (Exception ex) { var err = $"Unable to save group {groupName}, {ex.Message}"; MessageBox.Show(err, "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } /// /// 另存为组。 /// /// /// private void BtnSaveAs_OnClick(object sender, RoutedEventArgs e) { if (cbxPresetList.SelectedIndex == -1) { MessageBox.Show("No group is selected.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); return; } var newName = $"Copy of {cbxPresetList.SelectedValue}"; var dlg = new PresetGroupSaveDialog("Save As Group", cbxPresetList.ItemsSource?.Cast(), newName); var ret = dlg.ShowDialog(); if (ret.HasValue && ret.Value) { var groupName = cbxPresetList.SelectedItem.ToString(); try { // 保存 TreeNodeSelectionGroupInfo.SaveToJsonFile(GetPresetGroupFullFileName(groupName), TreeRoot); // 保存一个空文件 TreeNodeSelectionGroupInfo.SaveToJsonFile(GetPresetGroupFullFileName(dlg.GroupName), TreeRoot); LoadPresetGroupFilesList(dlg.GroupName); } catch (Exception ex) { var err = $"Unable to save as group {groupName}, {ex.Message}"; MessageBox.Show(err, "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } } /// /// 选择组。 /// /// /// private void CbxPresetList_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { if (cbxPresetList.SelectedIndex == -1) { // if (_clearGroupSelectionOnly) return; TreeRoot.UnselectAll(); return; } var groupName = cbxPresetList.SelectedValue.ToString(); try { TreeNodeSelectionGroupInfo.RecoveryFromJsonFile(GetPresetGroupFullFileName(groupName), TreeRoot); } catch (Exception ex) { var err = $"Unable to recovery group {groupName}, {ex.Message}"; MessageBox.Show(err, "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } /// /// 删除组。 /// /// /// private void BtnDelete_OnClick(object sender, RoutedEventArgs e) { // 如果没选中任何组,忽略操作 if (cbxPresetList.SelectedIndex == -1) return; var groupName = cbxPresetList.SelectedItem.ToString(); if (string.IsNullOrEmpty(groupName)) return; var ret = MessageBox.Show($"Are you sure to delete the group {groupName}", "Warning", MessageBoxButton.YesNo, MessageBoxImage.Question); if (ret == MessageBoxResult.Yes) { try { FileSystem.DeleteFile(GetPresetGroupFullFileName(groupName), UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin); LoadPresetGroupFilesList(); } catch (Exception ex) { var err = $"Unable to delete group {groupName}, {ex.Message}"; MessageBox.Show(err, "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } } /// /// 折叠所有节点。 /// /// /// private void BtnCollapseAll_OnClick(object sender, RoutedEventArgs e) { if (TreeRoot.ChildNodes.Count > 0) { TreeRoot.CollapseAll(); } } /// /// 展开所有节点。 /// /// /// private void BtnExpandAll_OnClick(object sender, RoutedEventArgs e) { if (TreeRoot.ChildNodes.Count > 0) { TreeRoot.ExpandAll(); } } /// /// 应用筛选器。 /// /// /// /// private void BtnApplyFilter_OnClick(object sender, RoutedEventArgs e) { TreeRoot.SuspendUpdate(); TreeRoot.ApplyFilter(txtFilterKeyword.Text); TreeRoot.ResumeUpdate(); txtFilterKeyword.SelectAll(); txtFilterKeyword.Focus(); } /// /// 清除筛选器 /// /// /// private void BtnClearFilter_OnClick(object sender, RoutedEventArgs e) { TreeRoot.SuspendUpdate(); TreeRoot.ClearFilter(); TreeRoot.ResumeUpdate(); txtFilterKeyword.Text = ""; txtFilterKeyword.Focus(); } private void TxtFilterKeyword_OnKeyDown(object sender, KeyEventArgs e) { if (e.Key != Key.Enter) return; TreeRoot.ApplyFilter(txtFilterKeyword.Text); txtFilterKeyword.SelectAll(); txtFilterKeyword.Focus(); } private void BtnShowSelectedOnly_OnClick(object sender, RoutedEventArgs e) { TreeRoot.ShowSelectedOnly(); } private void BtnShowAll_OnClick(object sender, RoutedEventArgs e) { TreeRoot.ShowAll(); } #endregion } }