PageEditContent.xaml.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  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. using WpfToolkit.Controls;
  22. namespace PDF_Office.Views.PageEdit
  23. {
  24. /// <summary>
  25. /// PageEditContent.xaml 的交互逻辑
  26. /// </summary>
  27. public partial class PageEditContent : UserControl
  28. {
  29. /// <summary>
  30. /// 用于通知刷新图片的事件
  31. /// 因为目前的图片刷新策略依赖很多UI事件判断,因为将主要判断逻辑放在UI层,VM负责图片刷新,非必要情况尽量不要采用这种形式
  32. /// </summary>
  33. private IEventAggregator eventor;
  34. /// <summary>
  35. /// 暂存的唯一索引值,用于区分多页签
  36. /// </summary>
  37. private string unicode;
  38. /// <summary>
  39. /// 是否是加载时第一次触发滚动
  40. /// </summary>
  41. private bool isFirstScrollChange = true;
  42. /// <summary>
  43. /// 用于判断滚轮停止的计时器
  44. /// </summary>
  45. private DispatcherTimer timer = new DispatcherTimer();
  46. /// <summary>
  47. /// 判断是否开始框选
  48. /// </summary>
  49. private bool startChoose = false;
  50. /// <summary>
  51. /// 框选的起始位置
  52. /// </summary>
  53. private Point starPosition = new Point();
  54. /// <summary>
  55. /// 记录当前滑块的状态
  56. /// </summary>
  57. private ScrollEventType scrolltype = ScrollEventType.EndScroll;
  58. public PageEditContent()
  59. {
  60. InitializeComponent();
  61. }
  62. public PageEditContent(IEventAggregator eventAggregator) :this()
  63. {
  64. eventor = eventAggregator;
  65. unicode = App.mainWindowViewModel.SelectedItem.Unicode;
  66. timer.Interval = TimeSpan.FromSeconds(0.3);
  67. timer.Tick += Timer_Tick;
  68. //订阅页面刷新事件
  69. eventor.GetEvent<PageEditNotifyEvent>().Subscribe(OneNotifyEvent, e => e.Unicode == unicode);
  70. }
  71. private void Timer_Tick(object sender, EventArgs e)
  72. {
  73. PulishEvent();
  74. timer.Stop();
  75. }
  76. private void OneNotifyEvent(PageEditNotifyEventArgs e)
  77. {
  78. PulishEvent();
  79. }
  80. #region UI事件
  81. /// <summary>
  82. /// 每次显示的时候就触发事件,刷新所有图片
  83. /// </summary>
  84. /// <param name="sender"></param>
  85. /// <param name="e"></param>
  86. private void PageEdit_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
  87. {
  88. if((bool)e.NewValue)
  89. {
  90. //当前页面没有发生变化时,刷新图片 这种情况下会拿两次图,需要留意
  91. PulishEvent();
  92. //当前页面发生变化时通过ScrollChanged事件来刷新图片
  93. isFirstScrollChange = true;
  94. }
  95. }
  96. /// <summary>
  97. /// 鼠标滚轮滚动时触发的事件
  98. /// </summary>
  99. /// <param name="sender"></param>
  100. /// <param name="e"></param>
  101. private void ListPageEdit_ScrollChanged(object sender, ScrollChangedEventArgs e)
  102. {
  103. if (isFirstScrollChange)
  104. {
  105. PulishEvent();//第一次加载时触发的Scollchange 直接刷新界面,减少白板显示时间
  106. isFirstScrollChange = false;
  107. return;
  108. }
  109. else
  110. {
  111. if (e.VerticalChange != 0)
  112. timer.Start();//暂时找不到比较好的 判断Scroller停止的方法,先用计时器粗略判断
  113. }
  114. }
  115. /// <summary>
  116. /// 拖动右侧滑块时触发的事件
  117. /// </summary>
  118. /// <param name="sender"></param>
  119. /// <param name="e"></param>
  120. private void ListPageEdit_Scroll(object sender, System.Windows.Controls.Primitives.ScrollEventArgs e)
  121. {
  122. scrolltype = e.ScrollEventType;
  123. if (scrolltype != System.Windows.Controls.Primitives.ScrollEventType.EndScroll)
  124. {
  125. timer.Stop();
  126. return;
  127. }
  128. PulishEvent();
  129. }
  130. #endregion
  131. #region 方法
  132. /// <summary>
  133. /// 发布事件
  134. /// </summary>
  135. private void PulishEvent()
  136. {
  137. var itemSize = (ListPageEdit.Items[0] as PageEditItem).ItemSize;
  138. var range = GetRoughFromView(ListPageEdit, itemSize, new Thickness(5, 10, 5, 10));
  139. eventor.GetEvent<PageEditRefreshEvent>().Publish(new PageEditRefreshEventArgs() { Unicode = unicode, PageRange = range });
  140. }
  141. /// <summary>
  142. /// 获取滑轨的垂直偏移量,结合item总数和Item尺寸以及间隔,来估算实际展示的item范围
  143. /// 返回值为页面范围 从1开始
  144. /// </summary>
  145. /// <param name="view"></param>
  146. /// <param name="itemSize"></param>
  147. /// <param name="itemMargin"></param>
  148. /// <returns></returns>
  149. private Tuple<int, int, int> GetRoughFromView(ListBox view, Size itemSize, Thickness itemMargin)
  150. {
  151. //var scrollViewer = GetScrollHost(view);
  152. var scrollViewer = CommonHelper.FindVisualChild<ScrollViewer>(view);
  153. if (scrollViewer == null || scrollViewer.ActualHeight == 0 || scrollViewer.ActualWidth == 0)//视图展开
  154. return new Tuple<int, int, int>(0, 0, 0);
  155. try
  156. {
  157. var currentHeight = scrollViewer.ActualHeight - view.Padding.Top;
  158. var currentWidth = scrollViewer.ActualWidth;
  159. //计算当前窗口大小能显示的行数和列数
  160. var columnCount = (int)(currentWidth / (itemSize.Width + itemMargin.Left));
  161. var rowCount = (int)Math.Ceiling(currentHeight / (itemSize.Height + itemMargin.Bottom));
  162. var preItemCount = (int)((scrollViewer.VerticalOffset / scrollViewer.ExtentHeight) * ((view.Items.Count + columnCount - 1) / columnCount));//滑动百分比*行数 = 大概的垂直位置
  163. preItemCount = preItemCount * columnCount;
  164. var preEnd = (int)(((scrollViewer.VerticalOffset + scrollViewer.ActualHeight) / scrollViewer.ExtentHeight) * ((view.Items.Count + columnCount - 1) / columnCount));
  165. preEnd = preEnd * columnCount + columnCount - 1;
  166. var middle = (int)Math.Ceiling(preItemCount + preEnd / 2d);
  167. return new Tuple<int, int, int>(
  168. Math.Max(preItemCount, 0),
  169. Math.Min(view.Items.Count, preEnd),
  170. middle);
  171. }
  172. catch { }
  173. return new Tuple<int, int, int>(0, 0, 0);
  174. }
  175. /// <summary>
  176. /// 获取listbox里的ScrollViewer对象
  177. /// 留意如果有重写ListBox对象,该方法可能无效
  178. /// </summary>
  179. /// <param name="listBox"></param>
  180. /// <returns></returns>
  181. private ScrollViewer GetScrollHost(ListBox listBox)
  182. {
  183. if (VisualTreeHelper.GetChildrenCount(listBox) > 0)
  184. {
  185. int s = VisualTreeHelper.GetChildrenCount(listBox);
  186. Border border = VisualTreeHelper.GetChild(listBox, 0) as Border;
  187. if (border != null)
  188. {
  189. return VisualTreeHelper.GetChild(border, 0) as ScrollViewer;
  190. }
  191. }
  192. return null;
  193. }
  194. #endregion
  195. /// <summary>
  196. /// 输入框显示时,自动获取焦点
  197. /// </summary>
  198. /// <param name="sender"></param>
  199. /// <param name="e"></param>
  200. private void TextBox_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
  201. {
  202. if((bool)e.NewValue)
  203. {
  204. (sender as Control).Focus();
  205. }
  206. }
  207. private void ListPageEdit_PreviewMouseMove(object sender, MouseEventArgs e)
  208. {
  209. try
  210. {
  211. if (e.LeftButton == MouseButtonState.Pressed)
  212. {
  213. if (startChoose)
  214. {
  215. var position = e.GetPosition(ListPageEdit);
  216. if (position.X < 5 || position.X > ListPageEdit.ActualWidth - 5 || position.Y < 5 || position.Y > ListPageEdit.ActualHeight - 5)
  217. {
  218. startChoose = false;
  219. RectChoose.Visibility = Visibility.Collapsed;
  220. Mouse.Capture(null);//暂时未想到靠近顶部和底部自动翻滚的好方法,只能先屏蔽这部分功能
  221. return;
  222. }
  223. DoSelectItems();
  224. return;
  225. }
  226. //拖拽排序的逻辑
  227. //var pos = e.GetPosition(ListPageEdit);
  228. //if (pos.Y < 0 || pos.Y > ListPageEdit.ActualHeight)
  229. //{
  230. // MidLane.Visibility = Visibility.Collapsed;
  231. // return;
  232. //}
  233. //HitTestResult result = VisualTreeHelper.HitTest(PageEditListBox, pos);
  234. //if (result == null)
  235. //{
  236. // return;
  237. //}
  238. //var listBoxItem = CommonHelper.FindVisualParent<ListBoxItem>(result.VisualHit);
  239. //if (listBoxItem == null)
  240. //{
  241. // return;
  242. //}
  243. //isDragingEnter = false;
  244. //tempItem = listBoxItem;
  245. //var panel = GetPanel(tempItem);
  246. //var item_pos = e.GetPosition(panel);
  247. //if (item_pos != null)
  248. //{
  249. // item_x = item_pos.X;
  250. // item_y = item_pos.Y;
  251. //}
  252. //var scroll = GetScrollHost(PageEditListBox);
  253. //DataObject dataObj = new DataObject(listBoxItem.Content as StackPanel);
  254. //DragDrop.DoDragDrop(PageEditListBox, dataObj, DragDropEffects.Move);
  255. //Mouse.Capture(PageEditListBox);
  256. //return;
  257. }
  258. RectChoose.Visibility = Visibility.Collapsed;
  259. startChoose = false;
  260. Mouse.Capture(null);
  261. }
  262. catch (Exception ex)
  263. {
  264. // MidLane.Visibility = Visibility.Collapsed;
  265. }
  266. }
  267. private void ListPageEdit_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
  268. {
  269. var pos = e.GetPosition(ListPageEdit);
  270. HitTestResult result = VisualTreeHelper.HitTest(ListPageEdit, pos);
  271. if (result == null)
  272. {
  273. return;
  274. }
  275. //未选中item 并且不是点击滑轨时 开始框选
  276. var listBoxItem = CommonHelper.FindVisualParent<ListBoxItem>(result.VisualHit);
  277. var scroller = CommonHelper.FindVisualParent<ScrollBar>(result.VisualHit);
  278. if (listBoxItem == null)
  279. {
  280. if (scroller != null)
  281. {
  282. startChoose = false;
  283. return;
  284. }
  285. //点击空白处时开始框选
  286. startChoose = true;
  287. ListPageEdit.SelectedItems.Clear();
  288. starPosition = e.GetPosition(ListPageEdit);
  289. starPosition = new Point(starPosition.X, starPosition.Y + GetWrapPanel(ListPageEdit).VerticalOffset);
  290. Mouse.Capture(ListPageEdit);
  291. return;
  292. }
  293. startChoose = false;//选中了item 时,不能框选
  294. }
  295. private void ListPageEdit_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
  296. {
  297. startChoose = false;
  298. Mouse.Capture(null);
  299. RectChoose.Visibility = Visibility.Collapsed;
  300. }
  301. /// <summary>
  302. /// 获取Listobox的Wrappanel容器
  303. /// </summary>
  304. /// <param name="listBox"></param>
  305. /// <returns></returns>
  306. public VirtualizingWrapPanel GetWrapPanel(ListBox listBox)
  307. {
  308. Border border = VisualTreeHelper.GetChild(listBox, 0) as Border;
  309. var panel = CommonHelper.FindVisualChild<VirtualizingWrapPanel>(border);
  310. return panel;
  311. }
  312. /// <summary>
  313. ///根据鼠标拖选的框 选中里面的Item
  314. /// </summary>
  315. private void DoSelectItems()
  316. {
  317. var s = GetScrollHost(ListPageEdit);
  318. Point start = new Point();
  319. //通过 实时的垂直偏移量和第一次的偏移量抵消,来获取准确的垂直偏移值。
  320. start = new Point(starPosition.X, starPosition.Y - s.VerticalOffset);
  321. var rec = new Rect(start, Mouse.GetPosition(ListPageEdit));
  322. RectChoose.Margin = new Thickness(rec.Left, rec.Top, 0, 0);
  323. RectChoose.Width = rec.Width;
  324. RectChoose.Height = rec.Height;
  325. RectChoose.Visibility = Visibility.Visible;
  326. //检测遍历所有项,筛选在矩形框中的Item
  327. for (int i = 0; i < ListPageEdit.Items.Count; i++)
  328. {
  329. var _item = ListPageEdit.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
  330. //通过这一步来避免重复误选中
  331. var parent = CommonHelper.FindVisualParent<VirtualizingWrapPanel>(_item);
  332. if (parent == null)
  333. continue;
  334. var v = VisualTreeHelper.GetOffset(_item);
  335. if (rec.IntersectsWith(new Rect(v.X, v.Y, _item.ActualWidth, _item.ActualHeight)))
  336. {
  337. ListPageEdit.SelectedItems.Add(ListPageEdit.Items[i]);
  338. }
  339. else
  340. {
  341. ListPageEdit.SelectedItems.Remove(ListPageEdit.Items[i]);
  342. }
  343. }
  344. return;
  345. }
  346. }
  347. }