ScrollBar.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Reactive;
  5. using System.Reactive.Linq;
  6. using Avalonia.Data;
  7. using Avalonia.Interactivity;
  8. using Avalonia.Input;
  9. using Avalonia.Layout;
  10. namespace Avalonia.Controls.Primitives
  11. {
  12. public class ScrollEventArgs : EventArgs
  13. {
  14. public ScrollEventArgs(ScrollEventType eventType, double newValue)
  15. {
  16. ScrollEventType = eventType;
  17. NewValue = newValue;
  18. }
  19. public double NewValue { get; private set; }
  20. public ScrollEventType ScrollEventType { get; private set; }
  21. }
  22. /// <summary>
  23. /// A scrollbar control.
  24. /// </summary>
  25. public class ScrollBar : RangeBase
  26. {
  27. /// <summary>
  28. /// Defines the <see cref="ViewportSize"/> property.
  29. /// </summary>
  30. public static readonly StyledProperty<double> ViewportSizeProperty =
  31. AvaloniaProperty.Register<ScrollBar, double>(nameof(ViewportSize), defaultValue: double.NaN);
  32. /// <summary>
  33. /// Defines the <see cref="Visibility"/> property.
  34. /// </summary>
  35. public static readonly StyledProperty<ScrollBarVisibility> VisibilityProperty =
  36. AvaloniaProperty.Register<ScrollBar, ScrollBarVisibility>(nameof(Visibility));
  37. /// <summary>
  38. /// Defines the <see cref="Orientation"/> property.
  39. /// </summary>
  40. public static readonly StyledProperty<Orientation> OrientationProperty =
  41. AvaloniaProperty.Register<ScrollBar, Orientation>(nameof(Orientation), Orientation.Vertical);
  42. private Button _lineUpButton;
  43. private Button _lineDownButton;
  44. private Button _pageUpButton;
  45. private Button _pageDownButton;
  46. /// <summary>
  47. /// Initializes static members of the <see cref="ScrollBar"/> class.
  48. /// </summary>
  49. static ScrollBar()
  50. {
  51. Thumb.DragDeltaEvent.AddClassHandler<ScrollBar>((x, e) => x.OnThumbDragDelta(e), RoutingStrategies.Bubble);
  52. Thumb.DragCompletedEvent.AddClassHandler<ScrollBar>((x, e) => x.OnThumbDragComplete(e), RoutingStrategies.Bubble);
  53. }
  54. /// <summary>
  55. /// Initializes a new instance of the <see cref="ScrollBar"/> class.
  56. /// </summary>
  57. public ScrollBar()
  58. {
  59. var isVisible = Observable.Merge(
  60. this.GetObservable(MinimumProperty).Select(_ => Unit.Default),
  61. this.GetObservable(MaximumProperty).Select(_ => Unit.Default),
  62. this.GetObservable(ViewportSizeProperty).Select(_ => Unit.Default),
  63. this.GetObservable(VisibilityProperty).Select(_ => Unit.Default))
  64. .Select(_ => CalculateIsVisible());
  65. this.Bind(IsVisibleProperty, isVisible, BindingPriority.Style);
  66. UpdatePseudoClasses(Orientation);
  67. }
  68. /// <summary>
  69. /// Gets or sets the amount of the scrollable content that is currently visible.
  70. /// </summary>
  71. public double ViewportSize
  72. {
  73. get { return GetValue(ViewportSizeProperty); }
  74. set { SetValue(ViewportSizeProperty, value); }
  75. }
  76. /// <summary>
  77. /// Gets or sets a value that indicates whether the scrollbar should hide itself when it
  78. /// is not needed.
  79. /// </summary>
  80. public ScrollBarVisibility Visibility
  81. {
  82. get { return GetValue(VisibilityProperty); }
  83. set { SetValue(VisibilityProperty, value); }
  84. }
  85. /// <summary>
  86. /// Gets or sets the orientation of the scrollbar.
  87. /// </summary>
  88. public Orientation Orientation
  89. {
  90. get { return GetValue(OrientationProperty); }
  91. set { SetValue(OrientationProperty, value); }
  92. }
  93. public event EventHandler<ScrollEventArgs> Scroll;
  94. /// <summary>
  95. /// Calculates whether the scrollbar should be visible.
  96. /// </summary>
  97. /// <returns>The scrollbar's visibility.</returns>
  98. private bool CalculateIsVisible()
  99. {
  100. switch (Visibility)
  101. {
  102. case ScrollBarVisibility.Visible:
  103. return true;
  104. case ScrollBarVisibility.Disabled:
  105. case ScrollBarVisibility.Hidden:
  106. return false;
  107. case ScrollBarVisibility.Auto:
  108. return double.IsNaN(ViewportSize) || Maximum > 0;
  109. default:
  110. throw new InvalidOperationException("Invalid value for ScrollBar.Visibility.");
  111. }
  112. }
  113. protected override void OnKeyDown(KeyEventArgs e)
  114. {
  115. if (e.Key == Key.PageUp)
  116. {
  117. LargeDecrement();
  118. e.Handled = true;
  119. }
  120. else if (e.Key == Key.PageDown)
  121. {
  122. LargeIncrement();
  123. e.Handled = true;
  124. }
  125. }
  126. protected override void OnPropertyChanged<T>(
  127. AvaloniaProperty<T> property,
  128. Optional<T> oldValue,
  129. BindingValue<T> newValue,
  130. BindingPriority priority)
  131. {
  132. base.OnPropertyChanged(property, oldValue, newValue, priority);
  133. if (property == OrientationProperty)
  134. {
  135. UpdatePseudoClasses(newValue.GetValueOrDefault<Orientation>());
  136. }
  137. }
  138. protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
  139. {
  140. base.OnTemplateApplied(e);
  141. if (_lineUpButton != null)
  142. {
  143. _lineUpButton.Click -= LineUpClick;
  144. }
  145. if (_lineDownButton != null)
  146. {
  147. _lineDownButton.Click -= LineDownClick;
  148. }
  149. if (_pageUpButton != null)
  150. {
  151. _pageUpButton.Click -= PageUpClick;
  152. }
  153. if (_pageDownButton != null)
  154. {
  155. _pageDownButton.Click -= PageDownClick;
  156. }
  157. _lineUpButton = e.NameScope.Find<Button>("PART_LineUpButton");
  158. _lineDownButton = e.NameScope.Find<Button>("PART_LineDownButton");
  159. _pageUpButton = e.NameScope.Find<Button>("PART_PageUpButton");
  160. _pageDownButton = e.NameScope.Find<Button>("PART_PageDownButton");
  161. if (_lineUpButton != null)
  162. {
  163. _lineUpButton.Click += LineUpClick;
  164. }
  165. if (_lineDownButton != null)
  166. {
  167. _lineDownButton.Click += LineDownClick;
  168. }
  169. if (_pageUpButton != null)
  170. {
  171. _pageUpButton.Click += PageUpClick;
  172. }
  173. if (_pageDownButton != null)
  174. {
  175. _pageDownButton.Click += PageDownClick;
  176. }
  177. }
  178. private void LineUpClick(object sender, RoutedEventArgs e)
  179. {
  180. SmallDecrement();
  181. }
  182. private void LineDownClick(object sender, RoutedEventArgs e)
  183. {
  184. SmallIncrement();
  185. }
  186. private void PageUpClick(object sender, RoutedEventArgs e)
  187. {
  188. LargeDecrement();
  189. }
  190. private void PageDownClick(object sender, RoutedEventArgs e)
  191. {
  192. LargeIncrement();
  193. }
  194. private void SmallDecrement()
  195. {
  196. Value = Math.Max(Value - SmallChange, Minimum);
  197. OnScroll(ScrollEventType.SmallDecrement);
  198. }
  199. private void SmallIncrement()
  200. {
  201. Value = Math.Min(Value + SmallChange, Maximum);
  202. OnScroll(ScrollEventType.SmallIncrement);
  203. }
  204. private void LargeDecrement()
  205. {
  206. Value = Math.Max(Value - LargeChange, Minimum);
  207. OnScroll(ScrollEventType.LargeDecrement);
  208. }
  209. private void LargeIncrement()
  210. {
  211. Value = Math.Min(Value + LargeChange, Maximum);
  212. OnScroll(ScrollEventType.LargeIncrement);
  213. }
  214. private void OnThumbDragDelta(VectorEventArgs e)
  215. {
  216. OnScroll(ScrollEventType.ThumbTrack);
  217. }
  218. private void OnThumbDragComplete(VectorEventArgs e)
  219. {
  220. OnScroll(ScrollEventType.EndScroll);
  221. }
  222. protected void OnScroll(ScrollEventType scrollEventType)
  223. {
  224. Scroll?.Invoke(this, new ScrollEventArgs(scrollEventType, Value));
  225. }
  226. private void UpdatePseudoClasses(Orientation o)
  227. {
  228. PseudoClasses.Set(":vertical", o == Orientation.Vertical);
  229. PseudoClasses.Set(":horizontal", o == Orientation.Horizontal);
  230. }
  231. }
  232. }