Pagination.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. using System;
  2. using System.Windows;
  3. using System.Windows.Controls;
  4. using System.Windows.Input;
  5. using HandyControl.Data;
  6. using HandyControl.Interactivity;
  7. using HandyControl.Tools;
  8. using HandyControl.Tools.Extension;
  9. namespace HandyControl.Controls;
  10. /// <summary>
  11. /// 页码
  12. /// </summary>
  13. [TemplatePart(Name = ElementButtonLeft, Type = typeof(Button))]
  14. [TemplatePart(Name = ElementButtonRight, Type = typeof(Button))]
  15. [TemplatePart(Name = ElementButtonFirst, Type = typeof(RadioButton))]
  16. [TemplatePart(Name = ElementMoreLeft, Type = typeof(FrameworkElement))]
  17. [TemplatePart(Name = ElementPanelMain, Type = typeof(Panel))]
  18. [TemplatePart(Name = ElementMoreRight, Type = typeof(FrameworkElement))]
  19. [TemplatePart(Name = ElementButtonLast, Type = typeof(RadioButton))]
  20. [TemplatePart(Name = ElementButtonLast, Type = typeof(NumericUpDown))]
  21. public class Pagination : Control
  22. {
  23. #region Constants
  24. private const string ElementButtonLeft = "PART_ButtonLeft";
  25. private const string ElementButtonRight = "PART_ButtonRight";
  26. private const string ElementButtonFirst = "PART_ButtonFirst";
  27. private const string ElementMoreLeft = "PART_MoreLeft";
  28. private const string ElementPanelMain = "PART_PanelMain";
  29. private const string ElementMoreRight = "PART_MoreRight";
  30. private const string ElementButtonLast = "PART_ButtonLast";
  31. private const string ElementJump = "PART_Jump";
  32. #endregion Constants
  33. #region Data
  34. private Button _buttonLeft;
  35. private Button _buttonRight;
  36. private RadioButton _buttonFirst;
  37. private FrameworkElement _moreLeft;
  38. private Panel _panelMain;
  39. private FrameworkElement _moreRight;
  40. private RadioButton _buttonLast;
  41. private NumericUpDown _jumpNumericUpDown;
  42. private bool _appliedTemplate;
  43. #endregion Data
  44. #region Public Events
  45. /// <summary>
  46. /// 页面更新事件
  47. /// </summary>
  48. public static readonly RoutedEvent PageUpdatedEvent =
  49. EventManager.RegisterRoutedEvent("PageUpdated", RoutingStrategy.Bubble,
  50. typeof(EventHandler<FunctionEventArgs<int>>), typeof(Pagination));
  51. /// <summary>
  52. /// 页面更新事件
  53. /// </summary>
  54. public event EventHandler<FunctionEventArgs<int>> PageUpdated
  55. {
  56. add => AddHandler(PageUpdatedEvent, value);
  57. remove => RemoveHandler(PageUpdatedEvent, value);
  58. }
  59. #endregion Public Events
  60. public Pagination()
  61. {
  62. CommandBindings.Add(new CommandBinding(ControlCommands.Prev, ButtonPrev_OnClick));
  63. CommandBindings.Add(new CommandBinding(ControlCommands.Next, ButtonNext_OnClick));
  64. CommandBindings.Add(new CommandBinding(ControlCommands.Selected, ToggleButton_OnChecked));
  65. CommandBindings.Add(new CommandBinding(ControlCommands.Jump, (s, e) => PageIndex = (int) _jumpNumericUpDown.Value));
  66. OnAutoHidingChanged(AutoHiding);
  67. Update();
  68. }
  69. #region Public Properties
  70. #region MaxPageCount
  71. /// <summary>
  72. /// 最大页数
  73. /// </summary>
  74. public static readonly DependencyProperty MaxPageCountProperty = DependencyProperty.Register(
  75. nameof(MaxPageCount), typeof(int), typeof(Pagination), new PropertyMetadata(ValueBoxes.Int1Box, OnMaxPageCountChanged, CoerceMaxPageCount), ValidateHelper.IsInRangeOfPosIntIncludeZero);
  76. private static object CoerceMaxPageCount(DependencyObject d, object basevalue)
  77. {
  78. var intValue = (int) basevalue;
  79. return intValue < 1 ? 1 : intValue;
  80. }
  81. private static void OnMaxPageCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  82. {
  83. if (d is Pagination pagination)
  84. {
  85. if (pagination.PageIndex > pagination.MaxPageCount)
  86. {
  87. pagination.PageIndex = pagination.MaxPageCount;
  88. }
  89. pagination.CoerceValue(PageIndexProperty);
  90. pagination.OnAutoHidingChanged(pagination.AutoHiding);
  91. pagination.Update();
  92. }
  93. }
  94. /// <summary>
  95. /// 最大页数
  96. /// </summary>
  97. public int MaxPageCount
  98. {
  99. get => (int) GetValue(MaxPageCountProperty);
  100. set => SetValue(MaxPageCountProperty, value);
  101. }
  102. #endregion MaxPageCount
  103. #region DataCountPerPage
  104. /// <summary>
  105. /// 每页的数据量
  106. /// </summary>
  107. public static readonly DependencyProperty DataCountPerPageProperty = DependencyProperty.Register(
  108. nameof(DataCountPerPage), typeof(int), typeof(Pagination), new PropertyMetadata(20, OnDataCountPerPageChanged),
  109. ValidateHelper.IsInRangeOfPosInt);
  110. private static void OnDataCountPerPageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  111. {
  112. if (d is Pagination pagination)
  113. {
  114. pagination.Update();
  115. }
  116. }
  117. /// <summary>
  118. /// 每页的数据量
  119. /// </summary>
  120. public int DataCountPerPage
  121. {
  122. get => (int) GetValue(DataCountPerPageProperty);
  123. set => SetValue(DataCountPerPageProperty, value);
  124. }
  125. #endregion
  126. #region PageIndex
  127. /// <summary>
  128. /// 当前页
  129. /// </summary>
  130. public static readonly DependencyProperty PageIndexProperty = DependencyProperty.Register(
  131. nameof(PageIndex), typeof(int), typeof(Pagination), new PropertyMetadata(ValueBoxes.Int1Box, OnPageIndexChanged, CoercePageIndex), ValidateHelper.IsInRangeOfPosIntIncludeZero);
  132. private static object CoercePageIndex(DependencyObject d, object basevalue)
  133. {
  134. if (d is not Pagination pagination) return 1;
  135. var intValue = (int) basevalue;
  136. return intValue < 1
  137. ? 1
  138. : intValue > pagination.MaxPageCount
  139. ? pagination.MaxPageCount
  140. : intValue;
  141. }
  142. private static void OnPageIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  143. {
  144. if (d is Pagination pagination && e.NewValue is int value)
  145. {
  146. pagination.Update();
  147. pagination.RaiseEvent(new FunctionEventArgs<int>(PageUpdatedEvent, pagination)
  148. {
  149. Info = value
  150. });
  151. }
  152. }
  153. /// <summary>
  154. /// 当前页
  155. /// </summary>
  156. public int PageIndex
  157. {
  158. get => (int) GetValue(PageIndexProperty);
  159. set => SetValue(PageIndexProperty, value);
  160. }
  161. #endregion PageIndex
  162. #region MaxPageInterval
  163. /// <summary>
  164. /// 表示当前选中的按钮距离左右两个方向按钮的最大间隔(4表示间隔4个按钮,如果超过则用省略号表示)
  165. /// </summary>
  166. public static readonly DependencyProperty MaxPageIntervalProperty = DependencyProperty.Register(
  167. nameof(MaxPageInterval), typeof(int), typeof(Pagination), new PropertyMetadata(3, OnMaxPageIntervalChanged), ValidateHelper.IsInRangeOfPosIntIncludeZero);
  168. private static void OnMaxPageIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  169. {
  170. if (d is Pagination pagination)
  171. {
  172. pagination.Update();
  173. }
  174. }
  175. /// <summary>
  176. /// 表示当前选中的按钮距离左右两个方向按钮的最大间隔(4表示间隔4个按钮,如果超过则用省略号表示)
  177. /// </summary>
  178. public int MaxPageInterval
  179. {
  180. get => (int) GetValue(MaxPageIntervalProperty);
  181. set => SetValue(MaxPageIntervalProperty, value);
  182. }
  183. #endregion MaxPageInterval
  184. #region IsJumpEnabled
  185. public static readonly DependencyProperty IsJumpEnabledProperty = DependencyProperty.Register(
  186. nameof(IsJumpEnabled), typeof(bool), typeof(Pagination), new PropertyMetadata(ValueBoxes.FalseBox));
  187. public bool IsJumpEnabled
  188. {
  189. get => (bool) GetValue(IsJumpEnabledProperty);
  190. set => SetValue(IsJumpEnabledProperty, ValueBoxes.BooleanBox(value));
  191. }
  192. #endregion
  193. #region AutoHiding
  194. public static readonly DependencyProperty AutoHidingProperty = DependencyProperty.Register(
  195. nameof(AutoHiding), typeof(bool), typeof(Pagination), new PropertyMetadata(ValueBoxes.TrueBox, OnAutoHidingChanged));
  196. private static void OnAutoHidingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  197. {
  198. if (d is Pagination pagination)
  199. {
  200. pagination.OnAutoHidingChanged((bool) e.NewValue);
  201. }
  202. }
  203. private void OnAutoHidingChanged(bool newValue) => this.Show(!newValue || MaxPageCount > 1);
  204. public bool AutoHiding
  205. {
  206. get => (bool) GetValue(AutoHidingProperty);
  207. set => SetValue(AutoHidingProperty, ValueBoxes.BooleanBox(value));
  208. }
  209. #endregion
  210. public static readonly DependencyProperty PaginationButtonStyleProperty = DependencyProperty.Register(
  211. nameof(PaginationButtonStyle), typeof(Style), typeof(Pagination), new PropertyMetadata(default(Style)));
  212. public Style PaginationButtonStyle
  213. {
  214. get => (Style) GetValue(PaginationButtonStyleProperty);
  215. set => SetValue(PaginationButtonStyleProperty, value);
  216. }
  217. #endregion
  218. #region Public Methods
  219. public override void OnApplyTemplate()
  220. {
  221. _appliedTemplate = false;
  222. base.OnApplyTemplate();
  223. _buttonLeft = GetTemplateChild(ElementButtonLeft) as Button;
  224. _buttonRight = GetTemplateChild(ElementButtonRight) as Button;
  225. _buttonFirst = GetTemplateChild(ElementButtonFirst) as RadioButton;
  226. _moreLeft = GetTemplateChild(ElementMoreLeft) as FrameworkElement;
  227. _panelMain = GetTemplateChild(ElementPanelMain) as Panel;
  228. _moreRight = GetTemplateChild(ElementMoreRight) as FrameworkElement;
  229. _buttonLast = GetTemplateChild(ElementButtonLast) as RadioButton;
  230. _jumpNumericUpDown = GetTemplateChild(ElementJump) as NumericUpDown;
  231. CheckNull();
  232. _appliedTemplate = true;
  233. Update();
  234. }
  235. #endregion Public Methods
  236. #region Private Methods
  237. private void CheckNull()
  238. {
  239. if (_buttonLeft == null || _buttonRight == null || _buttonFirst == null ||
  240. _moreLeft == null || _panelMain == null || _moreRight == null ||
  241. _buttonLast == null) throw new Exception();
  242. }
  243. /// <summary>
  244. /// 更新
  245. /// </summary>
  246. private void Update()
  247. {
  248. if (!_appliedTemplate) return;
  249. _buttonLeft.IsEnabled = PageIndex > 1;
  250. _buttonRight.IsEnabled = PageIndex < MaxPageCount;
  251. if (MaxPageInterval == 0)
  252. {
  253. _buttonFirst.Collapse();
  254. _buttonLast.Collapse();
  255. _moreLeft.Collapse();
  256. _moreRight.Collapse();
  257. _panelMain.Children.Clear();
  258. var selectButton = CreateButton(PageIndex);
  259. _panelMain.Children.Add(selectButton);
  260. selectButton.IsChecked = true;
  261. return;
  262. }
  263. _buttonFirst.Show();
  264. _buttonLast.Show();
  265. _moreLeft.Show();
  266. _moreRight.Show();
  267. //更新最后一页
  268. if (MaxPageCount == 1)
  269. {
  270. _buttonLast.Collapse();
  271. }
  272. else
  273. {
  274. _buttonLast.Show();
  275. _buttonLast.Content = MaxPageCount.ToString();
  276. }
  277. //更新省略号
  278. var right = MaxPageCount - PageIndex;
  279. var left = PageIndex - 1;
  280. _moreRight.Show(right > MaxPageInterval);
  281. _moreLeft.Show(left > MaxPageInterval);
  282. //更新中间部分
  283. _panelMain.Children.Clear();
  284. if (PageIndex > 1 && PageIndex < MaxPageCount)
  285. {
  286. var selectButton = CreateButton(PageIndex);
  287. _panelMain.Children.Add(selectButton);
  288. selectButton.IsChecked = true;
  289. }
  290. else if (PageIndex == 1)
  291. {
  292. _buttonFirst.IsChecked = true;
  293. }
  294. else
  295. {
  296. _buttonLast.IsChecked = true;
  297. }
  298. var sub = PageIndex;
  299. for (var i = 0; i < MaxPageInterval - 1; i++)
  300. {
  301. if (--sub > 1)
  302. {
  303. _panelMain.Children.Insert(0, CreateButton(sub));
  304. }
  305. else
  306. {
  307. break;
  308. }
  309. }
  310. var add = PageIndex;
  311. for (var i = 0; i < MaxPageInterval - 1; i++)
  312. {
  313. if (++add < MaxPageCount)
  314. {
  315. _panelMain.Children.Add(CreateButton(add));
  316. }
  317. else
  318. {
  319. break;
  320. }
  321. }
  322. }
  323. private void ButtonPrev_OnClick(object sender, RoutedEventArgs e) => PageIndex--;
  324. private void ButtonNext_OnClick(object sender, RoutedEventArgs e) => PageIndex++;
  325. private RadioButton CreateButton(int page)
  326. {
  327. return new()
  328. {
  329. Style = PaginationButtonStyle,
  330. Content = page.ToString()
  331. };
  332. }
  333. private void ToggleButton_OnChecked(object sender, RoutedEventArgs e)
  334. {
  335. if (e.OriginalSource is not RadioButton button) return;
  336. if (button.IsChecked == false) return;
  337. PageIndex = int.Parse(button.Content.ToString());
  338. }
  339. #endregion Private Methods
  340. }