跳到主要内容

github/tai

公开

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

浏览文件

Navigation.cs

478 行 · 16.9 KB历史下载
1using System;
2using System.Collections.Generic;
3using System.Collections.ObjectModel;
4using System.Collections.Specialized;
5using System.Diagnostics;
6using System.Linq;
7using System.Text;
8using System.Windows;
9using System.Windows.Controls;
10using System.Windows.Media;
11using System.Windows.Media.Animation;
12using UI.Controls.Navigation.Models;
13
14namespace UI.Controls.Navigation
15{
16 public class Navigation : Control
17 {
18 public ContextMenu ItemContextMenu
19 {
20 get { return (ContextMenu)GetValue(ItemContextMenuProperty); }
21 set { SetValue(SelectedItemProperty, value); }
22 }
23 public static readonly DependencyProperty ItemContextMenuProperty =
24 DependencyProperty.Register("ItemContextMenu", typeof(ContextMenu), typeof(Navigation));
25
26 public NavigationItemModel SelectedItem
27 {
28 get { return (NavigationItemModel)GetValue(SelectedItemProperty); }
29 set { SetValue(SelectedItemProperty, value); }
30 }
31 public static readonly DependencyProperty SelectedItemProperty =
32 DependencyProperty.Register("SelectedItem", typeof(NavigationItemModel), typeof(Navigation), new PropertyMetadata(new PropertyChangedCallback(OnSelectedItemChanged)));
33
34 private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
35 {
36 var control = d as Navigation;
37 if (e.NewValue != e.OldValue)
38 {
39 var newItem = e.NewValue as NavigationItemModel;
40 var oldItem = e.OldValue as NavigationItemModel;
41 if (newItem != null && control.ItemsDictionary.ContainsKey(newItem.ID))
42 {
43 control.ItemsDictionary[newItem.ID].IsSelected = true;
44 }
45 if (oldItem != null && control.ItemsDictionary.ContainsKey(oldItem.ID))
46 {
47 control.ItemsDictionary[oldItem.ID].IsSelected = false;
48 }
49 control.ScrollToActive();
50 }
51 }
52
53 public bool IsShowNavigation
54 {
55 get { return (bool)GetValue(IsShowNavigationProperty); }
56 set { SetValue(IsShowNavigationProperty, value); }
57 }
58 public static readonly DependencyProperty IsShowNavigationProperty =
59 DependencyProperty.Register("IsShowNavigation", typeof(bool), typeof(Navigation), new PropertyMetadata(true, new PropertyChangedCallback(OnIsShowNavigationChanged)));
60
61 private static void OnIsShowNavigationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
62 {
63 var control = d as Navigation;
64 if (e.NewValue != e.OldValue)
65 {
66 if (control.IsShowNavigation && control.Visibility == Visibility.Collapsed && control.WindowWidth > 700)
67 {
68 control.Visibility = Visibility.Visible;
69 }
70 if (!control.IsShowNavigation && control.Visibility == Visibility.Visible)
71 {
72 control.Visibility = Visibility.Collapsed;
73 }
74 }
75 }
76
77 public object TopExtContent
78 {
79 get { return (object)GetValue(TopExtContentProperty); }
80 set { SetValue(TopExtContentProperty, value); }
81 }
82 public static readonly DependencyProperty TopExtContentProperty =
83 DependencyProperty.Register("TopExtContent", typeof(object), typeof(Navigation));
84 public object BottomExtContent
85 {
86 get { return (object)GetValue(BottomExtContentProperty); }
87 set { SetValue(BottomExtContentProperty, value); }
88 }
89 public static readonly DependencyProperty BottomExtContentProperty =
90 DependencyProperty.Register("BottomExtContent", typeof(object), typeof(Navigation));
91 public ObservableCollection<NavigationItemModel> Data
92 {
93 get
94 {
95 return (ObservableCollection<NavigationItemModel>)GetValue(DataProperty);
96 }
97 set
98 {
99 SetValue(DataProperty, value);
100 }
101 }
102 public static readonly DependencyProperty DataProperty =
103 DependencyProperty.Register("Data", typeof(ObservableCollection<NavigationItemModel>), typeof(Navigation), new PropertyMetadata(new PropertyChangedCallback(OnDataChanged)));
104
105 private static void OnDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
106 {
107 var control = d as Navigation;
108 if (e.NewValue != e.OldValue)
109 {
110 if (control.Data != null)
111 {
112 control.Data.CollectionChanged -= control.Data_CollectionChanged;
113 control.Data.CollectionChanged += control.Data_CollectionChanged;
114
115 foreach (var item in control.Data)
116 {
117 control.AddItem(item);
118 }
119 }
120 }
121 }
122
123 public double WindowWidth
124 {
125 get
126 {
127 return (double)GetValue(WindowWidthProperty);
128 }
129 set
130 {
131 SetValue(WindowWidthProperty, value);
132 }
133 }
134 public static readonly DependencyProperty WindowWidthProperty =
135 DependencyProperty.Register("WindowWidth", typeof(double), typeof(Navigation), new PropertyMetadata(new PropertyChangedCallback(OnWindowWidthChanged)));
136
137 private static void OnWindowWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
138 {
139 var control = d as Navigation;
140 if (control.IsShowNavigation)
141 {
142 if ((double)e.NewValue <= 700)
143 {
144 control.Visibility = Visibility.Collapsed;
145 }
146 else
147 {
148
149 control.Visibility = Visibility.Visible;
150 }
151 }
152 }
153
154
155 #region items property
156
157
158 //protected static PropertyChangedCallback ItemsPropertyChangedCallback = new PropertyChangedCallback(ItemsPropertyChanged);
159
160 //public static DependencyProperty ItemsProperty = DependencyProperty.RegisterAttached("Items", typeof(ObservableCollection<NavigationItemModel>), typeof(Navigation), new PropertyMetadata(null, ItemsPropertyChangedCallback));
161
162 //private static void ItemsPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
163 //{
164 // var control = (Navigation)sender;
165 // if (control == null)
166 // {
167 // return;
168 // }
169 // control.UnregisterItems(e.OldValue as ObservableCollection<NavigationItemModel>);
170 // control.RegisterItems(e.NewValue as ObservableCollection<NavigationItemModel>);
171 //}
172
173 //public ObservableCollection<NavigationItemModel> Items
174 //{
175 // get
176 // {
177 // return (ObservableCollection<NavigationItemModel>)GetValue(ItemsProperty);
178 // }
179 // set
180 // {
181 // SetValue(ItemsProperty, value);
182 // }
183 //}
184
185 //protected void UnregisterItems(ObservableCollection<NavigationItemModel> items)
186 //{
187 // if (items == null)
188 // {
189 // return;
190 // }
191 // items.CollectionChanged -= ItemsChanged;
192 //}
193
194 //protected void RegisterItems(ObservableCollection<NavigationItemModel> items)
195 //{
196 // if (items == null)
197 // {
198 // return;
199 // }
200 // items.CollectionChanged += ItemsChanged;
201 //}
202
203 //protected virtual void ItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
204 //{
205 // if (e.Action == NotifyCollectionChangedAction.Add)
206 // {
207 // foreach (var item in e.NewItems)
208 // {
209 // var data = item as NavigationItemModel;
210 // AddItem(data);
211 // }
212 // }
213 // else if (e.Action == NotifyCollectionChangedAction.Remove)
214 // {
215 // foreach (var item in e.OldItems)
216 // {
217 // var data = item as NavigationItemModel;
218 // RemoveItem(data);
219 // }
220 // }
221 //}
222 #endregion
223 public event RoutedEventHandler OnSelected;
224 public event RoutedEventHandler OnMouseRightButtonUP;
225
226 private StackPanel ItemsPanel;
227 private Dictionary<int, NavigationItem> ItemsDictionary;
228 // 选中标记块
229 private Border ActiveBlock;
230 // 动画
231 Storyboard storyboard;
232 // 滚动动画
233 DoubleAnimation scrollAnimation;
234 // 伸缩动画
235 DoubleAnimation stretchAnimation;
236
237 public Navigation()
238 {
239 DefaultStyleKey = typeof(Navigation);
240 ItemsDictionary = new Dictionary<int, NavigationItem>();
241 Loaded += Navigation_Loaded;
242 }
243
244 private void Navigation_Loaded(object sender, RoutedEventArgs e)
245 {
246 ScrollToActive();
247 }
248
249 #region create animation
250 private void CreateAnimations()
251 {
252 if (ActiveBlock != null)
253 {
254 var tfg = new TransformGroup();
255 tfg.Children.Add(new TranslateTransform() { X = 0, Y = 0 });
256 tfg.Children.Add(new ScaleTransform() { ScaleX = 0, ScaleY = 0 });
257 ActiveBlock.RenderTransform = tfg;
258 }
259 storyboard = new Storyboard();
260 scrollAnimation = new DoubleAnimation();
261 stretchAnimation = new DoubleAnimation();
262
263 // 滚动动画
264 scrollAnimation.EasingFunction = new SineEase() { EasingMode = EasingMode.EaseIn };
265 scrollAnimation.Duration = new Duration(TimeSpan.FromSeconds(0.35));
266
267 Storyboard.SetTarget(scrollAnimation, ActiveBlock);
268 Storyboard.SetTargetProperty(scrollAnimation, new PropertyPath("RenderTransform.Children[0].Y"));
269
270 // 伸缩动画
271 stretchAnimation.AutoReverse = true;
272 stretchAnimation.EasingFunction = new SineEase() { EasingMode = EasingMode.EaseIn };
273 stretchAnimation.Duration = new Duration(TimeSpan.FromSeconds(0.35));
274
275 Storyboard.SetTarget(stretchAnimation, ActiveBlock);
276 Storyboard.SetTargetProperty(stretchAnimation, new PropertyPath("RenderTransform.Children[1].ScaleY"));
277
278 storyboard.Children.Add(stretchAnimation);
279 storyboard.Children.Add(scrollAnimation);
280 }
281 #endregion
282
283 #region temp region
284 private void Data_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
285 {
286 if (e.Action == NotifyCollectionChangedAction.Add)
287 {
288 foreach (var item in e.NewItems)
289 {
290 AddItem(item as NavigationItemModel);
291 }
292 }
293 if (e.Action == NotifyCollectionChangedAction.Remove)
294 {
295 foreach (var item in e.OldItems)
296 {
297 RemoveItem(item as NavigationItemModel);
298 }
299 }
300 if (e.Action == NotifyCollectionChangedAction.Replace)
301 {
302 foreach (var ritem in e.NewItems)
303 {
304 var item = ritem as NavigationItemModel;
305 var id = item.ID;
306 ItemsDictionary[id].Icon = item.UnSelectedIcon;
307 ItemsDictionary[id].Title = item.Title;
308 ItemsDictionary[id].SelectedIcon = item.SelectedIcon;
309
310 }
311 }
312 }
313
314 public override void OnApplyTemplate()
315 {
316 base.OnApplyTemplate();
317 ItemsPanel = GetTemplateChild("ItemsPanel") as StackPanel;
318 ActiveBlock = GetTemplateChild("ActiveBlock") as Border;
319
320 CreateAnimations();
321 Render();
322 }
323 private int CreateID()
324 {
325 if (ItemsDictionary.Count == 0)
326 {
327 return 1;
328 }
329 return ItemsDictionary.Max(m => m.Key) + 1;
330 }
331 private void AddItem(NavigationItemModel item)
332 {
333 if (ItemsPanel != null)
334 {
335 var navItem = new NavigationItem();
336 int id = item.ID < -1 ? CreateID() : item.ID;
337 if (ItemsDictionary.ContainsKey(id))
338 {
339 return;
340 }
341
342 item.ID = id;
343 navItem.ID = id;
344 navItem.Title = item.Title;
345 navItem.Icon = item.UnSelectedIcon;
346 navItem.SelectedIcon = item.SelectedIcon;
347 navItem.IconColor = item.IconColor;
348 navItem.BadgeText = item.BadgeText;
349 navItem.Uri = item.Uri;
350
351 if (!string.IsNullOrEmpty(item.Title))
352 {
353 navItem.MouseUp += NavItem_MouseUp;
354 navItem.Unloaded += (e, c) =>
355 {
356 navItem.MouseUp -= NavItem_MouseUp;
357 };
358 }
359 ItemsPanel.Children.Add(navItem);
360 ItemsDictionary.Add(id, navItem);
361 //if (item.IsSelected)
362 //{
363 // SelectedItem = item;
364 //}
365 }
366 }
367
368 private void NavItem_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
369 {
370 var navitem = sender as NavigationItem;
371 if (e.ChangedButton == System.Windows.Input.MouseButton.Left)
372 {
373 //左键选中
374
375 SelectedItem = Data.Where(m => m.ID == navitem.ID).FirstOrDefault();
376 OnSelected?.Invoke(this, null);
377
378 if (SelectedItem != null && SelectedItem.ID != navitem.ID)
379 {
380 ScrollToActive();
381 }
382 }
383 if (e.ChangedButton == System.Windows.Input.MouseButton.Right)
384 {
385 //右键
386 var args = new RoutedEventArgs();
387 args.RoutedEvent = e.RoutedEvent;
388 args.Source = Data.Where(m => m.ID == navitem.ID).FirstOrDefault();
389 OnMouseRightButtonUP?.Invoke(this, args);
390 }
391 }
392
393 private void RemoveItem(NavigationItemModel item)
394 {
395 var navItem = ItemsDictionary[item.ID];
396 ItemsPanel.Children.Remove(navItem);
397 ItemsDictionary.Remove(item.ID);
398 }
399 private void Render()
400 {
401 ItemsPanel.Children.Clear();
402 ItemsDictionary.Clear();
403
404 if (Data != null)
405 {
406 foreach (var item in Data)
407 {
408 AddItem(item);
409 }
410 }
411
412 UpdateActiveLocation();
413 }
414 #endregion
415 private void UpdateActiveLocation()
416 {
417 if (SelectedItem == null || !IsLoaded)
418 {
419 return;
420 }
421 var item = ItemsDictionary[SelectedItem.ID];
422 item.Loaded += (e, c) =>
423 {
424 ScrollToActive(0);
425 };
426
427
428
429
430 }
431 private void ScrollToActive(double animationDuration = 0.35)
432 {
433 // 获取选中项
434 if (SelectedItem == null || ItemsDictionary.Count == 0 || !ItemsDictionary.ContainsKey(SelectedItem.ID))
435 {
436 return;
437 }
438 var item = ItemsDictionary[SelectedItem.ID];
439 item.IsSelected = true;
440
441 scrollAnimation.Duration = new Duration(TimeSpan.FromSeconds(animationDuration));
442 stretchAnimation.Duration = new Duration(TimeSpan.FromSeconds(animationDuration));
443
444
445 // 选中项的坐标
446 Point relativePoint = item.TransformToAncestor(this).Transform(new Point(0, 0));
447
448 // 设定动画方向
449 var activeBlockTTF = (ActiveBlock.RenderTransform as TransformGroup).Children[0] as TranslateTransform;
450 scrollAnimation.To = relativePoint.Y + 8;
451
452 // 伸缩动画
453
454 stretchAnimation.To = 1.6;
455
456 if (relativePoint.Y > activeBlockTTF.Y)
457 {
458 // 向下移动
459 var transformGroup = new TransformGroup();
460 transformGroup.Children.Add(new TranslateTransform(0, activeBlockTTF.Y));
461 transformGroup.Children.Add(new ScaleTransform(1, 1, 0, 200));
462
463 ActiveBlock.RenderTransform = transformGroup;
464 }
465 else
466 {
467 var transformGroup = new TransformGroup();
468 transformGroup.Children.Add(new TranslateTransform(0, activeBlockTTF.Y));
469 transformGroup.Children.Add(new ScaleTransform(1, 1, 0, 0));
470
471 ActiveBlock.RenderTransform = transformGroup;
472
473 }
474
475 storyboard.Begin();
476 }
477 }
478}