ParameterNodeTreeViewControl.xaml.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. 
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using System.Windows.Input;
  9. using MECF.Framework.UI.Client.ClientBase.Tree;
  10. using MECF.Framework.UI.Client.ClientViews.Dialogs;
  11. using Microsoft.VisualBasic.FileIO;
  12. namespace MECF.Framework.UI.Client.ClientBase.UserControls
  13. {
  14. /// <summary>
  15. /// Interaction logic for ParameterNodeTreeViewControl.xaml
  16. /// </summary>
  17. public partial class ParameterNodeTreeViewControl
  18. {
  19. #region Variables
  20. private bool _clearGroupSelectionOnly = false;
  21. #endregion
  22. /// <summary>
  23. /// 父级文件夹。
  24. /// </summary>
  25. private const string PRESET_GROUP_SUB_FOLDER_NAME = "PresetGroups";
  26. public ParameterNodeTreeViewControl()
  27. {
  28. //InitializeComponent();
  29. }
  30. protected override void OnInitialized(System.EventArgs e)
  31. {
  32. InitializeComponent();
  33. base.OnInitialized(e);
  34. LoadPresetGroupFilesList();
  35. }
  36. #region Properties
  37. public static readonly DependencyProperty TreeRootProperty = DependencyProperty.Register(
  38. nameof(TreeRoot), typeof(TreeNode), typeof(ParameterNodeTreeViewControl),
  39. new PropertyMetadata(default(TreeNode), (o, p) =>
  40. {
  41. if (o is ParameterNodeTreeViewControl control)
  42. {
  43. if (p.NewValue is TreeNode tree)
  44. {
  45. tree.TerminalNodeSelectionChanged += (sender, b) =>
  46. {
  47. control.RaiseEvent(new RoutedEventArgs(TerminalNodeSelectionChangedEvent, sender));
  48. };
  49. }
  50. }
  51. }));
  52. /// <summary>
  53. /// 设置或返回TreeView数据源。
  54. /// </summary>
  55. public TreeNode TreeRoot
  56. {
  57. get => (TreeNode)GetValue(TreeRootProperty);
  58. set => SetValue(TreeRootProperty, value);
  59. }
  60. public static readonly DependencyProperty PresetGroupsFolderNameProperty = DependencyProperty.Register(
  61. "PresetGroupsFolderName", typeof(string), typeof(ParameterNodeTreeViewControl),
  62. new PropertyMetadata(default(string)));
  63. /// <summary>
  64. /// 设置或返回存放预设组信息的子文件夹名称。
  65. /// </summary>
  66. public string PresetGroupsFolderName
  67. {
  68. get => (string)GetValue(PresetGroupsFolderNameProperty);
  69. set => SetValue(PresetGroupsFolderNameProperty, value);
  70. }
  71. #endregion
  72. #region Routed Events
  73. // Register a custom routed event using the Bubble routing strategy.
  74. public static readonly RoutedEvent TerminalNodeSelectionChangedEvent = EventManager.RegisterRoutedEvent(
  75. name: nameof(TerminalNodeSelectionChanged),
  76. routingStrategy: RoutingStrategy.Bubble,
  77. handlerType: typeof(RoutedEventHandler),
  78. ownerType: typeof(ParameterNodeTreeViewControl));
  79. // Provide CLR accessors for assigning an event handler.
  80. public event RoutedEventHandler TerminalNodeSelectionChanged
  81. {
  82. add => AddHandler(TerminalNodeSelectionChangedEvent, value);
  83. remove => RemoveHandler(TerminalNodeSelectionChangedEvent, value);
  84. }
  85. #endregion
  86. #region Methods
  87. /// <summary>
  88. /// 返回组信息文件名的完整路径。
  89. /// </summary>
  90. /// <param name="groupName">组名称</param>
  91. /// <returns></returns>
  92. private string GetPresetGroupFullFileName(string groupName)
  93. {
  94. return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, PRESET_GROUP_SUB_FOLDER_NAME,
  95. PresetGroupsFolderName, groupName + ".json");
  96. }
  97. /// <summary>
  98. /// 返回保存组信息的文件夹名称。
  99. /// </summary>
  100. /// <returns></returns>
  101. private string GetPresetGroupsFolderName()
  102. {
  103. return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, PRESET_GROUP_SUB_FOLDER_NAME,
  104. PresetGroupsFolderName);
  105. }
  106. /// <summary>
  107. /// 加载指定文件夹内的所有json文件。
  108. /// </summary>
  109. /// <returns></returns>
  110. /// <exception cref="ArgumentException"></exception>
  111. private void LoadPresetGroupFilesList(string selectedGroupName = "")
  112. {
  113. if (string.IsNullOrEmpty(PresetGroupsFolderName))
  114. throw new ArgumentException("the preset group files folder is not set.",
  115. paramName: nameof(PresetGroupsFolderName));
  116. var folderName = GetPresetGroupsFolderName();
  117. var previousSelection = selectedGroupName;
  118. // 如果未指定下次待加载的项目,则保存当前的选择
  119. if (string.IsNullOrEmpty(previousSelection))
  120. if (cbxPresetList.SelectedItem != null)
  121. previousSelection = cbxPresetList.SelectedItem.ToString();
  122. // 如果文件夹不存在,返回空列表
  123. if (!Directory.Exists(folderName))
  124. {
  125. Directory.CreateDirectory(folderName);
  126. cbxPresetList.ItemsSource = new List<string>();
  127. }
  128. else
  129. {
  130. var source =
  131. Directory.EnumerateFiles(folderName, "*.json")
  132. .Select(Path.GetFileNameWithoutExtension)
  133. .ToList();
  134. cbxPresetList.ItemsSource = source;
  135. // 重新选择上次的选择
  136. if (string.IsNullOrEmpty(previousSelection) == false)
  137. {
  138. if (source.FirstOrDefault(x => x == previousSelection) != null)
  139. cbxPresetList.SelectedItem = previousSelection;
  140. }
  141. else
  142. {
  143. // 如果TreeView还没有加载内容,则不要自动选择
  144. if (TreeRoot != null)
  145. cbxPresetList.SelectedIndex = 0;
  146. }
  147. }
  148. }
  149. /// <summary>
  150. /// 仅清空Group下拉框的选项,而不清除TreeView的选择状态。
  151. /// <para>通常用于DataGrid中删除条目后,清除Group选项。</para>
  152. /// </summary>
  153. public void ClearPresetGroupSelectionOnly()
  154. {
  155. _clearGroupSelectionOnly = true;
  156. cbxPresetList.SelectedIndex = -1;
  157. _clearGroupSelectionOnly = false;
  158. }
  159. #endregion
  160. #region Events
  161. /// <summary>
  162. /// 重新加载组列表。
  163. /// </summary>
  164. /// <param name="sender"></param>
  165. /// <param name="e"></param>
  166. private void BtnRefreshPresetList_OnClick(object sender, RoutedEventArgs e)
  167. {
  168. try
  169. {
  170. LoadPresetGroupFilesList();
  171. }
  172. catch (Exception ex)
  173. {
  174. var err = $"Unable to refresh the group list, {ex.Message}";
  175. MessageBox.Show(err, "Error", MessageBoxButton.OK,
  176. MessageBoxImage.Error);
  177. }
  178. }
  179. /// <summary>
  180. /// 新建组。
  181. /// </summary>
  182. /// <param name="sender"></param>
  183. /// <param name="e"></param>
  184. private void BtnNewGroup_OnClick(object sender, RoutedEventArgs e)
  185. {
  186. try
  187. {
  188. TreeRoot.UnselectAll();
  189. cbxPresetList.SelectedIndex = -1;
  190. }
  191. catch (Exception ex)
  192. {
  193. var err = $"Unable to create new group, {ex.Message}";
  194. MessageBox.Show(err, "Error", MessageBoxButton.OK,
  195. MessageBoxImage.Error);
  196. }
  197. }
  198. /// <summary>
  199. /// 保存组。
  200. /// </summary>
  201. /// <param name="sender"></param>
  202. /// <param name="e"></param>
  203. private void BtnSave_OnClick(object sender, RoutedEventArgs e)
  204. {
  205. var groupName = "";
  206. //if (TreeRoot.SelectedTerminalCount > TreeNode.QUERY_LIMIT_COUNT)
  207. //{
  208. // MessageBox.Show($"Too many items are selected, it's limited to {TreeNode.QUERY_LIMIT_COUNT}.",
  209. // "Error", MessageBoxButton.OK,
  210. // MessageBoxImage.Error);
  211. // return;
  212. //}
  213. if (cbxPresetList.SelectedIndex == -1)
  214. {
  215. var dlg = new PresetGroupSaveDialog("Save Group", cbxPresetList.ItemsSource?.Cast<string>());
  216. var ret = dlg.ShowDialog();
  217. if (ret.HasValue && ret.Value == true)
  218. {
  219. groupName = dlg.GroupName;
  220. }
  221. else
  222. {
  223. return;
  224. }
  225. }
  226. else
  227. {
  228. groupName = cbxPresetList.SelectedItem.ToString();
  229. }
  230. try
  231. {
  232. // 保存
  233. TreeNodeSelectionGroupInfo.SaveToJsonFile(GetPresetGroupFullFileName(groupName), TreeRoot);
  234. LoadPresetGroupFilesList(groupName);
  235. }
  236. catch (Exception ex)
  237. {
  238. var err = $"Unable to save group {groupName}, {ex.Message}";
  239. MessageBox.Show(err, "Error", MessageBoxButton.OK,
  240. MessageBoxImage.Error);
  241. }
  242. }
  243. /// <summary>
  244. /// 另存为组。
  245. /// </summary>
  246. /// <param name="sender"></param>
  247. /// <param name="e"></param>
  248. private void BtnSaveAs_OnClick(object sender, RoutedEventArgs e)
  249. {
  250. if (cbxPresetList.SelectedIndex == -1)
  251. {
  252. MessageBox.Show("No group is selected.", "Error", MessageBoxButton.OK,
  253. MessageBoxImage.Error);
  254. return;
  255. }
  256. var newName = $"Copy of {cbxPresetList.SelectedValue}";
  257. var dlg = new PresetGroupSaveDialog("Save As Group",
  258. cbxPresetList.ItemsSource?.Cast<string>(),
  259. newName);
  260. var ret = dlg.ShowDialog();
  261. if (ret.HasValue && ret.Value)
  262. {
  263. var groupName = cbxPresetList.SelectedItem.ToString();
  264. try
  265. {
  266. // 保存
  267. TreeNodeSelectionGroupInfo.SaveToJsonFile(GetPresetGroupFullFileName(groupName), TreeRoot);
  268. // 保存一个空文件
  269. TreeNodeSelectionGroupInfo.SaveToJsonFile(GetPresetGroupFullFileName(dlg.GroupName), TreeRoot);
  270. LoadPresetGroupFilesList(dlg.GroupName);
  271. }
  272. catch (Exception ex)
  273. {
  274. var err = $"Unable to save as group {groupName}, {ex.Message}";
  275. MessageBox.Show(err, "Error", MessageBoxButton.OK,
  276. MessageBoxImage.Error);
  277. }
  278. }
  279. }
  280. /// <summary>
  281. /// 选择组。
  282. /// </summary>
  283. /// <param name="sender"></param>
  284. /// <param name="e"></param>
  285. private void CbxPresetList_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
  286. {
  287. if (cbxPresetList.SelectedIndex == -1)
  288. {
  289. //
  290. if (_clearGroupSelectionOnly)
  291. return;
  292. TreeRoot.UnselectAll();
  293. return;
  294. }
  295. var groupName = cbxPresetList.SelectedValue.ToString();
  296. try
  297. {
  298. TreeNodeSelectionGroupInfo.RecoveryFromJsonFile(GetPresetGroupFullFileName(groupName), TreeRoot);
  299. }
  300. catch (Exception ex)
  301. {
  302. var err = $"Unable to recovery group {groupName}, {ex.Message}";
  303. MessageBox.Show(err, "Error", MessageBoxButton.OK,
  304. MessageBoxImage.Error);
  305. }
  306. }
  307. /// <summary>
  308. /// 删除组。
  309. /// </summary>
  310. /// <param name="sender"></param>
  311. /// <param name="e"></param>
  312. private void BtnDelete_OnClick(object sender, RoutedEventArgs e)
  313. {
  314. // 如果没选中任何组,忽略操作
  315. if (cbxPresetList.SelectedIndex == -1)
  316. return;
  317. var groupName = cbxPresetList.SelectedItem.ToString();
  318. if (string.IsNullOrEmpty(groupName))
  319. return;
  320. var ret = MessageBox.Show($"Are you sure to delete the group {groupName}", "Warning",
  321. MessageBoxButton.YesNo, MessageBoxImage.Question);
  322. if (ret == MessageBoxResult.Yes)
  323. {
  324. try
  325. {
  326. FileSystem.DeleteFile(GetPresetGroupFullFileName(groupName), UIOption.OnlyErrorDialogs,
  327. RecycleOption.SendToRecycleBin);
  328. LoadPresetGroupFilesList();
  329. }
  330. catch (Exception ex)
  331. {
  332. var err = $"Unable to delete group {groupName}, {ex.Message}";
  333. MessageBox.Show(err, "Error", MessageBoxButton.OK,
  334. MessageBoxImage.Error);
  335. }
  336. }
  337. }
  338. /// <summary>
  339. /// 折叠所有节点。
  340. /// </summary>
  341. /// <param name="sender"></param>
  342. /// <param name="e"></param>
  343. private void BtnCollapseAll_OnClick(object sender, RoutedEventArgs e)
  344. {
  345. TreeRoot.CollapseAll();
  346. }
  347. /// <summary>
  348. /// 展开所有节点。
  349. /// </summary>
  350. /// <param name="sender"></param>
  351. /// <param name="e"></param>
  352. private void BtnExpandAll_OnClick(object sender, RoutedEventArgs e)
  353. {
  354. TreeRoot.ExpandAll();
  355. }
  356. /// <summary>
  357. /// 应用筛选器。
  358. /// </summary>
  359. /// <param name="sender"></param>
  360. /// <param name="e"></param>
  361. /// <exception cref="NotImplementedException"></exception>
  362. private void BtnApplyFilter_OnClick(object sender, RoutedEventArgs e)
  363. {
  364. TreeRoot.SuspendUpdate();
  365. TreeRoot.ApplyFilter(txtFilterKeyword.Text);
  366. TreeRoot.ResumeUpdate();
  367. txtFilterKeyword.SelectAll();
  368. txtFilterKeyword.Focus();
  369. }
  370. /// <summary>
  371. /// 清除筛选器
  372. /// </summary>
  373. /// <param name="sender"></param>
  374. /// <param name="e"></param>
  375. private void BtnClearFilter_OnClick(object sender, RoutedEventArgs e)
  376. {
  377. TreeRoot.SuspendUpdate();
  378. TreeRoot.ClearFilter();
  379. TreeRoot.ResumeUpdate();
  380. txtFilterKeyword.Text = "";
  381. txtFilterKeyword.Focus();
  382. }
  383. private void TxtFilterKeyword_OnKeyDown(object sender, KeyEventArgs e)
  384. {
  385. if (e.Key != Key.Enter)
  386. return;
  387. TreeRoot.ApplyFilter(txtFilterKeyword.Text);
  388. txtFilterKeyword.SelectAll();
  389. txtFilterKeyword.Focus();
  390. }
  391. private void BtnShowSelectedOnly_OnClick(object sender, RoutedEventArgs e)
  392. {
  393. TreeRoot.ShowSelectedOnly();
  394. }
  395. private void BtnShowAll_OnClick(object sender, RoutedEventArgs e)
  396. {
  397. TreeRoot.ShowAll();
  398. }
  399. #endregion
  400. private void TextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  401. {
  402. var sss = e;
  403. var textBlock = sender as TextBlock;
  404. if (textBlock != null)
  405. {
  406. var data = textBlock.DataContext as TreeNode;
  407. if (data != null)
  408. {
  409. data.IsSelected = !data.IsSelected;
  410. }
  411. }
  412. }
  413. }
  414. }