480 lines
16 KiB
C#
480 lines
16 KiB
C#
/************************************************************************
|
||
*@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
|
||
{
|
||
/// <summary>
|
||
/// Interaction logic for ParameterNodeTreeViewControl.xaml
|
||
/// </summary>
|
||
public partial class ParameterNodeTreeViewControl
|
||
{
|
||
#region Variables
|
||
|
||
private bool _clearGroupSelectionOnly = false;
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 父级文件夹。
|
||
/// </summary>
|
||
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));
|
||
};
|
||
}
|
||
}
|
||
}));
|
||
|
||
/// <summary>
|
||
/// 设置或返回TreeView数据源。
|
||
/// </summary>
|
||
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)));
|
||
|
||
/// <summary>
|
||
/// 设置或返回存放预设组信息的子文件夹名称。
|
||
/// </summary>
|
||
public string PresetGroupsFolderName
|
||
{
|
||
get => (string)GetValue(PresetGroupsFolderNameProperty);
|
||
set => SetValue(PresetGroupsFolderNameProperty, 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);
|
||
}
|
||
|
||
|
||
#endregion
|
||
|
||
#region Methods
|
||
|
||
|
||
/// <summary>
|
||
/// 返回组信息文件名的完整路径。
|
||
/// </summary>
|
||
/// <param name="groupName">组名称</param>
|
||
/// <returns></returns>
|
||
private string GetPresetGroupFullFileName(string groupName)
|
||
{
|
||
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, PRESET_GROUP_SUB_FOLDER_NAME,
|
||
PresetGroupsFolderName, groupName + ".json");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 返回保存组信息的文件夹名称。
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private string GetPresetGroupsFolderName()
|
||
{
|
||
return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, PRESET_GROUP_SUB_FOLDER_NAME,
|
||
PresetGroupsFolderName);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载指定文件夹内的所有json文件。
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
/// <exception cref="ArgumentException"></exception>
|
||
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<string>();
|
||
}
|
||
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;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 仅清空Group下拉框的选项,而不清除TreeView的选择状态。
|
||
/// <para>通常用于DataGrid中删除条目后,清除Group选项。</para>
|
||
/// </summary>
|
||
public void ClearPresetGroupSelectionOnly()
|
||
{
|
||
_clearGroupSelectionOnly = true;
|
||
cbxPresetList.SelectedIndex = -1;
|
||
_clearGroupSelectionOnly = false;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Events
|
||
|
||
|
||
/// <summary>
|
||
/// 重新加载组列表。
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 新建组。
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存组。
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
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<string>());
|
||
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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 另存为组。
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
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<string>(),
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选择组。
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
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);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除组。
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 折叠所有节点。
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
private void BtnCollapseAll_OnClick(object sender, RoutedEventArgs e)
|
||
{
|
||
TreeRoot.CollapseAll();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 展开所有节点。
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
private void BtnExpandAll_OnClick(object sender, RoutedEventArgs e)
|
||
{
|
||
TreeRoot.ExpandAll();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 应用筛选器。
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
private void BtnApplyFilter_OnClick(object sender, RoutedEventArgs e)
|
||
{
|
||
TreeRoot.SuspendUpdate();
|
||
TreeRoot.ApplyFilter(txtFilterKeyword.Text);
|
||
TreeRoot.ResumeUpdate();
|
||
txtFilterKeyword.SelectAll();
|
||
txtFilterKeyword.Focus();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除筛选器
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
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
|
||
}
|
||
}
|