LayoutGridControl.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. /*************************************************************************************
  2. Extended WPF Toolkit
  3. Copyright (C) 2007-2013 Xceed Software Inc.
  4. This program is provided to you under the terms of the Microsoft Public
  5. License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
  6. For more features, controls, and fast professional support,
  7. pick up the Plus Edition at http://xceed.com/wpf_toolkit
  8. Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids
  9. ***********************************************************************************/
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Linq;
  13. using System.Text;
  14. using System.Windows.Controls;
  15. using System.Windows;
  16. using System.Windows.Input;
  17. using System.Windows.Media;
  18. using Xceed.Wpf.AvalonDock.Layout;
  19. using System.Diagnostics;
  20. using System.Windows.Threading;
  21. namespace Xceed.Wpf.AvalonDock.Controls
  22. {
  23. public abstract class LayoutGridControl<T> : Grid, ILayoutControl where T : class, ILayoutPanelElement
  24. {
  25. static LayoutGridControl()
  26. {
  27. }
  28. internal LayoutGridControl(LayoutPositionableGroup<T> model, Orientation orientation)
  29. {
  30. if (model == null)
  31. throw new ArgumentNullException("model");
  32. _model = model;
  33. _orientation = orientation;
  34. FlowDirection = System.Windows.FlowDirection.LeftToRight;
  35. }
  36. LayoutPositionableGroup<T> _model;
  37. public ILayoutElement Model
  38. {
  39. get { return _model; }
  40. }
  41. Orientation _orientation;
  42. public Orientation Orientation
  43. {
  44. get { return (_model as ILayoutOrientableGroup).Orientation; }
  45. }
  46. bool _initialized;
  47. ChildrenTreeChange? _asyncRefreshCalled;
  48. bool AsyncRefreshCalled
  49. {
  50. get { return _asyncRefreshCalled != null; }
  51. }
  52. protected override void OnInitialized(EventArgs e)
  53. {
  54. base.OnInitialized(e);
  55. _model.ChildrenTreeChanged += (s, args) =>
  56. {
  57. if (_asyncRefreshCalled.HasValue &&
  58. _asyncRefreshCalled.Value == args.Change)
  59. return;
  60. _asyncRefreshCalled = args.Change;
  61. Dispatcher.BeginInvoke(new Action(() =>
  62. {
  63. _asyncRefreshCalled = null;
  64. UpdateChildren();
  65. }), DispatcherPriority.Normal, null);
  66. };
  67. this.LayoutUpdated += new EventHandler(OnLayoutUpdated);
  68. }
  69. void OnLayoutUpdated(object sender, EventArgs e)
  70. {
  71. var modelWithAtcualSize = _model as ILayoutPositionableElementWithActualSize;
  72. modelWithAtcualSize.ActualWidth = ActualWidth;
  73. modelWithAtcualSize.ActualHeight = ActualHeight;
  74. if (!_initialized)
  75. {
  76. _initialized = true;
  77. UpdateChildren();
  78. }
  79. }
  80. void UpdateChildren()
  81. {
  82. var alreadyContainedChildren = Children.OfType<ILayoutControl>().ToArray();
  83. DetachOldSplitters();
  84. DetachPropertChangeHandler();
  85. Children.Clear();
  86. ColumnDefinitions.Clear();
  87. RowDefinitions.Clear();
  88. if (_model == null ||
  89. _model.Root == null)
  90. return;
  91. var manager = _model.Root.Manager;
  92. if (manager == null)
  93. return;
  94. foreach (ILayoutElement child in _model.Children)
  95. {
  96. var foundContainedChild = alreadyContainedChildren.FirstOrDefault(chVM => chVM.Model == child);
  97. if (foundContainedChild != null)
  98. Children.Add(foundContainedChild as UIElement);
  99. else
  100. Children.Add(manager.CreateUIElementForModel(child));
  101. }
  102. CreateSplitters();
  103. UpdateRowColDefinitions();
  104. AttachNewSplitters();
  105. AttachPropertyChangeHandler();
  106. }
  107. private void AttachPropertyChangeHandler()
  108. {
  109. foreach (var child in InternalChildren.OfType<ILayoutControl>())
  110. {
  111. child.Model.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(this.OnChildModelPropertyChanged);
  112. }
  113. }
  114. private void DetachPropertChangeHandler()
  115. {
  116. foreach (var child in InternalChildren.OfType<ILayoutControl>())
  117. {
  118. child.Model.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(this.OnChildModelPropertyChanged);
  119. }
  120. }
  121. void OnChildModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
  122. {
  123. if (AsyncRefreshCalled)
  124. return;
  125. if (_fixingChildrenDockLengths.CanEnter && e.PropertyName == "DockWidth" && Orientation == System.Windows.Controls.Orientation.Horizontal)
  126. {
  127. if (ColumnDefinitions.Count == InternalChildren.Count)
  128. {
  129. var changedElement = sender as ILayoutPositionableElement;
  130. var childFromModel = InternalChildren.OfType<ILayoutControl>().First(ch => ch.Model == changedElement) as UIElement;
  131. int indexOfChild = InternalChildren.IndexOf(childFromModel);
  132. ColumnDefinitions[indexOfChild].Width = changedElement.DockWidth;
  133. }
  134. }
  135. else if (_fixingChildrenDockLengths.CanEnter && e.PropertyName == "DockHeight" && Orientation == System.Windows.Controls.Orientation.Vertical)
  136. {
  137. if (RowDefinitions.Count == InternalChildren.Count)
  138. {
  139. var changedElement = sender as ILayoutPositionableElement;
  140. var childFromModel = InternalChildren.OfType<ILayoutControl>().First(ch => ch.Model == changedElement) as UIElement;
  141. int indexOfChild = InternalChildren.IndexOf(childFromModel);
  142. RowDefinitions[indexOfChild].Height = changedElement.DockHeight;
  143. }
  144. }
  145. else if (e.PropertyName == "IsVisible")
  146. {
  147. UpdateRowColDefinitions();
  148. }
  149. }
  150. void UpdateRowColDefinitions()
  151. {
  152. var root = _model.Root;
  153. if (root == null)
  154. return;
  155. var manager = root.Manager;
  156. if (manager == null)
  157. return;
  158. FixChildrenDockLengths();
  159. //Debug.Assert(InternalChildren.Count == _model.ChildrenCount + (_model.ChildrenCount - 1));
  160. #region Setup GridRows/Cols
  161. RowDefinitions.Clear();
  162. ColumnDefinitions.Clear();
  163. if (Orientation == Orientation.Horizontal)
  164. {
  165. int iColumn = 0;
  166. int iChild = 0;
  167. for (int iChildModel = 0; iChildModel < _model.Children.Count; iChildModel++, iColumn++, iChild++)
  168. {
  169. var childModel = _model.Children[iChildModel] as ILayoutPositionableElement;
  170. ColumnDefinitions.Add(new ColumnDefinition()
  171. {
  172. Width = childModel.IsVisible ? childModel.DockWidth : new GridLength(0.0, GridUnitType.Pixel),
  173. MinWidth = childModel.IsVisible ? childModel.DockMinWidth : 0.0
  174. });
  175. Grid.SetColumn(InternalChildren[iChild], iColumn);
  176. //append column for splitter
  177. if (iChild < InternalChildren.Count - 1)
  178. {
  179. iChild++;
  180. iColumn++;
  181. bool nextChildModelVisibleExist = false;
  182. for (int i = iChildModel + 1; i < _model.Children.Count; i++)
  183. {
  184. var nextChildModel = _model.Children[i] as ILayoutPositionableElement;
  185. if (nextChildModel.IsVisible)
  186. {
  187. nextChildModelVisibleExist = true;
  188. break;
  189. }
  190. }
  191. ColumnDefinitions.Add(new ColumnDefinition()
  192. {
  193. Width = childModel.IsVisible && nextChildModelVisibleExist ? new GridLength(manager.GridSplitterWidth) : new GridLength(0.0, GridUnitType.Pixel)
  194. });
  195. Grid.SetColumn(InternalChildren[iChild], iColumn);
  196. }
  197. }
  198. }
  199. else //if (_model.Orientation == Orientation.Vertical)
  200. {
  201. int iRow = 0;
  202. int iChild = 0;
  203. for (int iChildModel = 0; iChildModel < _model.Children.Count; iChildModel++, iRow++, iChild++)
  204. {
  205. var childModel = _model.Children[iChildModel] as ILayoutPositionableElement;
  206. RowDefinitions.Add(new RowDefinition()
  207. {
  208. Height = childModel.IsVisible ? childModel.DockHeight : new GridLength(0.0, GridUnitType.Pixel),
  209. MinHeight = childModel.IsVisible ? childModel.DockMinHeight : 0.0
  210. });
  211. Grid.SetRow(InternalChildren[iChild], iRow);
  212. //if (RowDefinitions.Last().Height.Value == 0.0)
  213. // System.Diagnostics.Debugger.Break();
  214. //append row for splitter (if necessary)
  215. if (iChild < InternalChildren.Count - 1)
  216. {
  217. iChild++;
  218. iRow++;
  219. bool nextChildModelVisibleExist = false;
  220. for (int i = iChildModel + 1; i < _model.Children.Count; i++)
  221. {
  222. var nextChildModel = _model.Children[i] as ILayoutPositionableElement;
  223. if (nextChildModel.IsVisible)
  224. {
  225. nextChildModelVisibleExist = true;
  226. break;
  227. }
  228. }
  229. RowDefinitions.Add(new RowDefinition()
  230. {
  231. Height = childModel.IsVisible && nextChildModelVisibleExist ? new GridLength(manager.GridSplitterHeight) : new GridLength(0.0, GridUnitType.Pixel)
  232. });
  233. //if (RowDefinitions.Last().Height.Value == 0.0)
  234. // System.Diagnostics.Debugger.Break();
  235. Grid.SetRow(InternalChildren[iChild], iRow);
  236. }
  237. }
  238. }
  239. #endregion
  240. }
  241. ReentrantFlag _fixingChildrenDockLengths = new ReentrantFlag();
  242. protected void FixChildrenDockLengths()
  243. {
  244. using (_fixingChildrenDockLengths.Enter())
  245. OnFixChildrenDockLengths();
  246. }
  247. protected abstract void OnFixChildrenDockLengths();
  248. #region Splitters
  249. void CreateSplitters()
  250. {
  251. for (int iChild = 1; iChild < Children.Count; iChild++)
  252. {
  253. var splitter = new LayoutGridResizerControl();
  254. splitter.Cursor = this.Orientation == Orientation.Horizontal ? Cursors.SizeWE : Cursors.SizeNS;
  255. Children.Insert(iChild, splitter);
  256. iChild++;
  257. }
  258. }
  259. void DetachOldSplitters()
  260. {
  261. foreach (var splitter in Children.OfType<LayoutGridResizerControl>())
  262. {
  263. splitter.DragStarted -= new System.Windows.Controls.Primitives.DragStartedEventHandler(OnSplitterDragStarted);
  264. splitter.DragDelta -= new System.Windows.Controls.Primitives.DragDeltaEventHandler(OnSplitterDragDelta);
  265. splitter.DragCompleted -= new System.Windows.Controls.Primitives.DragCompletedEventHandler(OnSplitterDragCompleted);
  266. }
  267. }
  268. void AttachNewSplitters()
  269. {
  270. foreach (var splitter in Children.OfType<LayoutGridResizerControl>())
  271. {
  272. splitter.DragStarted += new System.Windows.Controls.Primitives.DragStartedEventHandler(OnSplitterDragStarted);
  273. splitter.DragDelta += new System.Windows.Controls.Primitives.DragDeltaEventHandler(OnSplitterDragDelta);
  274. splitter.DragCompleted += new System.Windows.Controls.Primitives.DragCompletedEventHandler(OnSplitterDragCompleted);
  275. }
  276. }
  277. void OnSplitterDragStarted(object sender, System.Windows.Controls.Primitives.DragStartedEventArgs e)
  278. {
  279. var resizer = sender as LayoutGridResizerControl;
  280. ShowResizerOverlayWindow(resizer);
  281. }
  282. void OnSplitterDragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
  283. {
  284. LayoutGridResizerControl splitter = sender as LayoutGridResizerControl;
  285. var rootVisual = this.FindVisualTreeRoot() as Visual;
  286. var trToWnd = TransformToAncestor(rootVisual);
  287. Vector transformedDelta = trToWnd.Transform(new Point(e.HorizontalChange, e.VerticalChange)) -
  288. trToWnd.Transform(new Point());
  289. if (Orientation == System.Windows.Controls.Orientation.Horizontal)
  290. {
  291. Canvas.SetLeft(_resizerGhost, MathHelper.MinMax(_initialStartPoint.X + transformedDelta.X, 0.0, _resizerWindowHost.Width - _resizerGhost.Width));
  292. }
  293. else
  294. {
  295. Canvas.SetTop(_resizerGhost, MathHelper.MinMax(_initialStartPoint.Y + transformedDelta.Y, 0.0, _resizerWindowHost.Height - _resizerGhost.Height));
  296. }
  297. }
  298. void OnSplitterDragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
  299. {
  300. LayoutGridResizerControl splitter = sender as LayoutGridResizerControl;
  301. var rootVisual = this.FindVisualTreeRoot() as Visual;
  302. var trToWnd = TransformToAncestor(rootVisual);
  303. Vector transformedDelta = trToWnd.Transform(new Point(e.HorizontalChange, e.VerticalChange)) -
  304. trToWnd.Transform(new Point());
  305. double delta;
  306. if (Orientation == System.Windows.Controls.Orientation.Horizontal)
  307. delta = Canvas.GetLeft(_resizerGhost) - _initialStartPoint.X;
  308. else
  309. delta = Canvas.GetTop(_resizerGhost) - _initialStartPoint.Y;
  310. int indexOfResizer = InternalChildren.IndexOf(splitter);
  311. var prevChild = InternalChildren[indexOfResizer - 1] as FrameworkElement;
  312. var nextChild = GetNextVisibleChild(indexOfResizer);
  313. var prevChildActualSize = prevChild.TransformActualSizeToAncestor();
  314. var nextChildActualSize = nextChild.TransformActualSizeToAncestor();
  315. var prevChildModel = (ILayoutPositionableElement)(prevChild as ILayoutControl).Model;
  316. var nextChildModel = (ILayoutPositionableElement)(nextChild as ILayoutControl).Model;
  317. if (Orientation == System.Windows.Controls.Orientation.Horizontal)
  318. {
  319. //Trace.WriteLine(string.Format("PrevChild From {0}", prevChildModel.DockWidth));
  320. if (prevChildModel.DockWidth.IsStar)
  321. {
  322. prevChildModel.DockWidth = new GridLength(prevChildModel.DockWidth.Value * (prevChildActualSize.Width + delta) / prevChildActualSize.Width, GridUnitType.Star);
  323. }
  324. else
  325. {
  326. prevChildModel.DockWidth = new GridLength(prevChildModel.DockWidth.Value + delta, GridUnitType.Pixel);
  327. }
  328. //Trace.WriteLine(string.Format("PrevChild To {0}", prevChildModel.DockWidth));
  329. //Trace.WriteLine(string.Format("NextChild From {0}", nextChildModel.DockWidth));
  330. if (nextChildModel.DockWidth.IsStar)
  331. {
  332. nextChildModel.DockWidth = new GridLength(nextChildModel.DockWidth.Value * (nextChildActualSize.Width - delta) / nextChildActualSize.Width, GridUnitType.Star);
  333. }
  334. else
  335. {
  336. nextChildModel.DockWidth = new GridLength(nextChildModel.DockWidth.Value - delta, GridUnitType.Pixel);
  337. }
  338. //Trace.WriteLine(string.Format("NextChild To {0}", nextChildModel.DockWidth));
  339. }
  340. else
  341. {
  342. //Trace.WriteLine(string.Format("PrevChild From {0}", prevChildModel.DockHeight));
  343. if (prevChildModel.DockHeight.IsStar)
  344. {
  345. prevChildModel.DockHeight = new GridLength(prevChildModel.DockHeight.Value * (prevChildActualSize.Height + delta) / prevChildActualSize.Height, GridUnitType.Star);
  346. }
  347. else
  348. {
  349. prevChildModel.DockHeight = new GridLength(prevChildModel.DockHeight.Value + delta, GridUnitType.Pixel);
  350. }
  351. //Trace.WriteLine(string.Format("PrevChild To {0}", prevChildModel.DockHeight));
  352. //Trace.WriteLine(string.Format("NextChild From {0}", nextChildModel.DockHeight));
  353. if (nextChildModel.DockHeight.IsStar)
  354. {
  355. nextChildModel.DockHeight = new GridLength(nextChildModel.DockHeight.Value * (nextChildActualSize.Height - delta) / nextChildActualSize.Height, GridUnitType.Star);
  356. }
  357. else
  358. {
  359. nextChildModel.DockHeight = new GridLength(nextChildModel.DockHeight.Value - delta, GridUnitType.Pixel);
  360. }
  361. //Trace.WriteLine(string.Format("NextChild To {0}", nextChildModel.DockHeight));
  362. }
  363. HideResizerOverlayWindow();
  364. }
  365. Border _resizerGhost = null;
  366. Window _resizerWindowHost = null;
  367. Vector _initialStartPoint;
  368. FrameworkElement GetNextVisibleChild(int index)
  369. {
  370. for (int i = index + 1; i < InternalChildren.Count; i++)
  371. {
  372. if (InternalChildren[i] is LayoutGridResizerControl)
  373. continue;
  374. if (Orientation == System.Windows.Controls.Orientation.Horizontal)
  375. {
  376. if (ColumnDefinitions[i].Width.IsStar || ColumnDefinitions[i].Width.Value > 0)
  377. return InternalChildren[i] as FrameworkElement;
  378. }
  379. else
  380. {
  381. if (RowDefinitions[i].Height.IsStar || RowDefinitions[i].Height.Value > 0)
  382. return InternalChildren[i] as FrameworkElement;
  383. }
  384. }
  385. return null;
  386. }
  387. void ShowResizerOverlayWindow(LayoutGridResizerControl splitter)
  388. {
  389. _resizerGhost = new Border()
  390. {
  391. Background = splitter.BackgroundWhileDragging,
  392. Opacity = splitter.OpacityWhileDragging
  393. };
  394. int indexOfResizer = InternalChildren.IndexOf(splitter);
  395. var prevChild = InternalChildren[indexOfResizer - 1] as FrameworkElement;
  396. var nextChild = GetNextVisibleChild(indexOfResizer);
  397. var prevChildActualSize = prevChild.TransformActualSizeToAncestor();
  398. var nextChildActualSize = nextChild.TransformActualSizeToAncestor();
  399. var prevChildModel = (ILayoutPositionableElement)(prevChild as ILayoutControl).Model;
  400. var nextChildModel = (ILayoutPositionableElement)(nextChild as ILayoutControl).Model;
  401. Point ptTopLeftScreen = prevChild.PointToScreenDPIWithoutFlowDirection(new Point());
  402. Size actualSize;
  403. if (Orientation == System.Windows.Controls.Orientation.Horizontal)
  404. {
  405. actualSize = new Size(
  406. prevChildActualSize.Width - prevChildModel.DockMinWidth + splitter.ActualWidth + nextChildActualSize.Width - nextChildModel.DockMinWidth,
  407. nextChildActualSize.Height);
  408. _resizerGhost.Width = splitter.ActualWidth;
  409. _resizerGhost.Height = actualSize.Height;
  410. ptTopLeftScreen.Offset(prevChildModel.DockMinWidth, 0.0);
  411. }
  412. else
  413. {
  414. actualSize = new Size(
  415. prevChildActualSize.Width,
  416. prevChildActualSize.Height - prevChildModel.DockMinHeight + splitter.ActualHeight + nextChildActualSize.Height - nextChildModel.DockMinHeight);
  417. _resizerGhost.Height = splitter.ActualHeight;
  418. _resizerGhost.Width = actualSize.Width;
  419. ptTopLeftScreen.Offset(0.0, prevChildModel.DockMinHeight);
  420. }
  421. _initialStartPoint = splitter.PointToScreenDPIWithoutFlowDirection(new Point()) - ptTopLeftScreen;
  422. if (Orientation == System.Windows.Controls.Orientation.Horizontal)
  423. {
  424. Canvas.SetLeft(_resizerGhost, _initialStartPoint.X);
  425. }
  426. else
  427. {
  428. Canvas.SetTop(_resizerGhost, _initialStartPoint.Y);
  429. }
  430. Canvas panelHostResizer = new Canvas()
  431. {
  432. HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
  433. VerticalAlignment = System.Windows.VerticalAlignment.Stretch
  434. };
  435. panelHostResizer.Children.Add(_resizerGhost);
  436. _resizerWindowHost = new Window()
  437. {
  438. SizeToContent = System.Windows.SizeToContent.Manual,
  439. ResizeMode = ResizeMode.NoResize,
  440. WindowStyle = System.Windows.WindowStyle.None,
  441. ShowInTaskbar = false,
  442. AllowsTransparency = true,
  443. Background = null,
  444. Width = actualSize.Width,
  445. Height = actualSize.Height,
  446. Left = ptTopLeftScreen.X,
  447. Top = ptTopLeftScreen.Y,
  448. ShowActivated = false,
  449. //Owner = Window.GetWindow(this),
  450. Content = panelHostResizer
  451. };
  452. _resizerWindowHost.Loaded += (s, e) =>
  453. {
  454. _resizerWindowHost.SetParentToMainWindowOf(this);
  455. };
  456. _resizerWindowHost.Show();
  457. }
  458. void HideResizerOverlayWindow()
  459. {
  460. if (_resizerWindowHost != null)
  461. {
  462. _resizerWindowHost.Close();
  463. _resizerWindowHost = null;
  464. }
  465. }
  466. #endregion
  467. }
  468. }