跳到主要内容

github/tai

公开

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

浏览文件

Charts.cs

1498 行 · 50.4 KB历史下载
1using Core.Librarys;
2using Core.Models;
3using System;
4using System.Collections.Generic;
5using System.Diagnostics;
6using System.Linq;
7using System.Text;
8using System.Threading.Tasks;
9using System.Windows;
10using System.Windows.Controls;
11using System.Windows.Data;
12using System.Windows.Documents;
13using System.Windows.Input;
14using System.Windows.Media;
15using System.Windows.Media.Imaging;
16using System.Windows.Navigation;
17using System.Windows.Shapes;
18using System.Windows.Threading;
19using UI.Base;
20using UI.Controls.Base;
21using UI.Controls.Charts.Model;
22using UI.Controls.Input;
23using UI.Extensions;
24
25namespace UI.Controls.Charts
26{
27 public class Charts : Control
28 {
29 #region ChartsType 样式类型
30 /// <summary>
31 /// 样式类型
32 /// </summary>
33 public ChartsType ChartsType
34 {
35 get { return (ChartsType)GetValue(ChartsTypeProperty); }
36 set { SetValue(ChartsTypeProperty, value); }
37 }
38 public static readonly DependencyProperty ChartsTypeProperty =
39 DependencyProperty.Register("ChartsType",
40 typeof(ChartsType),
41 typeof(Charts));
42
43
44 #endregion
45 #region MaxValueLimit 限制最大值
46 /// <summary>
47 /// 样式类型
48 /// </summary>
49 public double MaxValueLimit
50 {
51 get { return (double)GetValue(MaxValueLimitProperty); }
52 set { SetValue(MaxValueLimitProperty, value); }
53 }
54 public static readonly DependencyProperty MaxValueLimitProperty =
55 DependencyProperty.Register("MaxValueLimit",
56 typeof(double),
57 typeof(Charts),
58 new PropertyMetadata((double)0));
59
60
61 #endregion
62 #region ShowLimit 显示条数限制
63 /// <summary>
64 /// 样式类型
65 /// </summary>
66 public int ShowLimit
67 {
68 get { return (int)GetValue(ShowLimitProperty); }
69 set { SetValue(ShowLimitProperty, value); }
70 }
71 public static readonly DependencyProperty ShowLimitProperty =
72 DependencyProperty.Register("ShowLimit",
73 typeof(int),
74 typeof(Charts),
75 new PropertyMetadata((int)0));
76
77
78 #endregion
79 #region Data 数据
80 /// <summary>
81 /// 样式类型
82 /// </summary>
83 public IEnumerable<ChartsDataModel> Data
84 {
85 get { return (IEnumerable<ChartsDataModel>)GetValue(DataProperty); }
86 set { SetValue(DataProperty, value); }
87 }
88 public static readonly DependencyProperty DataProperty =
89 DependencyProperty.Register("Data",
90 typeof(IEnumerable<ChartsDataModel>),
91 typeof(Charts),
92 new PropertyMetadata(null, new PropertyChangedCallback(OnPropertyChanged))
93 );
94
95 public IEnumerable<ChartsDataModel> ListViewBindingData
96 {
97 get { return (IEnumerable<ChartsDataModel>)GetValue(ListViewBindingDataProperty); }
98 set { SetValue(ListViewBindingDataProperty, value); }
99 }
100 public static readonly DependencyProperty ListViewBindingDataProperty =
101 DependencyProperty.Register("ListViewBindingData",
102 typeof(IEnumerable<ChartsDataModel>),
103 typeof(Charts));
104 #endregion
105 #region LoadingPlaceholderCount 加载中时占位显示条数
106 /// <summary>
107 /// 加载中时占位显示条数
108 /// </summary>
109 public int LoadingPlaceholderCount
110 {
111 get { return (int)GetValue(LoadingPlaceholderCountProperty); }
112 set { SetValue(LoadingPlaceholderCountProperty, value); }
113 }
114 public static readonly DependencyProperty LoadingPlaceholderCountProperty =
115 DependencyProperty.Register("LoadingPlaceholderCount",
116 typeof(int),
117 typeof(Charts),
118 new PropertyMetadata((int)3));
119
120
121 #endregion
122 #region IsLoading 是否在加载中
123 /// <summary>
124 /// 是否在加载中
125 /// </summary>
126 public bool IsLoading
127 {
128 get { return (bool)GetValue(IsLoadingProperty); }
129 set { SetValue(IsLoadingProperty, value); }
130 }
131 public static readonly DependencyProperty IsLoadingProperty =
132 DependencyProperty.Register("IsLoading",
133 typeof(bool),
134 typeof(Charts),
135 new PropertyMetadata(false, new PropertyChangedCallback(OnPropertyChanged)));
136
137
138 #endregion
139
140 #region IsCanScroll 是否可以滚动
141 /// <summary>
142 /// 是否在加载中
143 /// </summary>
144 public bool IsCanScroll
145 {
146 get { return (bool)GetValue(IsCanScrollProperty); }
147 set { SetValue(IsCanScrollProperty, value); }
148 }
149 public static readonly DependencyProperty IsCanScrollProperty =
150 DependencyProperty.Register("IsCanScroll",
151 typeof(bool),
152 typeof(Charts),
153 new PropertyMetadata(true, new PropertyChangedCallback(OnPropertyChanged)));
154
155
156 #endregion
157 #region ClickCommand 点击命令
158 /// <summary>
159 /// 点击命令
160 /// </summary>
161 public Command ClickCommand
162 {
163 get { return (Command)GetValue(ClickCommandProperty); }
164 set { SetValue(ClickCommandProperty, value); }
165 }
166 public static readonly DependencyProperty ClickCommandProperty =
167 DependencyProperty.Register("ClickCommand",
168 typeof(Command),
169 typeof(Charts));
170
171
172 #endregion
173 #region 柱状图信息数据
174 /// <summary>
175 /// 柱状图信息数据
176 /// </summary>
177 public List<ChartColumnInfoModel> ColumnInfoList
178 {
179 get { return (List<ChartColumnInfoModel>)GetValue(ColumnInfoListProperty); }
180 set { SetValue(ColumnInfoListProperty, value); }
181 }
182 public static readonly DependencyProperty ColumnInfoListProperty =
183 DependencyProperty.Register("ColumnInfoList",
184 typeof(List<ChartColumnInfoModel>),
185 typeof(Charts)
186 );
187
188
189 #endregion
190 #region 中间值
191 /// <summary>
192 /// 中间值
193 /// </summary>
194 public string Median
195 {
196 get { return (string)GetValue(MedianProperty); }
197 set { SetValue(MedianProperty, value); }
198 }
199 public static readonly DependencyProperty MedianProperty =
200 DependencyProperty.Register("Median",
201 typeof(string),
202 typeof(Charts)
203 );
204
205
206 #endregion
207 #region 最大值
208 /// <summary>
209 /// 最大值
210 /// </summary>
211 public string Maximum
212 {
213 get { return (string)GetValue(MaximumProperty); }
214 set { SetValue(MaximumProperty, value); }
215 }
216 public static readonly DependencyProperty MaximumProperty =
217 DependencyProperty.Register("Maximum",
218 typeof(string),
219 typeof(Charts)
220 );
221
222
223 #endregion
224 #region 单位
225 /// <summary>
226 /// 单位
227 /// </summary>
228 public string Unit
229 {
230 get { return (string)GetValue(UnitProperty); }
231 set { SetValue(UnitProperty, value); }
232 }
233 public static readonly DependencyProperty UnitProperty =
234 DependencyProperty.Register("Unit",
235 typeof(string),
236 typeof(Charts)
237 );
238
239
240 #endregion
241 #region 总计
242 /// <summary>
243 /// 总计
244 /// </summary>
245 public string Total
246 {
247 get { return (string)GetValue(TotalProperty); }
248 set { SetValue(TotalProperty, value); }
249 }
250 public static readonly DependencyProperty TotalProperty =
251 DependencyProperty.Register("Total",
252 typeof(string),
253 typeof(Charts)
254 );
255
256
257 #endregion
258
259 #region 数据值类型
260 /// <summary>
261 /// 数据值类型
262 /// </summary>
263 public ChartDataValueType DataValueType
264 {
265 get { return (ChartDataValueType)GetValue(DataValueTypeProperty); }
266 set { SetValue(DataValueTypeProperty, value); }
267 }
268 public static readonly DependencyProperty DataValueTypeProperty =
269 DependencyProperty.Register("DataValueType",
270 typeof(ChartDataValueType),
271 typeof(Charts)
272 );
273
274
275 #endregion
276 #region
277
278 public int NameIndexStart
279 {
280 get { return (int)GetValue(NameIndexStartProperty); }
281 set { SetValue(NameIndexStartProperty, value); }
282 }
283 public static readonly DependencyProperty NameIndexStartProperty =
284 DependencyProperty.Register("NameIndexStart",
285 typeof(int),
286 typeof(Charts), new PropertyMetadata((int)0)
287 );
288
289
290 #endregion
291 #region 是否空数据
292 /// <summary>
293 /// 是否空数据
294 /// </summary>
295 public bool IsEmpty
296 {
297 get { return (bool)GetValue(IsEmptyProperty); }
298 set { SetValue(IsEmptyProperty, value); }
299 }
300 public static readonly DependencyProperty IsEmptyProperty =
301 DependencyProperty.Register("IsEmpty",
302 typeof(bool),
303 typeof(Charts),
304 new PropertyMetadata(false, new PropertyChangedCallback(OnPropertyChanged)));
305
306
307 #endregion
308 #region 是否显示总计值(仅柱状图有效,默认显示)
309 /// <summary>
310 /// 是否显示总计值(仅柱状图有效,默认显示)
311 /// </summary>
312 public bool IsShowTotal
313 {
314 get { return (bool)GetValue(IsShowTotalProperty); }
315 set { SetValue(IsShowTotalProperty, value); }
316 }
317 public static readonly DependencyProperty IsShowTotalProperty =
318 DependencyProperty.Register("IsShowTotal",
319 typeof(bool),
320 typeof(Charts),
321 new PropertyMetadata(true));
322
323
324 #endregion
325 #region 是否允许选择项(仅柱状图有效,默认不允许)
326 /// <summary>
327 /// 是否允许选择项(仅柱状图有效,默认不允许)
328 /// </summary>
329 public bool IsCanColumnSelect
330 {
331 get { return (bool)GetValue(IsCanColumnSelectProperty); }
332 set { SetValue(IsCanColumnSelectProperty, value); }
333 }
334 public static readonly DependencyProperty IsCanColumnSelectProperty =
335 DependencyProperty.Register("IsCanColumnSelect",
336 typeof(bool),
337 typeof(Charts),
338 new PropertyMetadata(false));
339
340
341 #endregion
342 #region 是否显示搜索(仅列表样式有效,默认不显示)
343 /// <summary>
344 /// 是否显示搜索(仅列表样式有效,默认不显示)
345 /// </summary>
346 public bool IsSearch
347 {
348 get { return (bool)GetValue(IsSearchProperty); }
349 set { SetValue(IsSearchProperty, value); }
350 }
351 public static readonly DependencyProperty IsSearchProperty =
352 DependencyProperty.Register("IsSearch",
353 typeof(bool),
354 typeof(Charts),
355 new PropertyMetadata(false));
356
357
358 #endregion
359 #region 选中列索引(仅柱状图有效)
360 /// <summary>
361 /// 选中列索引(仅柱状图有效)
362 /// </summary>
363 public int ColumnSelectedIndex
364 {
365 get { return (int)GetValue(ColumnSelectedIndexProperty); }
366 set { SetValue(ColumnSelectedIndexProperty, value); }
367 }
368 public static readonly DependencyProperty ColumnSelectedIndexProperty =
369 DependencyProperty.Register("ColumnSelectedIndex",
370 typeof(int),
371 typeof(Charts), new PropertyMetadata(-1, new PropertyChangedCallback(OnPropertyChanged)));
372
373
374 #endregion
375 #region IsShowBadge 是否显示徽章
376 /// <summary>
377 /// 是否显示徽章(默认不显示)
378 /// </summary>
379 public bool IsShowBadge
380 {
381 get { return (bool)GetValue(IsShowBadgeProperty); }
382 set { SetValue(IsShowBadgeProperty, value); }
383 }
384 public static readonly DependencyProperty IsShowBadgeProperty =
385 DependencyProperty.Register("IsShowBadge",
386 typeof(bool),
387 typeof(Charts),
388 new PropertyMetadata(false));
389
390
391 #endregion
392 public bool IsShowValuesPopup
393 {
394 get { return (bool)GetValue(IsShowValuesPopupProperty); }
395 set { SetValue(IsShowValuesPopupProperty, value); }
396 }
397 public static readonly DependencyProperty IsShowValuesPopupProperty =
398 DependencyProperty.Register("IsShowValuesPopup",
399 typeof(bool),
400 typeof(Charts),
401 new PropertyMetadata(false));
402 public FrameworkElement ValuesPopupPlacementTarget
403 {
404 get { return (FrameworkElement)GetValue(ValuesPopupPlacementTargetProperty); }
405 set { SetValue(ValuesPopupPlacementTargetProperty, value); }
406 }
407 public static readonly DependencyProperty ValuesPopupPlacementTargetProperty =
408 DependencyProperty.Register("ValuesPopupPlacementTarget",
409 typeof(FrameworkElement),
410 typeof(Charts));
411
412 public List<ChartColumnInfoModel> ColumnValuesInfoList
413 {
414 get { return (List<ChartColumnInfoModel>)GetValue(ColumnValuesInfoListProperty); }
415 set { SetValue(ColumnValuesInfoListProperty, value); }
416 }
417 public static readonly DependencyProperty ColumnValuesInfoListProperty =
418 DependencyProperty.Register("ColumnValuesInfoList",
419 typeof(List<ChartColumnInfoModel>),
420 typeof(Charts)
421 );
422
423 public double ValuesPopupHorizontalOffset
424 {
425 get { return (double)GetValue(ValuesPopupHorizontalOffsetProperty); }
426 set { SetValue(ValuesPopupHorizontalOffsetProperty, value); }
427 }
428 public static readonly DependencyProperty ValuesPopupHorizontalOffsetProperty =
429 DependencyProperty.Register("ValuesPopupHorizontalOffset",
430 typeof(double),
431 typeof(Charts));
432
433 /// <summary>
434 /// 数据最大值
435 /// </summary>
436 public double DataMaximum
437 {
438 get { return (double)GetValue(DataMaximumProperty); }
439 set { SetValue(DataMaximumProperty, value); }
440 }
441 public static readonly DependencyProperty DataMaximumProperty =
442 DependencyProperty.Register("DataMaximum",
443 typeof(double),
444 typeof(Charts),
445 new PropertyMetadata((double)0)
446 );
447 public double DataMaxValue
448 {
449 get { return (double)GetValue(DataMaxValueProperty); }
450 set { SetValue(DataMaxValueProperty, value); }
451 }
452 public static readonly DependencyProperty DataMaxValueProperty =
453 DependencyProperty.Register("DataMaxValue",
454 typeof(double),
455 typeof(Charts),
456 new PropertyMetadata((double)0)
457 );
458
459 #region 是否以堆叠的形式展示(仅柱状图有效)
460 /// <summary>
461 /// 是否以堆叠的形式展示(仅柱状图有效)
462 /// </summary>
463 public bool IsStack
464 {
465 get { return (bool)GetValue(IsStackProperty); }
466 set { SetValue(IsStackProperty, value); }
467 }
468 public static readonly DependencyProperty IsStackProperty =
469 DependencyProperty.Register("IsStack",
470 typeof(bool),
471 typeof(Charts),
472 new PropertyMetadata(false, new PropertyChangedCallback(OnPropertyChanged)));
473
474
475 #endregion
476 #region 是否显示分类信息
477 /// <summary>
478 /// 是否显示分类信息(仅柱状图有效)
479 /// </summary>
480 public bool IsShowCategory
481 {
482 get { return (bool)GetValue(IsShowCategoryProperty); }
483 set { SetValue(IsShowCategoryProperty, value); }
484 }
485 public static readonly DependencyProperty IsShowCategoryProperty =
486 DependencyProperty.Register("IsShowCategory",
487 typeof(bool),
488 typeof(Charts),
489 new PropertyMetadata(true, new PropertyChangedCallback(OnPropertyChanged)));
490
491
492 #endregion
493 #region 图标大小(仅列表样式有效)
494 /// <summary>
495 /// 图标大小(仅列表样式有效)
496 /// </summary>
497 public double IconSize
498 {
499 get { return (double)GetValue(IconSizeProperty); }
500 set { SetValue(IconSizeProperty, value); }
501 }
502 public static readonly DependencyProperty IconSizeProperty =
503 DependencyProperty.Register("IconSize",
504 typeof(double),
505 typeof(Charts),
506 new PropertyMetadata((double)25));
507 #endregion
508
509 private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
510 {
511 var charts = (d as Charts);
512 if (e.Property == DataProperty && e.OldValue != e.NewValue)
513 {
514 charts.Render();
515 }
516 if (e.Property == IsLoadingProperty)
517 {
518 charts.Render();
519 }
520 if (e.Property == ColumnSelectedIndexProperty && e.OldValue != e.NewValue)
521 {
522 charts.SetColBorderActiveBg((int)e.OldValue, (int)e.NewValue);
523 }
524
525 }
526
527 #region 右键菜单
528 public ContextMenu ItemMenu
529 {
530 get { return (ContextMenu)GetValue(ItemMenuProperty); }
531 set { SetValue(ItemMenuProperty, value); }
532 }
533 public static readonly DependencyProperty ItemMenuProperty =
534 DependencyProperty.Register("ItemMenu",
535 typeof(ContextMenu),
536 typeof(Charts));
537 #endregion
538 /// <summary>
539 /// 点击项目时发生
540 /// </summary>
541 public event EventHandler OnItemClick;
542
543 /// <summary>
544 /// 容器
545 /// </summary>
546 private StackPanel _typeATempContainer;
547 private WrapPanel CardContainer;
548 private Grid MonthContainer;
549 private Border RadarContainer;
550 private ListView _listView;
551 private Canvas _typeColumnCanvas;
552 private Canvas _commonCanvas;
553
554 private Dictionary<int, List<Rectangle>> _typeColValueRectMap;
555 private Dictionary<int, Rectangle> _typeColBorderRectMap;
556 /// <summary>
557 /// 是否在渲染中
558 /// </summary>
559 private bool isRendering = false;
560 /// <summary>
561 /// 计算最大值
562 /// </summary>
563 private double maxValue = 0;
564
565 /// <summary>
566 /// 搜索关键字(仅列表样式有效
567 /// </summary>
568 private string searchKey;
569 public Charts()
570 {
571 DefaultStyleKey = typeof(Charts);
572 }
573
574 public override void OnApplyTemplate()
575 {
576 base.OnApplyTemplate();
577 CardContainer = GetTemplateChild("CardContainer") as WrapPanel;
578 MonthContainer = GetTemplateChild("MonthContainer") as Grid;
579 RadarContainer = GetTemplateChild("Radar") as Border;
580 _listView = GetTemplateChild("ListView") as ListView;
581 _typeATempContainer = GetTemplateChild("TypeATempContainer") as StackPanel;
582 _commonCanvas = GetTemplateChild("CommonCanvasContainer") as Canvas;
583
584 if (ChartsType == ChartsType.Column)
585 {
586 _typeColumnCanvas = GetTemplateChild("TypeColumnCanvas") as Canvas;
587 _typeColumnCanvas.SizeChanged += _typeColumnCanvas_SizeChanged;
588 }
589 if (ChartsType == ChartsType.Pie)
590 {
591 _commonCanvas.SizeChanged += _commonCanvas_SizeChanged;
592 }
593 Render();
594 }
595
596
597 private void _commonCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
598 {
599 RenderPieStyle();
600 }
601
602 private void _typeColumnCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
603 {
604 RenderColumnStyle();
605 }
606
607 private void Render()
608 {
609 if (IsLoading)
610 {
611 RenderLoadingPlaceholder();
612 }
613 else
614 {
615 RenderData();
616 }
617 }
618 private void RenderLoadingPlaceholder()
619 {
620 if (LoadingPlaceholderCount > 0 && CardContainer != null)
621 {
622 CardContainer.Children.Clear();
623 _typeATempContainer.Children.Clear();
624
625 for (int i = 0; i < LoadingPlaceholderCount; i++)
626 {
627 switch (ChartsType)
628 {
629 case ChartsType.List:
630 var item = new ChartsItemTypeList();
631 item.IsLoading = true;
632 _typeATempContainer.Children.Add(item);
633 break;
634 case ChartsType.Card:
635 var card = new ChartsItemTypeCard();
636 card.IsLoading = true;
637 CardContainer.Children.Add(card);
638 break;
639 }
640 }
641
642 }
643 }
644 private void Calculate()
645 {
646 if (Data == null || ChartsType == ChartsType.Column)
647 {
648 return;
649 }
650 // 计算最大值
651 // 如果设置了固定的最大值则使用,否则查找数据中的最大值
652 maxValue = MaxValueLimit > 0 ? MaxValueLimit : Data.Count() > 0 ? Data.Max(m => m.Value) : 0;
653
654 if (ChartsType == ChartsType.List)
655 {
656 //不允许最大值小于10,否则效果不好看
657 if (maxValue < 10)
658 {
659 maxValue = 10;
660 }
661 // 适当增加最大值
662 maxValue = Math.Round(maxValue / 2, MidpointRounding.AwayFromZero) * 2 + 2;
663
664 var countText = GetTemplateChild("ACount") as Run;
665 if (countText != null)
666 {
667 countText.Text = Data.Count().ToString();
668 }
669 }
670
671 DataMaxValue = maxValue;
672
673
674 }
675
676 private void RenderData()
677 {
678 if (isRendering || CardContainer == null)
679 {
680 return;
681 }
682
683
684 Calculate();
685 CardContainer.Children.Clear();
686 MonthContainer.Children.Clear();
687 _typeATempContainer.Children.Clear();
688 _commonCanvas.Children.Clear();
689
690 RadarContainer.Child = null;
691 ListViewBindingData = null;
692
693 if (Data == null || Data.Count() <= 0)
694 {
695 CardContainer.Children.Add(new EmptyData());
696 RadarContainer.Child = new EmptyData();
697 _typeATempContainer.Children.Add(new EmptyData());
698 _commonCanvas.Children.Add(new EmptyData());
699
700 IsEmpty = true;
701 return;
702 }
703 else
704 {
705 IsEmpty = false;
706 }
707
708 isRendering = true;
709 switch (ChartsType)
710 {
711 case ChartsType.List:
712 RenderListStyle();
713 break;
714 case ChartsType.Card:
715 RenderCardStyle();
716 break;
717 case ChartsType.Month:
718 RenderMonthStyle();
719 break;
720 case ChartsType.Column:
721 RenderColumnStyle();
722 break;
723 case ChartsType.Radar:
724 RenderLadarStyle();
725 break;
726 case ChartsType.Pie:
727 RenderPieStyle();
728 break;
729 }
730
731 }
732
733 #region 渲染列表样式
734 private void RenderListStyle()
735 {
736 ListViewBindingData = Data.OrderByDescending(x => x.Value).ToList();
737 if (ShowLimit > 0)
738 {
739 ListViewBindingData = ListViewBindingData.Take(ShowLimit);
740 }
741
742 isRendering = false;
743 var searchBox = GetTemplateChild("ASearchBox") as InputBox;
744 if (searchBox != null)
745 {
746 searchBox.TextChanged += SearchBox_TextChanged;
747 }
748
749 _listView.MouseLeftButtonUp += _listView_MouseLeftButtonUp;
750 _listView.MouseRightButtonUp += _listView_MouseRightButtonUp;
751
752 if (!IsCanScroll)
753 {
754 _listView.PreviewMouseWheel += _listView_PreviewMouseWheel;
755 }
756 }
757
758 private void _listView_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
759 {
760 if (!e.Handled)
761 {
762 e.Handled = true;
763
764 var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
765 eventArg.RoutedEvent = UIElement.MouseWheelEvent;
766 eventArg.Source = sender;
767
768 var parent = ((Control)sender).Parent as UIElement;
769 parent.RaiseEvent(eventArg);
770 }
771 }
772
773 private void _listView_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
774 {
775 if (_listView.SelectedItem != null)
776 {
777 if (ItemMenu != null)
778 {
779 ItemMenu.IsOpen = true;
780 ItemMenu.Tag = _listView.SelectedItem;
781 }
782 }
783 }
784
785 private void _listView_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
786 {
787 if (_listView.SelectedItem != null)
788 {
789 OnItemClick?.Invoke(_listView.SelectedItem, null);
790 ClickCommand?.Execute(_listView.SelectedItem);
791 }
792 }
793
794
795 private void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
796 {
797 var box = sender as InputBox;
798 if (box.Text != searchKey)
799 {
800 searchKey = box.Text.ToLower();
801 HandleSearch();
802 }
803 }
804
805 private void HandleSearch()
806 {
807 Task.Run(() =>
808 {
809 Dispatcher.Invoke(() =>
810 {
811 var newListData = new List<ChartsDataModel>();
812
813 foreach (var data in Data)
814 {
815 string content = (data.Name + data.PopupText).ToLower();
816 bool show = content.IndexOf(searchKey) != -1;
817
818 if (!show)
819 {
820 show = searchKey == "忽略" && data.BadgeList.Where(m => m.Type == ChartBadgeType.Ignore).Any();
821 if (data.BadgeList.Any())
822 {
823 // 搜索徽章名(分类名)
824 show = data.BadgeList.Where(m => m.Name.ToLower().Contains(searchKey)).Any();
825 }
826 }
827
828 if (show)
829 {
830 newListData.Add(data);
831 }
832 }
833
834 ListViewBindingData = newListData;
835 });
836
837 });
838 }
839
840
841 //private void RenderTypeAItems(List<ChartsDataModel> data, int start, int count)
842 //{
843
844 // for (int i = start; i < start + count; i++)
845 // {
846
847 // if (i >= data.Count)
848 // {
849 // break;
850 // }
851 // ChartsDataModel item = data[i];
852 // var chartsItem = new ChartsItemTypeA();
853 // chartsItem.Data = item;
854 // chartsItem.ToolTip = item.PopupText;
855 // chartsItem.MaxValue = maxValue;
856 // chartsItem.IsShowBadge = IsShowBadge;
857
858 // if (i == 19 && isLazyloading)
859 // {
860 // chartsItem.Loaded += ChartsItem_Loaded;
861 // }
862 // // 处理点击事件
863 // HandleItemClick(chartsItem, item);
864 // if (!IsCanScroll)
865 // {
866 // NoScrollContainer.Children.Add(chartsItem);
867 // }
868 // else
869 // {
870 // Container.Children.Add(chartsItem);
871 // }
872 // if (ShowLimit > 0 && i == ShowLimit - 1)
873 // {
874 // break;
875 // }
876
877 // }
878
879 //}
880
881 //private void ChartsItem_Loaded(object sender, RoutedEventArgs e)
882 //{
883 // var chartsItem = (ChartsItemTypeA)sender;
884 // chartsItem.Loaded -= ChartsItem_Loaded;
885
886 // int count = Data.Count() - 20;
887 // for (int i = 0; i < count; i++)
888 // {
889 // var pl = new TextBlock();
890 // pl.Height = chartsItem.ActualHeight;
891 // }
892
893 // itemHeight = chartsItem.ActualHeight;
894 //}
895 #endregion
896 #region 渲染卡片样式Card
897 private void RenderCardStyle()
898 {
899 var data = Data.OrderByDescending(x => x.Value).Take(ShowLimit).ToList();
900
901 data.Shuffle();
902
903 foreach (var item in data)
904 {
905 var chartsItem = new ChartsItemTypeCard();
906 chartsItem.Data = item;
907
908 // 处理点击事件
909 HandleItemClick(chartsItem, item);
910
911 chartsItem.MaxValue = maxValue;
912 chartsItem.ToolTip = item.PopupText;
913 CardContainer.Children.Add(chartsItem);
914 }
915 isRendering = false;
916
917 }
918 #endregion
919
920 #region 渲染月份样式
921 private void RenderMonthStyle()
922 {
923 var data = Data.ToList();
924 DateTime month = data[0].DateTime;
925
926 int days = DateTime.DaysInMonth(month.Year, month.Month);
927
928 // 绘制网格
929 var headerGrid = new Grid();
930 headerGrid.Margin = new Thickness(0, 0, 0, 10);
931
932 var dataGrid = new Grid();
933
934 string[] week = { "一", "二", "三", "四", "五", "六", "日" };
935 for (int i = 0; i < 7; i++)
936 {
937
938 dataGrid.ColumnDefinitions.Add(new ColumnDefinition()
939 {
940 Width = new GridLength(1, GridUnitType.Star)
941 });
942 headerGrid.ColumnDefinitions.Add(new ColumnDefinition()
943 {
944 Width = new GridLength(1, GridUnitType.Star)
945 });
946
947 // 填充头部
948 var header = new TextBlock();
949 header.Text = week[i];
950 header.VerticalAlignment = VerticalAlignment.Center;
951 header.HorizontalAlignment = HorizontalAlignment.Center;
952 Grid.SetColumn(header, i);
953 headerGrid.Children.Add(header);
954 }
955
956 for (int i = 0; i < 6; i++)
957 {
958 dataGrid.RowDefinitions.Add(new RowDefinition()
959 {
960 Height = new GridLength(1, GridUnitType.Star)
961 });
962 }
963
964 // 填充空数据
965 for (int i = 0; i < days; i++)
966 {
967 var date = new DateTime(month.Year, month.Month, i + 1);
968 var chartsItem = new ChartsItemTypeMonth();
969 chartsItem.Data = new ChartsDataModel()
970 {
971 DateTime = date,
972 };
973 var location = CalGridLocation(date);
974 Grid.SetColumn(chartsItem, location[0]);
975 Grid.SetRow(chartsItem, location[1]);
976 dataGrid.Children.Add(chartsItem);
977 Debug.WriteLine((i + 1) + " -> " + location[0] + "," + location[1]);
978 }
979
980 // 填充有效数据
981 foreach (var item in data)
982 {
983 if (item.DateTime != null)
984 {
985 int day = item.DateTime.Day;
986
987 Debug.WriteLine("日期:" + day + "号");
988 var chartsItem = new ChartsItemTypeMonth();
989 chartsItem.Data = item;
990 chartsItem.ToolTip = item.PopupText;
991 chartsItem.MaxValue = maxValue;
992
993 // 处理点击事件
994 HandleItemClick(chartsItem, item);
995
996 var location = CalGridLocation(item.DateTime);
997
998 Grid.SetColumn(chartsItem, location[0]);
999 Grid.SetRow(chartsItem, location[1]);
1000 dataGrid.Children.Add(chartsItem);
1001 //if (ShowLimit > 0 && Container.Children.Count == ShowLimit)
1002 //{
1003 // break;
1004 //}
1005 }
1006 }
1007
1008 var sp = new StackPanel();
1009 sp.Children.Add(headerGrid);
1010 sp.Children.Add(dataGrid);
1011
1012 MonthContainer.Children.Add(sp);
1013 isRendering = false;
1014 }
1015
1016 private int[] CalGridLocation(DateTime date)
1017 {
1018 var res = new int[2];
1019 // 判断1号是星期几
1020 int firstDayWeekNum = (int)new DateTime(date.Year, date.Month, 1).DayOfWeek;
1021 if (firstDayWeekNum == 0)
1022 {
1023 firstDayWeekNum = 7;
1024 }
1025
1026 int col = 0;
1027 int row = 0;
1028 if (firstDayWeekNum == 1)
1029 {
1030 col = date.Day - 1;
1031 }
1032 else
1033 {
1034 col = date.Day + firstDayWeekNum - 2;
1035 }
1036 if (col > 6)
1037 {
1038 row = (int)col / 7;
1039
1040 int dayWeekNum = (int)new DateTime(date.Year, date.Month, date.Day).DayOfWeek;
1041 if (dayWeekNum == 0)
1042 {
1043 dayWeekNum = 7;
1044 }
1045 col = dayWeekNum - 1;
1046
1047
1048 }
1049
1050 res[0] = col;
1051 res[1] = row;
1052 return res;
1053 }
1054 #endregion
1055
1056 #region 渲染柱形图
1057 private void SetColBorderActiveBg(int oldIndex, int newIndex)
1058 {
1059 if (!IsCanColumnSelect || _typeColBorderRectMap == null)
1060 {
1061 return;
1062 }
1063
1064 if (_typeColBorderRectMap.ContainsKey(oldIndex))
1065 {
1066 var oldItem = _typeColBorderRectMap[oldIndex];
1067 oldItem.Fill = new SolidColorBrush(Colors.Transparent);
1068 }
1069 if (_typeColBorderRectMap.ContainsKey(newIndex))
1070 {
1071 var background = Application.Current.Resources["ThemeBrush"] as SolidColorBrush;
1072
1073 var checkItem = _typeColBorderRectMap[newIndex];
1074 checkItem.Fill = new SolidColorBrush(background.Color) { Opacity = .1 };
1075 }
1076 }
1077 /// <summary>
1078 /// 渲染柱形图
1079 /// </summary>
1080 private void RenderColumnStyle()
1081 {
1082 if (_typeColumnCanvas == null || _typeColumnCanvas.ActualHeight == 0)
1083 {
1084 return;
1085 }
1086
1087 _typeColumnCanvas.Children.Clear();
1088 ColumnInfoList = null;
1089 _typeColValueRectMap = new Dictionary<int, List<Rectangle>>();
1090 _typeColBorderRectMap = new Dictionary<int, Rectangle>();
1091
1092 maxValue = 0;
1093 Maximum = string.Empty;
1094 Median = string.Empty;
1095 Total = string.Empty;
1096
1097 if (Data == null || Data.Count() == 0)
1098 {
1099 return;
1100 }
1101
1102
1103 double total = 0;
1104 // 查找最大值
1105 int colValueCount = Data.FirstOrDefault().Values.Length;
1106 double[] tempValueArr = new double[colValueCount];
1107
1108 foreach (var item in Data)
1109 {
1110
1111 for (int i = 0; i < colValueCount; i++)
1112 {
1113 tempValueArr[i] += item.Values[i];
1114 }
1115
1116 // 查找最大值
1117 double max = item.Values.Max();
1118 if (max > maxValue)
1119 {
1120 maxValue = max;
1121 }
1122
1123 total += item.Values.Sum();
1124
1125 }
1126
1127 if (DataMaximum > 0)
1128 {
1129 maxValue = DataMaximum;
1130 }
1131 else
1132 {
1133 if (IsStack)
1134 {
1135 maxValue = tempValueArr.Max();
1136 }
1137 }
1138 if (maxValue == 0)
1139 {
1140 maxValue = 10;
1141 }
1142
1143 Maximum = Covervalue(maxValue);
1144 Median = Covervalue((maxValue / 2));
1145 Total = Covervalue(total);
1146
1147 // 列数
1148 int columns = Data.FirstOrDefault().Values.Length;
1149
1150
1151 // 间距
1152 int margin = 5;
1153
1154 if (columns <= 7)
1155 {
1156 margin = 25;
1157 }
1158 else if (columns <= 12)
1159 {
1160 margin = 15;
1161 }
1162 else if (columns >= 20)
1163 {
1164 margin = 2;
1165 }
1166
1167 int columnCount = Data.Count();
1168 var list = Data.ToList();
1169
1170 // 列名高度
1171 const double colNameHeight = 30;
1172 // 列名下边距
1173 const double colNameBottomMargin = 5;
1174 // 画布高度
1175 double canvasHeight = _typeColumnCanvas.ActualHeight - colNameHeight - colNameBottomMargin;
1176 // 画布宽度
1177 double canvasWidth = _typeColumnCanvas.ActualWidth;
1178 // 边框宽度
1179 double columnBorderWidth = _typeColumnCanvas.ActualWidth / columns;
1180 // 列值宽度
1181 double colValueRectWidth = _typeColumnCanvas.ActualWidth / columns - (margin * 2);
1182
1183 for (int i = 0; i < columns; i++)
1184 {
1185 // 绘制列边框
1186 var columnBorder = new Rectangle();
1187 columnBorder.Width = columnBorderWidth;
1188 columnBorder.Height = canvasHeight;
1189 columnBorder.Fill = new SolidColorBrush(Colors.Transparent);
1190 Canvas.SetLeft(columnBorder, i * columnBorderWidth);
1191 Canvas.SetTop(columnBorder, colNameBottomMargin);
1192 Panel.SetZIndex(columnBorder, 999);
1193 _typeColumnCanvas.Children.Add(columnBorder);
1194 _typeColBorderRectMap.Add(i, columnBorder);
1195
1196 // 列名
1197 string colName = list[0].ColumnNames != null && list[0].ColumnNames.Length > 0 ? list[0].ColumnNames[i] : (i + NameIndexStart).ToString();
1198 // 绘制列名
1199 TextBlock colNameText = new TextBlock();
1200 colNameText.TextAlignment = TextAlignment.Center;
1201 colNameText.Width = columnBorderWidth;
1202 colNameText.FontSize = 12;
1203 colNameText.Text = colName;
1204 colNameText.Foreground = UI.Base.Color.Colors.GetFromString("#FF8A8A8A");
1205
1206 Canvas.SetLeft(colNameText, i * columnBorderWidth);
1207 Canvas.SetBottom(colNameText, colNameBottomMargin);
1208 _typeColumnCanvas.Children.Add(colNameText);
1209
1210
1211 var valuesPopupList = new List<ChartColumnInfoModel>();
1212
1213 _typeColValueRectMap.Add(i, new List<Rectangle>());
1214
1215 for (int di = 0; di < columnCount; di++)
1216 {
1217 var item = list[di];
1218 //string colColor = item.Color == null ? UI.Base.Color.Colors.MainColors[di] : item.Color;
1219 string themeColor = ((System.Windows.Media.Color)Application.Current.Resources["ThemeColor"]).ToString();
1220 string colColor = item.Color == null ? themeColor : item.Color;
1221 double value = item.Values[i];
1222
1223
1224 if (value > 0)
1225 {
1226 // 绘制列
1227 var colValueRect = new Rectangle();
1228 colValueRect.Width = colValueRectWidth;
1229 colValueRect.Height = value / maxValue * canvasHeight;
1230 colValueRect.Fill = UI.Base.Color.Colors.GetFromString(colColor);
1231 if (!IsStack)
1232 {
1233 colValueRect.RadiusX = 4;
1234 colValueRect.RadiusY = 4;
1235 }
1236 Canvas.SetLeft(colValueRect, i * columnBorderWidth + margin);
1237 Canvas.SetBottom(colValueRect, colNameHeight);
1238
1239 _typeColumnCanvas.Children.Add(colValueRect);
1240 _typeColValueRectMap[i].Add(colValueRect);
1241
1242 // 列分类统计数据
1243 valuesPopupList.Add(new ChartColumnInfoModel()
1244 {
1245 Color = item.Color,
1246 Name = item.Name,
1247 Icon = item.Icon,
1248 Text = Covervalue(value) + Unit,
1249 Value = value,
1250 });
1251 }
1252 }
1253 var index = i;
1254
1255 columnBorder.MouseEnter += (e, c) =>
1256 {
1257 ColumnValuesInfoList = valuesPopupList.OrderByDescending(m => m.Value).ToList();
1258 ValuesPopupPlacementTarget = columnBorder;
1259 IsShowValuesPopup = valuesPopupList.Count > 0;
1260
1261 ValuesPopupHorizontalOffset = -17.5 + (columnBorder.ActualWidth / 2);
1262
1263 if (ColumnSelectedIndex != index)
1264 {
1265 var themeBrush = Application.Current.Resources["ThemeBrush"] as SolidColorBrush;
1266 columnBorder.Fill = new SolidColorBrush(themeBrush.Color) { Opacity = .05 };
1267 }
1268 };
1269 columnBorder.MouseLeave += (e, c) =>
1270 {
1271 IsShowValuesPopup = false;
1272 if (ColumnSelectedIndex != index)
1273 {
1274 columnBorder.Fill = new SolidColorBrush(Colors.Transparent);
1275
1276 }
1277 };
1278 if (IsCanColumnSelect)
1279 {
1280 columnBorder.MouseLeftButtonDown += (e, c) =>
1281 {
1282 ColumnSelectedIndex = index;
1283 };
1284 }
1285 }
1286 // 调整列值 zindex
1287 foreach (var item in _typeColValueRectMap)
1288 {
1289 var rectList = IsStack ? item.Value : item.Value.OrderByDescending(m => m.Height).ToList();
1290 for (int i = 0; i < rectList.Count; i++)
1291 {
1292 var rect = rectList[i];
1293 if (IsStack)
1294 {
1295 double marginBottom = 0;
1296 if (i > 0)
1297 {
1298 var lastRect = rectList[i - 1];
1299 marginBottom = Canvas.GetBottom(lastRect) + lastRect.Height;
1300 Canvas.SetBottom(rect, marginBottom);
1301 }
1302 }
1303 else
1304 {
1305 Panel.SetZIndex(rect, i);
1306 }
1307 }
1308 }
1309
1310
1311 // 最高
1312 var topValueText = new TextBlock();
1313 topValueText.Text = Maximum;
1314 topValueText.FontSize = 12;
1315 topValueText.Foreground = UI.Base.Color.Colors.GetFromString("#ccc");
1316 topValueText.ToolTip = "最高值";
1317
1318 var topValueTextSize = UIHelper.MeasureString(topValueText);
1319 Panel.SetZIndex(topValueText, 1000);
1320 Canvas.SetRight(topValueText, 0);
1321 Canvas.SetTop(topValueText, colNameBottomMargin - topValueTextSize.Height / 2);
1322 _typeColumnCanvas.Children.Add(topValueText);
1323
1324 var topValueLine = new Line();
1325 topValueLine.Stroke = UI.Base.Color.Colors.GetFromString("#ccc");
1326 topValueLine.StrokeDashArray = new DoubleCollection() { 2, 5 };
1327 topValueLine.X1 = colNameBottomMargin;
1328 topValueLine.X2 = canvasWidth - colNameBottomMargin - (topValueTextSize.Width);
1329 topValueLine.Y1 = colNameBottomMargin;
1330 topValueLine.Y2 = colNameBottomMargin;
1331 topValueLine.StrokeThickness = 1;
1332 _typeColumnCanvas.Children.Add(topValueLine);
1333
1334 // 中间值
1335 double midY = (maxValue / 2) / maxValue * canvasHeight + colNameBottomMargin;
1336
1337 var midValueText = new TextBlock();
1338 midValueText.Text = Median;
1339 midValueText.FontSize = 12;
1340 midValueText.Foreground = UI.Base.Color.Colors.GetFromString("#ccc");
1341 midValueText.ToolTip = "中间值";
1342
1343 var midValueTextSize = UIHelper.MeasureString(midValueText);
1344 Panel.SetZIndex(midValueText, 1000);
1345 Canvas.SetRight(midValueText, 0);
1346 Canvas.SetTop(midValueText, midY - midValueTextSize.Height / 2);
1347 _typeColumnCanvas.Children.Add(midValueText);
1348
1349 var midValueLine = new Line();
1350 midValueLine.Stroke = UI.Base.Color.Colors.GetFromString("#ccc");
1351 midValueLine.StrokeDashArray = new DoubleCollection() { 2, 5 };
1352 midValueLine.StrokeThickness = 1;
1353
1354
1355 midValueLine.X1 = colNameBottomMargin;
1356 midValueLine.X2 = canvasWidth - colNameBottomMargin - (midValueTextSize.Width);
1357 midValueLine.Y1 = midY;
1358 midValueLine.Y2 = midY;
1359 _typeColumnCanvas.Children.Add(midValueLine);
1360
1361
1362 // 平均
1363 double avg = tempValueArr.Average();
1364 double avgY = (avg) / maxValue * canvasHeight;
1365
1366 var avgValueText = new TextBlock();
1367 avgValueText.Text = Covervalue(avg);
1368 avgValueText.FontSize = 12;
1369 avgValueText.Foreground = UI.Base.Color.Colors.GetFromString(StateData.ThemeColor);
1370 avgValueText.ToolTip = "平均值";
1371
1372 var avgValueTextSize = UIHelper.MeasureString(avgValueText);
1373 Panel.SetZIndex(avgValueText, 1000);
1374 Canvas.SetRight(avgValueText, 0);
1375 Canvas.SetBottom(avgValueText, avgY + colNameHeight - avgValueTextSize.Height / 2);
1376 _typeColumnCanvas.Children.Add(avgValueText);
1377
1378 var avgValueLine = new Line();
1379 avgValueLine.Stroke = UI.Base.Color.Colors.GetFromString(StateData.ThemeColor);
1380 avgValueLine.StrokeDashArray = new DoubleCollection() { 2, 5 };
1381 avgValueLine.StrokeThickness = 1;
1382 avgValueLine.X1 = colNameBottomMargin;
1383 avgValueLine.X2 = canvasWidth - colNameBottomMargin - (avgValueTextSize.Width);
1384 avgValueLine.Y1 = canvasHeight - avgY + colNameBottomMargin + avgValueLine.StrokeThickness;
1385 avgValueLine.Y2 = canvasHeight - avgY + colNameBottomMargin + avgValueLine.StrokeThickness;
1386 //avgValueLine.Width = canvasWidth;
1387 //avgValueLine.Height = 10;
1388 //Canvas.SetBottom(avgValueLine, avgY + colNameHeight);
1389 _typeColumnCanvas.Children.Add(avgValueLine);
1390
1391 // 组装分类统计数据
1392 var infoList = new List<ChartColumnInfoModel>();
1393 list = list.OrderByDescending(m => m.Values.Sum()).ToList();
1394 foreach (var item in list)
1395 {
1396 infoList.Add(new ChartColumnInfoModel()
1397 {
1398 Color = item.Color,
1399 Name = item.Name,
1400 Icon = item.Icon,
1401 Text = Covervalue(item.Values.Sum()) + Unit
1402 });
1403 }
1404 ColumnInfoList = infoList;
1405 isRendering = false;
1406 }
1407 #endregion
1408
1409 #region 渲染雷达图
1410 private void RenderLadarStyle()
1411 {
1412 if (Data.Count() <= 2)
1413 {
1414 RadarContainer.Child = new EmptyData();
1415 isRendering = false;
1416 return;
1417 }
1418 // 最大值
1419 double total = 0;
1420 // 查找最大值
1421 foreach (var item in Data)
1422 {
1423 // 查找最大值
1424 double max = item.Values.Sum();
1425 if (max > maxValue)
1426 {
1427 maxValue = max;
1428 }
1429
1430 total += item.Values.Sum();
1431
1432 }
1433 //maxValue = total;
1434
1435 if (DataMaximum > 0)
1436 {
1437 maxValue = DataMaximum;
1438 }
1439 var radar = new ChartsItemTypeRadar();
1440 radar.Data = Data.OrderBy(m => m.Values.Sum()).ToList();
1441 radar.MaxValue = maxValue;
1442 RadarContainer.Child = radar;
1443
1444 isRendering = false;
1445 }
1446 #endregion
1447
1448 #region 饼图
1449 private void RenderPieStyle()
1450 {
1451 if (_commonCanvas == null)
1452 {
1453 isRendering = false;
1454 return;
1455 }
1456 _commonCanvas.Children.Clear();
1457 var item = new ChartsItemTypePie();
1458 item.Width = _commonCanvas.ActualWidth;
1459 item.Height = _commonCanvas.ActualHeight;
1460 item.HorizontalAlignment = HorizontalAlignment.Left;
1461 item.VerticalAlignment = VerticalAlignment.Top;
1462 item.Data = Data.OrderBy(m => m.Value).ToList();
1463 _commonCanvas.Children.Add(item);
1464
1465 isRendering = false;
1466 }
1467 #endregion
1468
1469 private string Covervalue(double value)
1470 {
1471 if (DataValueType == ChartDataValueType.Seconds)
1472 {
1473 return Time.ToString((int)value);
1474 }
1475 else
1476 {
1477 return value.ToString();
1478 }
1479 }
1480
1481 private void HandleItemClick(UIElement el, ChartsDataModel data)
1482 {
1483 el.MouseLeftButtonUp += (s, e) =>
1484 {
1485 OnItemClick?.Invoke(data, null);
1486 ClickCommand?.Execute(data);
1487 };
1488 el.MouseRightButtonUp += (s, e) =>
1489 {
1490 if (ItemMenu != null)
1491 {
1492 ItemMenu.IsOpen = true;
1493 ItemMenu.Tag = data;
1494 }
1495 };
1496 }
1497 }
1498}