PageEditContent.xaml.cs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. using PDF_Office.EventAggregators;
  2. using PDF_Office.Helper;
  3. using PDF_Office.Model.PageEdit;
  4. using Prism.Events;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using System.Windows;
  11. using System.Windows.Controls;
  12. using System.Windows.Controls.Primitives;
  13. using System.Windows.Data;
  14. using System.Windows.Documents;
  15. using System.Windows.Input;
  16. using System.Windows.Media;
  17. using System.Windows.Media.Imaging;
  18. using System.Windows.Navigation;
  19. using System.Windows.Shapes;
  20. using System.Windows.Threading;
  21. namespace PDF_Office.Views.PageEdit
  22. {
  23. /// <summary>
  24. /// PageEditContent.xaml 的交互逻辑
  25. /// </summary>
  26. public partial class PageEditContent : UserControl
  27. {
  28. /// <summary>
  29. /// 用于通知刷新图片的事件
  30. /// 因为目前的图片刷新策略依赖很多UI事件判断,因为将主要判断逻辑放在UI层,VM负责图片刷新,非必要情况尽量不要采用这种形式
  31. /// </summary>
  32. private IEventAggregator eventor;
  33. /// <summary>
  34. /// 暂存的唯一索引值,用于区分多页签
  35. /// </summary>
  36. private string unicode;
  37. /// <summary>
  38. /// 是否是加载时第一次触发滚动
  39. /// </summary>
  40. private bool isFirstScrollChange = true;
  41. /// <summary>
  42. /// 用于判断滚轮停止的计时器
  43. /// </summary>
  44. private DispatcherTimer timer = new DispatcherTimer();
  45. /// <summary>
  46. /// 记录当前滑块的状态
  47. /// </summary>
  48. private ScrollEventType scrolltype = ScrollEventType.EndScroll;
  49. public PageEditContent()
  50. {
  51. InitializeComponent();
  52. }
  53. public PageEditContent(IEventAggregator eventAggregator) :this()
  54. {
  55. eventor = eventAggregator;
  56. unicode = App.mainWindowViewModel.SelectedItem.Unicode;
  57. timer.Interval = TimeSpan.FromSeconds(0.3);
  58. timer.Tick += Timer_Tick;
  59. //订阅页面刷新事件
  60. eventor.GetEvent<PageEditNotifyEvent>().Subscribe(OneNotifyEvent, e => e.Unicode == unicode);
  61. }
  62. private void Timer_Tick(object sender, EventArgs e)
  63. {
  64. PulishEvent();
  65. timer.Stop();
  66. }
  67. private void OneNotifyEvent(PageEditNotifyEventArgs e)
  68. {
  69. PulishEvent();
  70. }
  71. #region UI事件
  72. /// <summary>
  73. /// 每次显示的时候就触发事件,刷新所有图片
  74. /// </summary>
  75. /// <param name="sender"></param>
  76. /// <param name="e"></param>
  77. private void PageEdit_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
  78. {
  79. if((bool)e.NewValue)
  80. {
  81. //当前页面没有发生变化时,刷新图片 这种情况下会拿两次图,需要留意
  82. PulishEvent();
  83. //当前页面发生变化时通过ScrollChanged事件来刷新图片
  84. isFirstScrollChange = true;
  85. }
  86. }
  87. /// <summary>
  88. /// 鼠标滚轮滚动时触发的事件
  89. /// </summary>
  90. /// <param name="sender"></param>
  91. /// <param name="e"></param>
  92. private void ListPageEdit_ScrollChanged(object sender, ScrollChangedEventArgs e)
  93. {
  94. if (isFirstScrollChange)
  95. {
  96. PulishEvent();//第一次加载时触发的Scollchange 直接刷新界面,减少白板显示时间
  97. isFirstScrollChange = false;
  98. return;
  99. }
  100. else
  101. {
  102. if (e.VerticalChange != 0)
  103. timer.Start();//暂时找不到比较好的 判断Scroller停止的方法,先用计时器粗略判断
  104. }
  105. }
  106. /// <summary>
  107. /// 拖动右侧滑块时触发的事件
  108. /// </summary>
  109. /// <param name="sender"></param>
  110. /// <param name="e"></param>
  111. private void ListPageEdit_Scroll(object sender, System.Windows.Controls.Primitives.ScrollEventArgs e)
  112. {
  113. scrolltype = e.ScrollEventType;
  114. if (scrolltype != System.Windows.Controls.Primitives.ScrollEventType.EndScroll)
  115. {
  116. timer.Stop();
  117. return;
  118. }
  119. PulishEvent();
  120. }
  121. #endregion
  122. #region 方法
  123. /// <summary>
  124. /// 发布事件
  125. /// </summary>
  126. private void PulishEvent()
  127. {
  128. var itemSize = (ListPageEdit.Items[0] as PageEditItem).ItemSize;
  129. var range = GetRoughFromView(ListPageEdit, itemSize, new Thickness(5, 10, 5, 10));
  130. eventor.GetEvent<PageEditRefreshEvent>().Publish(new PageEditRefreshEventArgs() { Unicode = unicode, PageRange = range });
  131. }
  132. /// <summary>
  133. /// 获取滑轨的垂直偏移量,结合item总数和Item尺寸以及间隔,来估算实际展示的item范围
  134. /// 返回值为页面范围 从1开始
  135. /// </summary>
  136. /// <param name="view"></param>
  137. /// <param name="itemSize"></param>
  138. /// <param name="itemMargin"></param>
  139. /// <returns></returns>
  140. private Tuple<int, int, int> GetRoughFromView(ListBox view, Size itemSize, Thickness itemMargin)
  141. {
  142. //var scrollViewer = GetScrollHost(view);
  143. var scrollViewer = CommonHelper.FindVisualChild<ScrollViewer>(view);
  144. if (scrollViewer == null || scrollViewer.ActualHeight == 0 || scrollViewer.ActualWidth == 0)//视图展开
  145. return new Tuple<int, int, int>(0, 0, 0);
  146. try
  147. {
  148. var currentHeight = scrollViewer.ActualHeight - view.Padding.Top;
  149. var currentWidth = scrollViewer.ActualWidth;
  150. //计算当前窗口大小能显示的行数和列数
  151. var columnCount = (int)(currentWidth / (itemSize.Width + itemMargin.Left));
  152. var rowCount = (int)Math.Ceiling(currentHeight / (itemSize.Height + itemMargin.Bottom));
  153. var preItemCount = (int)((scrollViewer.VerticalOffset / scrollViewer.ExtentHeight) * ((view.Items.Count + columnCount - 1) / columnCount));//滑动百分比*行数 = 大概的垂直位置
  154. preItemCount = preItemCount * columnCount;
  155. var preEnd = (int)(((scrollViewer.VerticalOffset + scrollViewer.ActualHeight) / scrollViewer.ExtentHeight) * ((view.Items.Count + columnCount - 1) / columnCount));
  156. preEnd = preEnd * columnCount + columnCount - 1;
  157. var middle = (int)Math.Ceiling(preItemCount + preEnd / 2d);
  158. return new Tuple<int, int, int>(
  159. Math.Max(preItemCount, 0),
  160. Math.Min(view.Items.Count, preEnd),
  161. middle);
  162. }
  163. catch { }
  164. return new Tuple<int, int, int>(0, 0, 0);
  165. }
  166. /// <summary>
  167. /// 获取listbox里的ScrollViewer对象
  168. /// 留意如果有重写ListBox对象,该方法可能无效
  169. /// </summary>
  170. /// <param name="listBox"></param>
  171. /// <returns></returns>
  172. private ScrollViewer GetScrollHost(ListBox listBox)
  173. {
  174. if (VisualTreeHelper.GetChildrenCount(listBox) > 0)
  175. {
  176. int s = VisualTreeHelper.GetChildrenCount(listBox);
  177. Border border = VisualTreeHelper.GetChild(listBox, 0) as Border;
  178. if (border != null)
  179. {
  180. return VisualTreeHelper.GetChild(border, 0) as ScrollViewer;
  181. }
  182. }
  183. return null;
  184. }
  185. #endregion
  186. /// <summary>
  187. /// 输入框显示时,自动获取焦点
  188. /// </summary>
  189. /// <param name="sender"></param>
  190. /// <param name="e"></param>
  191. private void TextBox_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
  192. {
  193. if((bool)e.NewValue)
  194. {
  195. (sender as Control).Focus();
  196. }
  197. }
  198. }
  199. }