跳到主要内容

github/tai

公开

Tai · Windows 软件与网站使用时长统计。上游 Planshit/Tai 的源码镜像,非灵栈原创;MIT 许可。来源:https://github.com/Planshit/Tai 。本次为手动同步,暂不提供安装包。

浏览文件

SettingPanel.cs

670 行 · 22.7 KB历史下载
1using Core.Librarys;
2using Core.Models.Config;
3using Newtonsoft.Json;
4using System;
5using System.Collections;
6using System.Collections.Generic;
7using System.Collections.ObjectModel;
8using System.Diagnostics;
9using System.IO;
10using System.Linq;
11using System.Reflection;
12using System.Text;
13using System.Threading.Tasks;
14using System.Windows;
15using System.Windows.Controls;
16using System.Windows.Media;
17using UI.Controls.Button;
18using UI.Controls.Input;
19using UI.Controls.List;
20using UI.Controls.Select;
21
22namespace UI.Controls.SettingPanel
23{
24
25 public class SettingPanel : Control
26 {
27 public struct Config
28 {
29 public ConfigAttribute Attribute { get; set; }
30 public PropertyInfo PropertyInfo { get; set; }
31 }
32 public object Data { get { return (object)GetValue(DataProperty); } set { SetValue(DataProperty, value); } }
33 public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(object), typeof(SettingPanel), new PropertyMetadata(new PropertyChangedCallback(OnDataChanged)));
34
35 public SolidColorBrush SpliteLineBrush { get { return (SolidColorBrush)GetValue(SpliteLineBrushProperty); } set { SetValue(SpliteLineBrushProperty, value); } }
36 public static readonly DependencyProperty SpliteLineBrushProperty = DependencyProperty.Register("SpliteLineBrush", typeof(SolidColorBrush), typeof(SettingPanel));
37
38 private static void OnDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
39 {
40 var control = d as SettingPanel;
41 if (e.NewValue != e.OldValue)
42 {
43 control.Render();
44 }
45 }
46
47
48 private StackPanel Container;
49 private object configData;
50 private readonly string nosetGroupKey = "noset_group";
51 private bool isCanRender = true;
52 private Dictionary<string, List<Config>> configList;
53 public Dictionary<string, List<string>> SettingData { get; set; }
54 public SettingPanel()
55 {
56 DefaultStyleKey = typeof(SettingPanel);
57 SettingData = new Dictionary<string, List<string>>();
58 }
59
60 public override void OnApplyTemplate()
61 {
62 base.OnApplyTemplate();
63
64 Container = GetTemplateChild("Container") as StackPanel;
65
66 isCanRender = true;
67 Render();
68 }
69 private void Render()
70 {
71 if (!isCanRender)
72 {
73 isCanRender = true;
74 return;
75 }
76 configList = new Dictionary<string, List<Config>>();
77 configList.Add(nosetGroupKey, new List<Config>());
78
79 if (Container == null)
80 {
81 return;
82 }
83
84 Container.Children.Clear();
85
86 if (Data == null)
87 {
88 return;
89 }
90
91
92
93 if (Data is IEnumerable)
94 {
95 // 多例
96 RenderMultiPanel();
97 }
98 else
99 {
100 // 单例
101 RenderSinglePanel();
102 }
103
104
105 }
106
107
108 #region 渲染多例面板
109 /// <summary>
110 /// 渲染多例面板
111 /// </summary>
112
113 private void RenderMultiPanel()
114 {
115 var dataList = Data as IEnumerable<object>;
116 var list = dataList.ToList();
117 var cacheList = new Dictionary<int, object>();
118
119 var dataType = Data.GetType();
120 var itemType = dataType.GenericTypeArguments[0];
121 var test1 = dataType.GetGenericArguments()[0];
122
123 var emptyData = Activator.CreateInstance(itemType);
124
125 // 添加按钮
126 var addBtn = new Button.Button();
127 addBtn.Content = "新建项";
128 addBtn.Width = 150;
129 addBtn.Icon = Base.IconTypes.CalculatorAddition;
130 addBtn.Click += (s, e) =>
131 {
132 int id = cacheList.Keys.Count > 0 ? cacheList.Keys.Max() + 1 : 1;
133
134 cacheList.Add(id, emptyData);
135
136 var panel = CreateMultiPanel(emptyData, id, cacheList);
137
138 Container.Children.Insert(Container.Children.Count - 1, panel);
139
140 };
141 Container.Children.Add(addBtn);
142
143
144 // 渲染数据
145 for (int i = 0; i < list.Count; i++)
146 {
147 int id = cacheList.Keys.Count > 0 ? cacheList.Keys.Max() + 1 : 1;
148
149 cacheList.Add(id, list[i]);
150 var panel = CreateMultiPanel(list[i], id, cacheList);
151 Container.Children.Insert(Container.Children.Count - 1, panel);
152 }
153
154
155
156 }
157
158
159 private SettingPanelMultiItem CreateMultiPanel(object data, int id, Dictionary<int, object> cacheList)
160 {
161 var panel = new SettingPanelMultiItem();
162 panel.Data = data;
163 panel.Tag = id;
164 panel.SettingData = SettingData;
165 panel.DataChanged += (e, c) =>
166 {
167 int pid = (int)panel.Tag;
168
169 if (cacheList.ContainsKey(pid))
170 {
171 // 更新
172 cacheList[pid] = panel.Data;
173
174 var listType = typeof(List<>);
175 var constructedListType = listType.MakeGenericType(panel.Data.GetType());
176
177 var newList = (IList)Activator.CreateInstance(constructedListType);
178
179 foreach (var item in cacheList.Values)
180 {
181 newList.Add(item);
182 }
183
184 isCanRender = false;
185 Data = newList;
186 }
187 };
188 panel.OnRemoveAction = new Command(new Action<object>((s) =>
189 {
190 cacheList.Remove(id);
191 var listType = typeof(List<>);
192 var constructedListType = listType.MakeGenericType(panel.Data.GetType());
193
194 var newList = (IList)Activator.CreateInstance(constructedListType);
195 foreach (var item in cacheList.Values)
196 {
197 newList.Add(item);
198 }
199
200 Container.Children.Remove(panel);
201 isCanRender = false;
202 Data = newList;
203 }));
204 return panel;
205 }
206 #endregion
207
208 /// <summary>
209 /// 渲染单例面板
210 /// </summary>
211 private void RenderSinglePanel()
212 {
213 configData = Activator.CreateInstance(Data.GetType());
214
215 // 遍历方法特性
216 foreach (PropertyInfo pi in Data.GetType().GetProperties())
217 {
218 foreach (Attribute attr in pi.GetCustomAttributes(true))
219 {
220 if (attr is ConfigAttribute)
221 {
222 ConfigAttribute attribute = (ConfigAttribute)attr;
223
224 var config = new Config()
225 {
226 Attribute = attribute,
227 PropertyInfo = pi
228 };
229 if (string.IsNullOrEmpty(attribute.Group))
230 {
231 // 未分组
232 configList[nosetGroupKey].Add(config);
233 }
234 else
235 {
236 if (!configList.ContainsKey(attribute.Group))
237 {
238 configList.Add(attribute.Group, new List<Config>());
239 }
240 configList[attribute.Group].Add(config);
241 }
242
243
244 }
245 }
246 }
247
248 // 渲染已分组的项目
249 foreach (var item in configList)
250 {
251 if (item.Key != nosetGroupKey)
252 {
253 RenderGroup(item.Value, item.Key);
254 }
255 }
256
257 // 渲染未分组项目
258 RenderGroup(configList[nosetGroupKey]);
259 }
260 private void RenderGroup(List<Config> configList, string groupName = null)
261 {
262 if (configList == null || configList.Count == 0)
263 {
264 return;
265 }
266 var itemContainer = new StackPanel();
267
268 var sortList = configList.OrderBy(m => m.Attribute.Index).ToList();
269 for (int i = 0; i < sortList.Count; i++)
270 {
271 var config = sortList[i];
272
273 var control = RenderConfigItem(config.Attribute, config.PropertyInfo);
274 if (control != null)
275 {
276 itemContainer.Children.Add(control);
277
278 if (i != sortList.Count - 1)
279 {
280 // 添加分割线
281 var spliteLine = new Border();
282 spliteLine.Height = 1;
283 spliteLine.Background = SpliteLineBrush;
284 itemContainer.Children.Add(spliteLine);
285 }
286 }
287
288
289 }
290
291 var groupControl = GetCreateGroupContainer(itemContainer, groupName);
292 Container.Children.Add(groupControl);
293 }
294
295 private StackPanel GetCreateGroupContainer(UIElement item, string groupName = null)
296 {
297 var container = new StackPanel();
298 container.Margin = new Thickness(0, 10, 0, 20);
299 if (groupName != null)
300 {
301 var groupNameControl = new TextBlock();
302 groupNameControl.Text = groupName;
303 groupNameControl.FontSize = 14;
304 groupNameControl.Margin = new Thickness(0, 0, 0, 10);
305 container.Children.Add(groupNameControl);
306 }
307
308 var border = new Border();
309 border.Background = Background;
310 border.CornerRadius = new CornerRadius(6);
311 border.BorderBrush = BorderBrush;
312 //border.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#ededed"));
313 border.BorderThickness = new Thickness(1);
314 border.Child = item;
315
316 container.Children.Add(border);
317
318 return container;
319 }
320
321
322 private UIElement RenderConfigItem(ConfigAttribute attribute, PropertyInfo pi)
323 {
324 UIElement uIElement = null;
325 if (pi.PropertyType == typeof(bool))
326 {
327 uIElement = RenderBooleanConfigControl(attribute, pi);
328 }
329 else if (pi.PropertyType == typeof(List<string>))
330 {
331 uIElement = RenderListStringConfigControl(attribute, pi);
332 }
333 else if (pi.PropertyType == typeof(int))
334 {
335 uIElement = RenderOptionsConfigControl(attribute, pi);
336 }
337 else if (pi.PropertyType == typeof(string))
338 {
339 if (pi.Name.LastIndexOf("Color") != -1)
340 {
341 uIElement = RenderColorConfigControl(attribute, pi);
342 }
343 }
344 return uIElement;
345 }
346
347 /// <summary>
348 /// 渲染颜色选择配置项
349 /// </summary>
350 /// <param name="configAttribute"></param>
351 /// <param name="pi"></param>
352 /// <returns></returns>
353 private UIElement RenderColorConfigControl(ConfigAttribute configAttribute, PropertyInfo pi)
354 {
355 var control = new Base.ColorSelect();
356 control.Color = (string)pi.GetValue(Data);
357 control.OnSelected += (e, c) =>
358 {
359 pi.SetValue(configData, control.Color);
360 isCanRender = false;
361 Data = configData;
362 };
363
364 var item = new SettingPanelItem();
365 item.Init(configAttribute, control);
366
367 pi.SetValue(configData, pi.GetValue(Data));
368 return item;
369 }
370
371 /// <summary>
372 /// 渲染下拉选择配置项
373 /// </summary>
374 /// <param name="configAttribute"></param>
375 /// <param name="pi"></param>
376 /// <returns></returns>
377 private UIElement RenderOptionsConfigControl(ConfigAttribute configAttribute, PropertyInfo pi)
378 {
379 var control = new Select.Select();
380 var optionsArr = configAttribute.Options.Split('|');
381
382 var options = new List<SelectItemModel>();
383 for (int i = 0; i < optionsArr.Length; i++)
384 {
385 var name = optionsArr[i];
386 options.Add(new SelectItemModel()
387 {
388 Name = name,
389 Data = i
390 });
391 }
392 control.Options = options;
393 control.IsShowIcon = false;
394 control.SelectedItem = options[(int)pi.GetValue(Data)];
395 control.Tag = this;
396 control.OnSelectedItemChanged += (e, c) =>
397 {
398 pi.SetValue(configData, control.SelectedItem.Data);
399
400 isCanRender = false;
401 Data = configData;
402 };
403 //var inputControl = new Toggle.Toggle();
404 //inputControl.ToggleChanged += (e, c) =>
405 //{
406 // pi.SetValue(configData, inputControl.IsChecked);
407
408 // Data = configData;
409 //};
410
411 //inputControl.IsChecked = (bool)pi.GetValue(Data);
412
413 var item = new SettingPanelItem();
414 item.Init(configAttribute, control);
415
416 pi.SetValue(configData, pi.GetValue(Data));
417 return item;
418 }
419
420 private UIElement RenderBooleanConfigControl(ConfigAttribute configAttribute, PropertyInfo pi)
421 {
422 var inputControl = new Toggle.Toggle();
423 inputControl.ToggleChanged += (e, c) =>
424 {
425 pi.SetValue(configData, inputControl.IsChecked);
426
427 isCanRender = false;
428 Data = configData;
429 };
430
431 inputControl.IsChecked = (bool)pi.GetValue(Data);
432
433 var item = new SettingPanelItem();
434 item.Init(configAttribute, inputControl);
435
436 pi.SetValue(configData, pi.GetValue(Data));
437 return item;
438 }
439
440 private UIElement RenderListStringConfigControl(ConfigAttribute configAttribute, PropertyInfo pi)
441 {
442 var list = pi.GetValue(Data) as List<string>;
443
444 var listControl = new BaseList();
445 listControl.MaxHeight = 200;
446 listControl.Loaded += (sender, args) =>
447 {
448 listControl.Items.CollectionChanged += (o, e) =>
449 {
450 var newData = new List<string>();
451 foreach (var item in listControl.Items)
452 {
453 newData.Add(item.ToString());
454 }
455 pi.SetValue(configData, newData);
456
457 isCanRender = false;
458 Data = configData;
459 };
460 };
461 listControl.Margin = new Thickness(15, 0, 15, 10);
462
463 if (list != null)
464 {
465 foreach (string item in list)
466 {
467 listControl.Items.Add(item);
468 }
469 }
470 else
471 {
472 list = new List<string>();
473 }
474 var contextMenu = new ContextMenu();
475
476 var contextMenuItemDel = new MenuItem();
477 contextMenuItemDel.Header = "移除";
478 contextMenuItemDel.Click += (e, c) =>
479 {
480 listControl.Items.Remove(listControl.SelectedItem);
481 };
482 var contextMenuItemCopy = new MenuItem();
483 contextMenuItemCopy.Header = "复制内容";
484 contextMenuItemCopy.Click += (e, c) =>
485 {
486 Clipboard.SetText(listControl.SelectedItem);
487 };
488 contextMenu.Items.Add(contextMenuItemCopy);
489 contextMenu.Items.Add(new Separator());
490 contextMenu.Items.Add(contextMenuItemDel);
491 listControl.ContextMenu = contextMenu;
492
493
494 // 添加输入框
495 var addInputBox = new InputBox();
496 addInputBox.Placeholder = configAttribute.Placeholder;
497 addInputBox.Margin = new Thickness(0, 0, 10, 0);
498
499
500 //添加
501 var addBtn = new Button.Button();
502 //addBtn.Margin = new Thickness(15, 0, 15, 10);
503 addBtn.Content = "添加";
504
505 addBtn.Click += (e, c) =>
506 {
507 if (addInputBox.Text == String.Empty || list.Contains(addInputBox.Text))
508 {
509 addInputBox.Error = configAttribute.Name + (addInputBox.Text == String.Empty ? "不能为空" : "已存在");
510 addInputBox.ShowError();
511 return;
512 }
513 //list.Add(addInputBox.Text);
514 listControl.Items.Add(addInputBox.Text);
515 addInputBox.Text = String.Empty;
516
517
518
519 };
520 pi.SetValue(configData, list);
521
522 IconButton moreActionBtn = new IconButton();
523 moreActionBtn.VerticalAlignment = VerticalAlignment.Center;
524 moreActionBtn.HorizontalAlignment = HorizontalAlignment.Right;
525 moreActionBtn.Margin = new Thickness(0, 5, 5, 0);
526 moreActionBtn.Icon = Base.IconTypes.More;
527
528 var moreActionMenu = new ContextMenu();
529
530 moreActionBtn.MouseLeftButtonUp += (e, c) =>
531 {
532 moreActionMenu.IsOpen = true;
533 };
534 bool isHasMoreAction = false;
535 if (configAttribute.IsCanImportExport)
536 {
537 // 允许导入导出
538 isHasMoreAction = true;
539
540 // 导入操作
541 var importMenuItem = new MenuItem();
542 importMenuItem.Header = "导入";
543 importMenuItem.Click += (e, c) =>
544 {
545 Microsoft.Win32.OpenFileDialog ofd = new Microsoft.Win32.OpenFileDialog();
546 ofd.Title = "选择文件";
547 ofd.Filter = "json(*.json)|*.json";
548 ofd.FileName = configAttribute.Name;
549
550 bool? result = ofd.ShowDialog();
551 if (result == true)
552 {
553 try
554 {
555 List<string> data = JsonConvert.DeserializeObject<List<string>>(File.ReadAllText(ofd.FileName));
556 if (data == null)
557 {
558 MessageBox.Show("文件格式有误或者数据为空,请选择有效的导出文件。");
559 }
560 else
561 {
562 if (MessageBox.Show("导入将覆盖现有配置,确定吗?", "注意", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
563 {
564 pi.SetValue(configData, data);
565 listControl.Items.Clear();
566 foreach (string item in data)
567 {
568 listControl.Items.Add(item);
569 }
570 MessageBox.Show("导入完成!", "提示");
571 }
572 }
573 }
574 catch (Exception ex)
575 {
576 Logger.Error($"导入配置“{configAttribute.Name}”时失败:{ex.Message}");
577 MessageBox.Show("导入失败!", "提示");
578 }
579 }
580
581 };
582
583 // 导出操作
584 var exportMenuItem = new MenuItem();
585 exportMenuItem.Header = "导出";
586 exportMenuItem.Click += (e, c) =>
587 {
588 try
589 {
590 Microsoft.Win32.SaveFileDialog sfd = new Microsoft.Win32.SaveFileDialog();
591 sfd.Title = "选择文件";
592 sfd.Filter = "json(*.json)|*.json";
593 sfd.FileName = configAttribute.Name + "导出配置";
594
595 bool? result = sfd.ShowDialog();
596 if (result == true)
597 {
598 File.WriteAllText(sfd.FileName, JsonConvert.SerializeObject(listControl.Items));
599 MessageBox.Show("导出完成", "提示");
600 }
601 }
602 catch (Exception ex)
603 {
604 Logger.Error($"导出配置“{configAttribute.Name}”时失败:{ex.Message}");
605 MessageBox.Show("导出失败!", "提示");
606 }
607
608 };
609
610
611 moreActionMenu.Items.Add(importMenuItem);
612 moreActionMenu.Items.Add(exportMenuItem);
613 }
614
615
616 var inputPanel = new Grid();
617 inputPanel.ColumnDefinitions.Add(
618 new ColumnDefinition()
619 {
620 Width = new GridLength(10, GridUnitType.Star)
621 });
622 inputPanel.ColumnDefinitions.Add(
623 new ColumnDefinition()
624 {
625 Width = new GridLength(2, GridUnitType.Star)
626 });
627
628 inputPanel.Margin = new Thickness(15, 10, 15, 10);
629 Grid.SetColumn(addInputBox, 0);
630 Grid.SetColumn(addBtn, 1);
631
632 inputPanel.Children.Add(addInputBox);
633 inputPanel.Children.Add(addBtn);
634
635 // 标题和说明
636
637 var description = new TextBlock();
638 description.Text = configAttribute.Description;
639 description.Margin = new Thickness(10, 10, 10, 0);
640 description.Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#989CA1"));
641 var container = new StackPanel();
642
643 var head = new Grid();
644 head.ColumnDefinitions.Add(
645 new ColumnDefinition()
646 {
647 Width = new GridLength(10, GridUnitType.Star)
648 });
649 head.ColumnDefinitions.Add(
650 new ColumnDefinition()
651 {
652 Width = new GridLength(2, GridUnitType.Star)
653 });
654 Grid.SetColumn(description, 0);
655 head.Children.Add(description);
656
657 // 更多操作按钮
658 if (isHasMoreAction)
659 {
660 Grid.SetColumn(moreActionBtn, 1);
661 head.Children.Add(moreActionBtn);
662 }
663
664 container.Children.Add(head);
665 container.Children.Add(inputPanel);
666 container.Children.Add(listControl);
667 return container;
668 }
669 }
670}