跳到主要内容

github/tai

公开

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

浏览文件

ChartsItemTypeRadar.cs

323 行 · 10.9 KB历史下载
1using Core.Librarys;
2using Core.Librarys.Image;
3using System;
4using System.Collections.Generic;
5using System.Diagnostics;
6using System.Globalization;
7using System.IO;
8using System.Linq;
9using System.Text;
10using System.Threading.Tasks;
11using System.Windows;
12using System.Windows.Controls;
13using System.Windows.Data;
14using System.Windows.Documents;
15using System.Windows.Input;
16using System.Windows.Media;
17using System.Windows.Media.Imaging;
18using System.Windows.Navigation;
19using System.Windows.Shapes;
20using UI.Controls.Charts.Model;
21
22namespace UI.Controls.Charts
23{
24 public class ChartsItemTypeRadar : Control
25 {
26 #region Data
27 /// <summary>
28 /// Data
29 /// </summary>
30 public List<ChartsDataModel> Data
31 {
32 get { return (List<ChartsDataModel>)GetValue(DataProperty); }
33 set { SetValue(DataProperty, value); }
34 }
35 public static readonly DependencyProperty DataProperty =
36 DependencyProperty.Register("Data",
37 typeof(List<ChartsDataModel>),
38 typeof(ChartsItemTypeRadar));
39
40
41 #endregion
42
43 #region MaxValue 最大值
44 /// <summary>
45 /// 样式类型
46 /// </summary>
47 public double MaxValue
48 {
49 get { return (double)GetValue(MaxValueProperty); }
50 set { SetValue(MaxValueProperty, value); }
51 }
52 public static readonly DependencyProperty MaxValueProperty =
53 DependencyProperty.Register("MaxValue",
54 typeof(double),
55 typeof(ChartsItemTypeRadar));
56
57
58 #endregion
59
60 #region IsLoading 是否正在加载中
61 /// <summary>
62 /// 是否正在加载中
63 /// </summary>
64 public bool IsLoading
65 {
66 get { return (bool)GetValue(IsLoadingProperty); }
67 set { SetValue(IsLoadingProperty, value); }
68 }
69 public static readonly DependencyProperty IsLoadingProperty =
70 DependencyProperty.Register("IsLoading",
71 typeof(bool),
72 typeof(ChartsItemTypeRadar));
73
74
75 #endregion
76
77 public Geometry RadarPathData
78 {
79 get { return (Geometry)GetValue(RadarPathDataProperty); }
80 set { SetValue(RadarPathDataProperty, value); }
81 }
82 public static readonly DependencyProperty RadarPathDataProperty =
83 DependencyProperty.Register("RadarPathData",
84 typeof(Geometry),
85 typeof(ChartsItemTypeRadar));
86
87 private Canvas canvas;
88 public ChartsItemTypeRadar()
89 {
90 DefaultStyleKey = typeof(ChartsItemTypeRadar);
91 }
92
93
94 public override void OnApplyTemplate()
95 {
96 base.OnApplyTemplate();
97 canvas = GetTemplateChild("Canvas") as Canvas;
98 Loaded += ChartsItemTypeRadar_Loaded;
99 }
100
101 private void ChartsItemTypeRadar_Loaded(object sender, RoutedEventArgs e)
102 {
103 Render();
104 }
105
106 private void Render()
107 {
108 if (Data == null || Data.Count == 0)
109 {
110 return;
111 }
112 double size = ActualWidth != double.NaN ? ActualWidth : 200;
113 //size -= 50;
114 int count = Data.Count;
115
116 // 边长
117 double lineWidth = size / 2;
118
119 // 角度
120 double angle = (Math.PI * 2) / count;
121
122 var r = lineWidth / count;
123
124 // 多边形边框
125 for (int i = 0; i < 3; i++)
126 {
127 var points = new List<Point>();
128 // 当前半径
129 var currR = (r - (5 * i)) * (count - i);
130
131 for (int j = 0; j < count; j++)
132 {
133 points.Add(new Point(lineWidth + currR * Math.Cos(angle * j), lineWidth + currR * Math.Sin(angle * j)));
134 }
135 // 画出边框
136 for (int i2 = 0; i2 < points.Count; i2++)
137 {
138 var line = new Line();
139 line.X1 = points[i2].X;
140 line.Y1 = points[i2].Y;
141
142 line.X2 = i2 == points.Count - 1 ? points[0].X : points[i2 + 1].X;
143 line.Y2 = i2 == points.Count - 1 ? points[0].Y : points[i2 + 1].Y;
144
145 line.StrokeThickness = i == 0 ? 1 : 1;
146
147 line.Stroke = UI.Base.Color.Colors.GetFromString(i == 0 ? "#eeeef2" : "#eeeef2");
148
149 canvas.Children.Add(line);
150
151 }
152 }
153
154 // 顶点连线
155 for (var i = 0; i < count; i++)
156 {
157 //double diff = i == 0 || i == count - 1 ? 0 : 5;
158 double diff = 0;
159 var x = (double)(lineWidth + (lineWidth - diff) * Math.Cos((angle) * i));
160 var y = (double)(lineWidth + (lineWidth - diff) * Math.Sin((angle) * i));
161
162 var line = new Line();
163 line.X1 = lineWidth;
164 line.Y1 = lineWidth;
165
166 line.X2 = x;
167 line.Y2 = y;
168
169 line.StrokeThickness = .5;
170 line.Stroke = UI.Base.Color.Colors.GetFromString("#dedede");
171
172 canvas.Children.Add(line);
173
174 // 类别文字
175 var font = new TextBlock();
176 font.Text = Data[i].Name.Length > 4 ? Data[i].Name.Substring(0, 4) : Data[i].Name;
177 font.Foreground = UI.Base.Color.Colors.GetFromString("#7f7f7f");
178 font.FontSize = 12;
179 font.ToolTip = $"{Data[i].Name} {Time.ToString((int)Data[i].Values.Sum())}";
180
181 var textSize = MeasureString(font);
182 Debug.WriteLine(font.Text + " -> " + angle * i);
183 if (angle * i > 0 && angle * i <= Math.PI / 2)
184 {
185 // >0 && < 1.57
186 x += textSize.Height / 2;
187 y += textSize.Height / 2;
188 }
189 else if (angle * i > 1.79 && angle * i < 1.8)
190 {
191 x += textSize.Height / 2;
192 y += textSize.Width / 2;
193 }
194 else if (angle * i > 2.09 && angle * i < 2.1)
195 {
196 x += textSize.Height / 2;
197 y += textSize.Height / 2;
198 }
199 else if (angle * i > 2.51 && angle * i < 2.52)
200 {
201 x += textSize.Height / 2;
202 y += textSize.Height / 2;
203 }
204 else if (angle * i > 2.69 && angle * i < 2.7)
205 {
206 x += textSize.Height / 2;
207 y += textSize.Height / 2;
208 }
209 else if (angle * i > Math.PI / 2 && angle * i <= Math.PI)
210 {
211 // > 1.57 && < 3.14
212 x -= textSize.Height / 2;
213 y -= textSize.Width / 2;
214
215 }
216 //else if (angle * i > 3.5 && angle * i < 4.2)
217 //{
218 // x -= textSize.Height / 2;
219 // y -= textSize.Width / 2;
220
221 //}
222 else if (angle * i > Math.PI && angle * i <= Math.PI * 3 / 2)
223 {
224 // > 3.14 && < 4.71
225 x += font.FontSize / 2;
226 y -= textSize.Width + textSize.Height / 2;
227 }
228 else if (angle * i > Math.PI * 3 / 2)
229 {
230 // > 4.71
231 x += textSize.Height / 2;
232 y -= textSize.Width + textSize.Height / 2;
233 }
234 else
235 {
236 // 顶点
237 x += textSize.Height + textSize.Height / 2;
238 y -= textSize.Width / 2;
239 }
240
241 font.RenderTransform = new RotateTransform()
242 {
243 Angle = 90
244 };
245 Canvas.SetLeft(font, x);
246 Canvas.SetTop(font, y);
247 canvas.Children.Add(font);
248 }
249
250 // 中心装饰点
251 var centerPoint = new Ellipse();
252 centerPoint.Width = 15;
253 centerPoint.Height = 15;
254 centerPoint.Fill = UI.Base.Color.Colors.GetFromString("#ffffff");
255 centerPoint.Stroke = UI.Base.Color.Colors.GetFromString("#dedede");
256 centerPoint.StrokeThickness = 1;
257 Canvas.SetLeft(centerPoint, lineWidth - centerPoint.Width / 2);
258 Canvas.SetTop(centerPoint, lineWidth - centerPoint.Width / 2);
259 canvas.Children.Add(centerPoint);
260
261 // 数据区域
262 var pc = new PointCollection();
263
264 for (int i = 0; i < count; i++)
265 {
266 double sum = Data[i].Values.Sum();
267 double value = sum / MaxValue;
268 value = value == 1 ? 0.97 : value;
269
270 var x = (double)(lineWidth + (lineWidth) * Math.Cos((angle) * i) * value);
271 var y = (double)(lineWidth + (lineWidth) * Math.Sin((angle) * i) * value);
272
273 pc.Add(new Point(x, y));
274
275 // 数据点
276 var dataPoint = new Ellipse();
277 dataPoint.Width = 5;
278 dataPoint.Height = 5;
279
280 dataPoint.Fill = UI.Base.Color.Colors.GetFromString(StateData.ThemeColor);
281 dataPoint.Stroke = UI.Base.Color.Colors.GetFromString(StateData.ThemeColor);
282 dataPoint.StrokeThickness = 1;
283 Canvas.SetLeft(dataPoint, x - dataPoint.Width / 2);
284 Canvas.SetTop(dataPoint, y - dataPoint.Width / 2);
285 canvas.Children.Add(dataPoint);
286 }
287
288 var p = new Polygon();
289 p.Stroke = UI.Base.Color.Colors.GetFromString(StateData.ThemeColor);
290 p.Fill = UI.Base.Color.Colors.GetFromString(StateData.ThemeColor, .3);
291 p.StrokeThickness = 1;
292 p.HorizontalAlignment = HorizontalAlignment.Left;
293 p.VerticalAlignment = VerticalAlignment.Center;
294 p.Points = pc;
295 canvas.Children.Add(p);
296
297 }
298 private Size MeasureString(TextBlock textBlock)
299 {
300 var formattedText = new FormattedText(
301 textBlock.Text,
302 CultureInfo.CurrentCulture,
303 FlowDirection.LeftToRight,
304 new Typeface(textBlock.FontFamily, textBlock.FontStyle, textBlock.FontWeight, textBlock.FontStretch),
305 textBlock.FontSize,
306 Brushes.Black,
307 new NumberSubstitution());
308
309 return new Size(formattedText.Width, formattedText.Height);
310 }
311
312 protected override void OnMouseEnter(MouseEventArgs e)
313 {
314 base.OnMouseEnter(e);
315 VisualStateManager.GoToState(this, "MouseOver", true);
316 }
317 protected override void OnMouseLeave(MouseEventArgs e)
318 {
319 base.OnMouseLeave(e);
320 VisualStateManager.GoToState(this, "NormalState", true);
321 }
322 }
323}