Drawer.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. using System;
  2. using System.Windows;
  3. using System.Windows.Controls;
  4. using System.Windows.Data;
  5. using System.Windows.Documents;
  6. using System.Windows.Input;
  7. using System.Windows.Markup;
  8. using System.Windows.Media;
  9. using System.Windows.Media.Animation;
  10. using HandyControl.Data;
  11. using HandyControl.Interactivity;
  12. using HandyControl.Tools;
  13. namespace HandyControl.Controls;
  14. [ContentProperty("Content")]
  15. public class Drawer : FrameworkElement
  16. {
  17. private Storyboard _storyboard;
  18. private AdornerContainer _container;
  19. private ContentControl _animationControl;
  20. private TranslateTransform _translateTransform;
  21. private double _animationLength;
  22. private string _animationPropertyName;
  23. private FrameworkElement _maskElement;
  24. private AdornerLayer _layer;
  25. private UIElement _contentElement;
  26. private Point _contentRenderTransformOrigin;
  27. static Drawer()
  28. {
  29. DataContextProperty.OverrideMetadata(typeof(Drawer), new FrameworkPropertyMetadata(DataContextPropertyChanged));
  30. }
  31. public Drawer()
  32. {
  33. Loaded += Drawer_Loaded;
  34. }
  35. private void Drawer_Loaded(object sender, RoutedEventArgs e)
  36. {
  37. if (IsOpen)
  38. {
  39. OnIsOpenChanged(true);
  40. }
  41. Loaded -= Drawer_Loaded;
  42. }
  43. private static void DataContextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
  44. ((Drawer) d).OnDataContextPropertyChanged(e);
  45. private void OnDataContextPropertyChanged(DependencyPropertyChangedEventArgs e) => UpdateDataContext(_animationControl, e.OldValue, e.NewValue);
  46. public static readonly RoutedEvent OpenedEvent =
  47. EventManager.RegisterRoutedEvent("Opened", RoutingStrategy.Bubble,
  48. typeof(RoutedEventHandler), typeof(Drawer));
  49. public event RoutedEventHandler Opened
  50. {
  51. add => AddHandler(OpenedEvent, value);
  52. remove => RemoveHandler(OpenedEvent, value);
  53. }
  54. public static readonly RoutedEvent ClosedEvent =
  55. EventManager.RegisterRoutedEvent("Closed", RoutingStrategy.Bubble,
  56. typeof(RoutedEventHandler), typeof(Drawer));
  57. public event RoutedEventHandler Closed
  58. {
  59. add => AddHandler(ClosedEvent, value);
  60. remove => RemoveHandler(ClosedEvent, value);
  61. }
  62. public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register(
  63. nameof(IsOpen), typeof(bool), typeof(Drawer), new FrameworkPropertyMetadata(ValueBoxes.FalseBox, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnIsOpenChanged));
  64. private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  65. {
  66. var ctl = (Drawer) d;
  67. ctl.OnIsOpenChanged((bool) e.NewValue);
  68. }
  69. public bool IsOpen
  70. {
  71. get => (bool) GetValue(IsOpenProperty);
  72. set => SetValue(IsOpenProperty, ValueBoxes.BooleanBox(value));
  73. }
  74. public static readonly DependencyProperty MaskCanCloseProperty = DependencyProperty.Register(
  75. nameof(MaskCanClose), typeof(bool), typeof(Drawer), new PropertyMetadata(ValueBoxes.TrueBox));
  76. public bool MaskCanClose
  77. {
  78. get => (bool) GetValue(MaskCanCloseProperty);
  79. set => SetValue(MaskCanCloseProperty, ValueBoxes.BooleanBox(value));
  80. }
  81. public static readonly DependencyProperty ShowMaskProperty = DependencyProperty.Register(
  82. nameof(ShowMask), typeof(bool), typeof(Drawer), new PropertyMetadata(ValueBoxes.TrueBox));
  83. public bool ShowMask
  84. {
  85. get => (bool) GetValue(ShowMaskProperty);
  86. set => SetValue(ShowMaskProperty, ValueBoxes.BooleanBox(value));
  87. }
  88. public static readonly DependencyProperty DockProperty = DependencyProperty.Register(
  89. nameof(Dock), typeof(Dock), typeof(Drawer), new PropertyMetadata(default(Dock)));
  90. public Dock Dock
  91. {
  92. get => (Dock) GetValue(DockProperty);
  93. set => SetValue(DockProperty, value);
  94. }
  95. public static readonly DependencyProperty ShowModeProperty = DependencyProperty.Register(
  96. nameof(ShowMode), typeof(DrawerShowMode), typeof(Drawer), new PropertyMetadata(default(DrawerShowMode)));
  97. public DrawerShowMode ShowMode
  98. {
  99. get => (DrawerShowMode) GetValue(ShowModeProperty);
  100. set => SetValue(ShowModeProperty, value);
  101. }
  102. public static readonly DependencyProperty MaskBrushProperty = DependencyProperty.Register(
  103. nameof(MaskBrush), typeof(Brush), typeof(Drawer), new PropertyMetadata(default(Brush)));
  104. public Brush MaskBrush
  105. {
  106. get => (Brush) GetValue(MaskBrushProperty);
  107. set => SetValue(MaskBrushProperty, value);
  108. }
  109. public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
  110. nameof(Content), typeof(object), typeof(Drawer), new PropertyMetadata(default(object)));
  111. public object Content
  112. {
  113. get => GetValue(ContentProperty);
  114. set => SetValue(ContentProperty, value);
  115. }
  116. private void CreateContainer()
  117. {
  118. _storyboard = new Storyboard();
  119. _storyboard.Completed += Storyboard_Completed;
  120. _translateTransform = new TranslateTransform();
  121. _animationControl = new ContentControl
  122. {
  123. Content = Content,
  124. RenderTransform = _translateTransform,
  125. DataContext = this
  126. };
  127. var panel = new SimplePanel
  128. {
  129. ClipToBounds = true
  130. };
  131. if (ShowMask)
  132. {
  133. _maskElement = new Border
  134. {
  135. Background = MaskBrush,
  136. Opacity = 0
  137. };
  138. _maskElement.PreviewMouseLeftButtonDown += MaskElement_PreviewMouseLeftButtonDown;
  139. panel.Children.Add(_maskElement);
  140. }
  141. _animationControl.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
  142. var size = _animationControl.DesiredSize;
  143. switch (Dock)
  144. {
  145. case Dock.Left:
  146. _animationControl.HorizontalAlignment = HorizontalAlignment.Left;
  147. _animationControl.VerticalAlignment = VerticalAlignment.Stretch;
  148. _translateTransform.X = -size.Width;
  149. _animationLength = -size.Width;
  150. _animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.X)";
  151. break;
  152. case Dock.Top:
  153. _animationControl.HorizontalAlignment = HorizontalAlignment.Stretch;
  154. _animationControl.VerticalAlignment = VerticalAlignment.Top;
  155. _translateTransform.Y = -size.Height;
  156. _animationLength = -size.Height;
  157. _animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.Y)";
  158. break;
  159. case Dock.Right:
  160. _animationControl.HorizontalAlignment = HorizontalAlignment.Right;
  161. _animationControl.VerticalAlignment = VerticalAlignment.Stretch;
  162. _translateTransform.X = size.Width;
  163. _animationLength = size.Width;
  164. _animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.X)";
  165. break;
  166. case Dock.Bottom:
  167. _animationControl.HorizontalAlignment = HorizontalAlignment.Stretch;
  168. _animationControl.VerticalAlignment = VerticalAlignment.Bottom;
  169. _translateTransform.Y = size.Height;
  170. _animationLength = size.Height;
  171. _animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.Y)";
  172. break;
  173. default:
  174. throw new ArgumentOutOfRangeException();
  175. }
  176. _animationControl.DataContext = DataContext;
  177. _animationControl.CommandBindings.Clear();
  178. _animationControl.CommandBindings.Add(new CommandBinding(ControlCommands.Close, (s, e) => SetCurrentValue(IsOpenProperty, ValueBoxes.FalseBox)));
  179. panel.Children.Add(_animationControl);
  180. _container = new AdornerContainer(_layer)
  181. {
  182. Child = panel,
  183. ClipToBounds = true
  184. };
  185. }
  186. private void Storyboard_Completed(object sender, EventArgs e)
  187. {
  188. if (!IsOpen)
  189. {
  190. _contentElement.SetCurrentValue(RenderTransformOriginProperty, _contentRenderTransformOrigin);
  191. _layer.Remove(_container);
  192. RaiseEvent(new RoutedEventArgs(ClosedEvent, this));
  193. }
  194. else
  195. {
  196. RaiseEvent(new RoutedEventArgs(OpenedEvent, this));
  197. }
  198. }
  199. private void OnIsOpenChanged(bool isOpen)
  200. {
  201. if (Content == null || DesignerHelper.IsInDesignMode)
  202. {
  203. return;
  204. }
  205. AdornerDecorator decorator;
  206. var parent = VisualHelper.GetParent<DrawerContainer>(this);
  207. if (parent != null)
  208. {
  209. _contentElement = parent.Child;
  210. decorator = parent;
  211. }
  212. else
  213. {
  214. var window = WindowHelper.GetActiveWindow();
  215. if (window == null)
  216. {
  217. return;
  218. }
  219. decorator = VisualHelper.GetChild<AdornerDecorator>(window);
  220. _contentElement = window.Content as UIElement;
  221. }
  222. if (_contentElement == null)
  223. {
  224. return;
  225. }
  226. _layer = decorator?.AdornerLayer;
  227. if (_layer == null)
  228. {
  229. return;
  230. }
  231. _contentRenderTransformOrigin = _contentElement.RenderTransformOrigin;
  232. if (_container == null)
  233. {
  234. CreateContainer();
  235. }
  236. switch (ShowMode)
  237. {
  238. case DrawerShowMode.Push:
  239. ShowByPush(isOpen);
  240. break;
  241. case DrawerShowMode.Press:
  242. _contentElement.SetCurrentValue(RenderTransformOriginProperty, new Point(0.5, 0.5));
  243. ShowByPress(isOpen);
  244. break;
  245. }
  246. if (isOpen)
  247. {
  248. if (_maskElement != null)
  249. {
  250. var maskAnimation = AnimationHelper.CreateAnimation(1);
  251. Storyboard.SetTarget(maskAnimation, _maskElement);
  252. Storyboard.SetTargetProperty(maskAnimation, new PropertyPath(OpacityProperty.Name));
  253. _storyboard.Children.Add(maskAnimation);
  254. }
  255. var drawerAnimation = AnimationHelper.CreateAnimation(0);
  256. Storyboard.SetTarget(drawerAnimation, _animationControl);
  257. Storyboard.SetTargetProperty(drawerAnimation, new PropertyPath(_animationPropertyName));
  258. _storyboard.Children.Add(drawerAnimation);
  259. _layer.Remove(_container);
  260. _layer.Add(_container);
  261. }
  262. else
  263. {
  264. if (_maskElement != null)
  265. {
  266. var maskAnimation = AnimationHelper.CreateAnimation(0);
  267. Storyboard.SetTarget(maskAnimation, _maskElement);
  268. Storyboard.SetTargetProperty(maskAnimation, new PropertyPath(OpacityProperty.Name));
  269. _storyboard.Children.Add(maskAnimation);
  270. }
  271. var drawerAnimation = AnimationHelper.CreateAnimation(_animationLength);
  272. Storyboard.SetTarget(drawerAnimation, _animationControl);
  273. Storyboard.SetTargetProperty(drawerAnimation, new PropertyPath(_animationPropertyName));
  274. _storyboard.Children.Add(drawerAnimation);
  275. }
  276. _storyboard.Begin();
  277. }
  278. private void ShowByPush(bool isOpen)
  279. {
  280. string animationPropertyName;
  281. switch (Dock)
  282. {
  283. case Dock.Left:
  284. case Dock.Right:
  285. animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.X)";
  286. _contentElement.RenderTransform = new TranslateTransform
  287. {
  288. X = isOpen ? 0 : -_animationLength
  289. };
  290. break;
  291. case Dock.Top:
  292. case Dock.Bottom:
  293. animationPropertyName = "(UIElement.RenderTransform).(TranslateTransform.Y)";
  294. _contentElement.RenderTransform = new TranslateTransform
  295. {
  296. Y = isOpen ? 0 : -_animationLength
  297. };
  298. break;
  299. default:
  300. throw new ArgumentOutOfRangeException();
  301. }
  302. var animation = isOpen
  303. ? AnimationHelper.CreateAnimation(-_animationLength)
  304. : AnimationHelper.CreateAnimation(0);
  305. Storyboard.SetTarget(animation, _contentElement);
  306. Storyboard.SetTargetProperty(animation, new PropertyPath(animationPropertyName));
  307. _storyboard.Children.Add(animation);
  308. }
  309. private void ShowByPress(bool isOpen)
  310. {
  311. _contentElement.RenderTransform = isOpen
  312. ? new ScaleTransform()
  313. : new ScaleTransform
  314. {
  315. ScaleX = 0.9,
  316. ScaleY = 0.9
  317. };
  318. var animationX = isOpen
  319. ? AnimationHelper.CreateAnimation(.9)
  320. : AnimationHelper.CreateAnimation(1);
  321. Storyboard.SetTarget(animationX, _contentElement);
  322. Storyboard.SetTargetProperty(animationX, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleX)"));
  323. _storyboard.Children.Add(animationX);
  324. var animationY = isOpen
  325. ? AnimationHelper.CreateAnimation(.9)
  326. : AnimationHelper.CreateAnimation(1);
  327. Storyboard.SetTarget(animationY, _contentElement);
  328. Storyboard.SetTargetProperty(animationY, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)"));
  329. _storyboard.Children.Add(animationY);
  330. }
  331. private void MaskElement_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  332. {
  333. if (MaskCanClose)
  334. {
  335. SetCurrentValue(IsOpenProperty, ValueBoxes.FalseBox);
  336. }
  337. }
  338. private void UpdateDataContext(FrameworkElement target, object oldValue, object newValue)
  339. {
  340. if (target == null || BindingOperations.GetBindingExpression(target, DataContextProperty) != null) return;
  341. if (ReferenceEquals(this, target.DataContext) || Equals(oldValue, target.DataContext))
  342. {
  343. target.DataContext = newValue ?? this;
  344. }
  345. }
  346. }