LayoutRoot.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  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;
  15. using System.Collections.ObjectModel;
  16. using System.Windows.Markup;
  17. using System.Xml.Serialization;
  18. using Standard;
  19. namespace Xceed.Wpf.AvalonDock.Layout
  20. {
  21. [ContentProperty("RootPanel")]
  22. [Serializable]
  23. public class LayoutRoot : LayoutElement, ILayoutContainer, ILayoutRoot
  24. {
  25. public LayoutRoot()
  26. {
  27. RightSide = new LayoutAnchorSide();
  28. LeftSide = new LayoutAnchorSide();
  29. TopSide = new LayoutAnchorSide();
  30. BottomSide = new LayoutAnchorSide();
  31. RootPanel = new LayoutPanel(new LayoutDocumentPane());
  32. }
  33. #region RootPanel
  34. private LayoutPanel _rootPanel;
  35. public LayoutPanel RootPanel
  36. {
  37. get { return _rootPanel; }
  38. set
  39. {
  40. if (_rootPanel != value)
  41. {
  42. RaisePropertyChanging("RootPanel");
  43. if (_rootPanel != null &&
  44. _rootPanel.Parent == this)
  45. _rootPanel.Parent = null;
  46. _rootPanel = value;
  47. if (_rootPanel == null)
  48. _rootPanel = new LayoutPanel(new LayoutDocumentPane());
  49. if (_rootPanel != null)
  50. _rootPanel.Parent = this;
  51. RaisePropertyChanged("RootPanel");
  52. }
  53. }
  54. }
  55. #endregion
  56. #region TopSide
  57. private LayoutAnchorSide _topSide = null;
  58. public LayoutAnchorSide TopSide
  59. {
  60. get { return _topSide; }
  61. set
  62. {
  63. if (_topSide != value)
  64. {
  65. RaisePropertyChanging("TopSide");
  66. _topSide = value;
  67. if (_topSide != null)
  68. _topSide.Parent = this;
  69. RaisePropertyChanged("TopSide");
  70. }
  71. }
  72. }
  73. #endregion
  74. #region RightSide
  75. private LayoutAnchorSide _rightSide;
  76. public LayoutAnchorSide RightSide
  77. {
  78. get { return _rightSide; }
  79. set
  80. {
  81. if (_rightSide != value)
  82. {
  83. RaisePropertyChanging("RightSide");
  84. _rightSide = value;
  85. if (_rightSide != null)
  86. _rightSide.Parent = this;
  87. RaisePropertyChanged("RightSide");
  88. }
  89. }
  90. }
  91. #endregion
  92. #region LeftSide
  93. private LayoutAnchorSide _leftSide = null;
  94. public LayoutAnchorSide LeftSide
  95. {
  96. get { return _leftSide; }
  97. set
  98. {
  99. if (_leftSide != value)
  100. {
  101. RaisePropertyChanging("LeftSide");
  102. _leftSide = value;
  103. if (_leftSide != null)
  104. _leftSide.Parent = this;
  105. RaisePropertyChanged("LeftSide");
  106. }
  107. }
  108. }
  109. #endregion
  110. #region BottomSide
  111. private LayoutAnchorSide _bottomSide = null;
  112. public LayoutAnchorSide BottomSide
  113. {
  114. get { return _bottomSide; }
  115. set
  116. {
  117. if (_bottomSide != value)
  118. {
  119. RaisePropertyChanging("BottomSide");
  120. _bottomSide = value;
  121. if (_bottomSide != null)
  122. _bottomSide.Parent = this;
  123. RaisePropertyChanged("BottomSide");
  124. }
  125. }
  126. }
  127. #endregion
  128. #region FloatingWindows
  129. ObservableCollection<LayoutFloatingWindow> _floatingWindows = null;
  130. public ObservableCollection<LayoutFloatingWindow> FloatingWindows
  131. {
  132. get
  133. {
  134. if (_floatingWindows == null)
  135. {
  136. _floatingWindows = new ObservableCollection<LayoutFloatingWindow>();
  137. _floatingWindows.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_floatingWindows_CollectionChanged);
  138. }
  139. return _floatingWindows;
  140. }
  141. }
  142. void _floatingWindows_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
  143. {
  144. if (e.OldItems != null && (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove ||
  145. e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Replace))
  146. {
  147. foreach (LayoutFloatingWindow element in e.OldItems)
  148. {
  149. if (element.Parent == this)
  150. element.Parent = null;
  151. }
  152. }
  153. if (e.NewItems != null && (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add ||
  154. e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Replace))
  155. {
  156. foreach (LayoutFloatingWindow element in e.NewItems)
  157. element.Parent = this;
  158. }
  159. }
  160. #endregion
  161. #region HiddenAnchorables
  162. ObservableCollection<LayoutAnchorable> _hiddenAnchorables = null;
  163. public ObservableCollection<LayoutAnchorable> Hidden
  164. {
  165. get
  166. {
  167. if (_hiddenAnchorables == null)
  168. {
  169. _hiddenAnchorables = new ObservableCollection<LayoutAnchorable>();
  170. _hiddenAnchorables.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_hiddenAnchorables_CollectionChanged);
  171. }
  172. return _hiddenAnchorables;
  173. }
  174. }
  175. void _hiddenAnchorables_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
  176. {
  177. if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove ||
  178. e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Replace)
  179. {
  180. if (e.OldItems != null)
  181. {
  182. foreach (LayoutAnchorable element in e.OldItems)
  183. {
  184. if (element.Parent == this)
  185. element.Parent = null;
  186. }
  187. }
  188. }
  189. if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add ||
  190. e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Replace)
  191. {
  192. if (e.NewItems != null)
  193. {
  194. foreach (LayoutAnchorable element in e.NewItems)
  195. {
  196. if (element.Parent != this)
  197. {
  198. if (element.Parent != null)
  199. element.Parent.RemoveChild(element);
  200. element.Parent = this;
  201. }
  202. }
  203. }
  204. }
  205. }
  206. #endregion
  207. #region Children
  208. public IEnumerable<ILayoutElement> Children
  209. {
  210. get
  211. {
  212. if (RootPanel != null)
  213. yield return RootPanel;
  214. if (_floatingWindows != null)
  215. {
  216. foreach (var floatingWindow in _floatingWindows)
  217. yield return floatingWindow;
  218. }
  219. if (TopSide != null)
  220. yield return TopSide;
  221. if (RightSide != null)
  222. yield return RightSide;
  223. if (BottomSide != null)
  224. yield return BottomSide;
  225. if (LeftSide != null)
  226. yield return LeftSide;
  227. if (_hiddenAnchorables != null)
  228. {
  229. foreach (var hiddenAnchorable in _hiddenAnchorables)
  230. yield return hiddenAnchorable;
  231. }
  232. }
  233. }
  234. public void RemoveChild(ILayoutElement element)
  235. {
  236. if (element == RootPanel)
  237. RootPanel = null;
  238. else if (_floatingWindows != null && _floatingWindows.Contains(element))
  239. _floatingWindows.Remove(element as LayoutFloatingWindow);
  240. else if (_hiddenAnchorables != null && _hiddenAnchorables.Contains(element))
  241. _hiddenAnchorables.Remove(element as LayoutAnchorable);
  242. else if (element == TopSide)
  243. TopSide = null;
  244. else if (element == RightSide)
  245. RightSide = null;
  246. else if (element == BottomSide)
  247. BottomSide = null;
  248. else if (element == LeftSide)
  249. LeftSide = null;
  250. }
  251. public void ReplaceChild(ILayoutElement oldElement, ILayoutElement newElement)
  252. {
  253. if (oldElement == RootPanel)
  254. RootPanel = (LayoutPanel)newElement;
  255. else if (_floatingWindows != null && _floatingWindows.Contains(oldElement))
  256. {
  257. int index = _floatingWindows.IndexOf(oldElement as LayoutFloatingWindow);
  258. _floatingWindows.Remove(oldElement as LayoutFloatingWindow);
  259. _floatingWindows.Insert(index, newElement as LayoutFloatingWindow);
  260. }
  261. else if (_hiddenAnchorables != null && _hiddenAnchorables.Contains(oldElement))
  262. {
  263. int index = _hiddenAnchorables.IndexOf(oldElement as LayoutAnchorable);
  264. _hiddenAnchorables.Remove(oldElement as LayoutAnchorable);
  265. _hiddenAnchorables.Insert(index, newElement as LayoutAnchorable);
  266. }
  267. else if (oldElement == TopSide)
  268. TopSide = (LayoutAnchorSide)newElement;
  269. else if (oldElement == RightSide)
  270. RightSide = (LayoutAnchorSide)newElement;
  271. else if (oldElement == BottomSide)
  272. BottomSide = (LayoutAnchorSide)newElement;
  273. else if (oldElement == LeftSide)
  274. LeftSide = (LayoutAnchorSide)newElement;
  275. }
  276. public int ChildrenCount
  277. {
  278. get
  279. {
  280. return 5 +
  281. (_floatingWindows != null ? _floatingWindows.Count : 0) +
  282. (_hiddenAnchorables != null ? _hiddenAnchorables.Count : 0);
  283. }
  284. }
  285. #endregion
  286. #region ActiveContent
  287. [field:NonSerialized]
  288. private WeakReference _activeContent = null;
  289. private bool _activeContentSet = false;
  290. [XmlIgnore]
  291. public LayoutContent ActiveContent
  292. {
  293. get { return _activeContent.GetValueOrDefault<LayoutContent>(); }
  294. set
  295. {
  296. var currentValue = ActiveContent;
  297. if (currentValue != value)
  298. {
  299. InternalSetActiveContent(currentValue, value);
  300. }
  301. }
  302. }
  303. void InternalSetActiveContent(LayoutContent currentValue, LayoutContent newActiveContent)
  304. {
  305. RaisePropertyChanging("ActiveContent");
  306. if (currentValue != null)
  307. currentValue.IsActive = false;
  308. _activeContent = new WeakReference(newActiveContent);
  309. currentValue = ActiveContent;
  310. if (currentValue != null)
  311. currentValue.IsActive = true;
  312. RaisePropertyChanged("ActiveContent");
  313. _activeContentSet = currentValue != null;
  314. if (currentValue != null)
  315. {
  316. if (currentValue.Parent is LayoutDocumentPane || currentValue is LayoutDocument)
  317. LastFocusedDocument = currentValue;
  318. }
  319. else
  320. LastFocusedDocument = null;
  321. }
  322. void UpdateActiveContentProperty()
  323. {
  324. var activeContent = ActiveContent;
  325. if (_activeContentSet && (activeContent == null || activeContent.Root != this))
  326. {
  327. _activeContentSet = false;
  328. InternalSetActiveContent(activeContent, null);
  329. }
  330. }
  331. #endregion
  332. #region LastFocusedDocument
  333. [field: NonSerialized]
  334. private WeakReference _lastFocusedDocument = null;
  335. [field: NonSerialized]
  336. private bool _lastFocusedDocumentSet = false;
  337. [XmlIgnore]
  338. public LayoutContent LastFocusedDocument
  339. {
  340. get { return _lastFocusedDocument.GetValueOrDefault<LayoutContent>(); }
  341. private set
  342. {
  343. var currentValue = LastFocusedDocument;
  344. if (currentValue != value)
  345. {
  346. RaisePropertyChanging("LastFocusedDocument");
  347. if (currentValue != null)
  348. currentValue.IsLastFocusedDocument = false;
  349. _lastFocusedDocument = new WeakReference(value);
  350. currentValue = LastFocusedDocument;
  351. if (currentValue != null)
  352. currentValue.IsLastFocusedDocument = true;
  353. _lastFocusedDocumentSet = currentValue != null;
  354. RaisePropertyChanged("LastFocusedDocument");
  355. }
  356. }
  357. }
  358. #endregion
  359. #region Manager
  360. [NonSerialized]
  361. private DockingManager _manager = null;
  362. [XmlIgnore]
  363. public DockingManager Manager
  364. {
  365. get { return _manager; }
  366. internal set
  367. {
  368. if (_manager != value)
  369. {
  370. RaisePropertyChanging("Manager");
  371. _manager = value;
  372. RaisePropertyChanged("Manager");
  373. }
  374. }
  375. }
  376. #endregion
  377. #region CollectGarbage
  378. /// <summary>
  379. /// Removes any empty container not directly referenced by other layout items
  380. /// </summary>
  381. public void CollectGarbage()
  382. {
  383. bool exitFlag = true;
  384. #region collect empty panes
  385. do
  386. {
  387. exitFlag = true;
  388. //for each content that references via PreviousContainer a disconnected Pane set the property to null
  389. foreach (var content in this.Descendents().OfType<ILayoutPreviousContainer>().Where(c => c.PreviousContainer != null &&
  390. (c.PreviousContainer.Parent == null || c.PreviousContainer.Parent.Root != this)))
  391. {
  392. content.PreviousContainer = null;
  393. }
  394. //for each pane that is empty
  395. foreach (var emptyPane in this.Descendents().OfType<ILayoutPane>().Where(p => p.ChildrenCount == 0))
  396. {
  397. //...set null any reference coming from contents not yet hosted in a floating window
  398. foreach (var contentReferencingEmptyPane in this.Descendents().OfType<LayoutContent>()
  399. .Where(c => ((ILayoutPreviousContainer)c).PreviousContainer == emptyPane && !c.IsFloating))
  400. {
  401. if (contentReferencingEmptyPane is LayoutAnchorable &&
  402. !((LayoutAnchorable)contentReferencingEmptyPane).IsVisible)
  403. continue;
  404. ((ILayoutPreviousContainer)contentReferencingEmptyPane).PreviousContainer = null;
  405. contentReferencingEmptyPane.PreviousContainerIndex = -1;
  406. }
  407. //...if this pane is the only documentpane present in the layout than skip it
  408. if (emptyPane is LayoutDocumentPane &&
  409. this.Descendents().OfType<LayoutDocumentPane>().Count(c => c != emptyPane) == 0)
  410. continue;
  411. //...if this empty panes is not referenced by anyone, than removes it from its parent container
  412. if (!this.Descendents().OfType<ILayoutPreviousContainer>().Any(c => c.PreviousContainer == emptyPane))
  413. {
  414. var parentGroup = emptyPane.Parent as ILayoutContainer;
  415. parentGroup.RemoveChild(emptyPane);
  416. exitFlag = false;
  417. break;
  418. }
  419. }
  420. if (!exitFlag)
  421. {
  422. //removes any empty anchorable pane group
  423. foreach (var emptyPaneGroup in this.Descendents().OfType<LayoutAnchorablePaneGroup>().Where(p => p.ChildrenCount == 0))
  424. {
  425. var parentGroup = emptyPaneGroup.Parent as ILayoutContainer;
  426. parentGroup.RemoveChild(emptyPaneGroup);
  427. exitFlag = false;
  428. break;
  429. }
  430. }
  431. if (!exitFlag)
  432. {
  433. //removes any empty layout panel
  434. foreach (var emptyPaneGroup in this.Descendents().OfType<LayoutPanel>().Where(p => p.ChildrenCount == 0))
  435. {
  436. var parentGroup = emptyPaneGroup.Parent as ILayoutContainer;
  437. parentGroup.RemoveChild(emptyPaneGroup);
  438. exitFlag = false;
  439. break;
  440. }
  441. }
  442. if (!exitFlag)
  443. {
  444. //removes any empty floating window
  445. foreach (var emptyPaneGroup in this.Descendents().OfType<LayoutFloatingWindow>().Where(p => p.ChildrenCount == 0))
  446. {
  447. var parentGroup = emptyPaneGroup.Parent as ILayoutContainer;
  448. parentGroup.RemoveChild(emptyPaneGroup);
  449. exitFlag = false;
  450. break;
  451. }
  452. }
  453. if (!exitFlag)
  454. {
  455. //removes any empty anchor group
  456. foreach (var emptyPaneGroup in this.Descendents().OfType<LayoutAnchorGroup>().Where(p => p.ChildrenCount == 0))
  457. {
  458. var parentGroup = emptyPaneGroup.Parent as ILayoutContainer;
  459. parentGroup.RemoveChild(emptyPaneGroup);
  460. exitFlag = false;
  461. break;
  462. }
  463. }
  464. }
  465. while (!exitFlag);
  466. #endregion
  467. #region collapse single child anchorable pane groups
  468. do
  469. {
  470. exitFlag = true;
  471. //for each pane that is empty
  472. foreach (var paneGroupToCollapse in this.Descendents().OfType<LayoutAnchorablePaneGroup>().Where(p => p.ChildrenCount == 1 && p.Children[0] is LayoutAnchorablePaneGroup).ToArray())
  473. {
  474. var singleChild = paneGroupToCollapse.Children[0] as LayoutAnchorablePaneGroup;
  475. paneGroupToCollapse.Orientation = singleChild.Orientation;
  476. paneGroupToCollapse.RemoveChild(singleChild);
  477. while (singleChild.ChildrenCount > 0)
  478. {
  479. paneGroupToCollapse.InsertChildAt(
  480. paneGroupToCollapse.ChildrenCount, singleChild.Children[0]);
  481. }
  482. exitFlag = false;
  483. break;
  484. }
  485. }
  486. while (!exitFlag);
  487. #endregion
  488. #region collapse single child document pane groups
  489. do
  490. {
  491. exitFlag = true;
  492. //for each pane that is empty
  493. foreach (var paneGroupToCollapse in this.Descendents().OfType<LayoutDocumentPaneGroup>().Where(p => p.ChildrenCount == 1 && p.Children[0] is LayoutDocumentPaneGroup).ToArray())
  494. {
  495. var singleChild = paneGroupToCollapse.Children[0] as LayoutDocumentPaneGroup;
  496. paneGroupToCollapse.Orientation = singleChild.Orientation;
  497. paneGroupToCollapse.RemoveChild(singleChild);
  498. while (singleChild.ChildrenCount > 0)
  499. {
  500. paneGroupToCollapse.InsertChildAt(
  501. paneGroupToCollapse.ChildrenCount, singleChild.Children[0]);
  502. }
  503. exitFlag = false;
  504. break;
  505. }
  506. }
  507. while (!exitFlag);
  508. #endregion
  509. #region collapse single child layout panels
  510. do
  511. {
  512. exitFlag = true;
  513. //for each panel that has only one child
  514. foreach (var panelToCollapse in this.Descendents().OfType<LayoutPanel>().Where(p => p.ChildrenCount == 1 && p.Children[0] is LayoutPanel).ToArray())
  515. {
  516. var singleChild = panelToCollapse.Children[0] as LayoutPanel;
  517. panelToCollapse.Orientation = singleChild.Orientation;
  518. panelToCollapse.RemoveChild(singleChild);
  519. while (singleChild.ChildrenCount > 0)
  520. {
  521. panelToCollapse.InsertChildAt(
  522. panelToCollapse.ChildrenCount, singleChild.Children[0]);
  523. }
  524. exitFlag = false;
  525. break;
  526. }
  527. }
  528. while (!exitFlag);
  529. #endregion
  530. #region Update ActiveContent and LastFocusedDocument properties
  531. UpdateActiveContentProperty();
  532. #endregion
  533. #if DEBUG
  534. System.Diagnostics.Debug.Assert(
  535. !this.Descendents().OfType<LayoutAnchorablePane>().Any(a => a.ChildrenCount == 0 && a.IsVisible));
  536. #if TRACE
  537. RootPanel.ConsoleDump(4);
  538. #endif
  539. #endif
  540. }
  541. #endregion
  542. public event EventHandler Updated;
  543. internal void FireLayoutUpdated()
  544. {
  545. if (Updated != null)
  546. Updated(this, EventArgs.Empty);
  547. }
  548. #region LayoutElement Added/Removed events
  549. internal void OnLayoutElementAdded(LayoutElement element)
  550. {
  551. if (ElementAdded != null)
  552. ElementAdded(this, new LayoutElementEventArgs(element));
  553. }
  554. public event EventHandler<LayoutElementEventArgs> ElementAdded;
  555. internal void OnLayoutElementRemoved(LayoutElement element)
  556. {
  557. if (element.Descendents().OfType<LayoutContent>().Any(c => c == LastFocusedDocument))
  558. LastFocusedDocument = null;
  559. if (element.Descendents().OfType<LayoutContent>().Any(c => c == ActiveContent))
  560. ActiveContent = null;
  561. if (ElementRemoved != null)
  562. ElementRemoved(this, new LayoutElementEventArgs(element));
  563. }
  564. public event EventHandler<LayoutElementEventArgs> ElementRemoved;
  565. #endregion
  566. #if TRACE
  567. public override void ConsoleDump(int tab)
  568. {
  569. System.Diagnostics.Trace.Write( new string( ' ', tab * 4 ) );
  570. System.Diagnostics.Trace.WriteLine( "RootPanel()" );
  571. RootPanel.ConsoleDump(tab + 1);
  572. System.Diagnostics.Trace.Write( new string( ' ', tab * 4 ) );
  573. System.Diagnostics.Trace.WriteLine( "FloatingWindows()" );
  574. foreach (LayoutFloatingWindow fw in FloatingWindows)
  575. fw.ConsoleDump(tab + 1);
  576. System.Diagnostics.Trace.Write( new string( ' ', tab * 4 ) );
  577. System.Diagnostics.Trace.WriteLine( "Hidden()" );
  578. foreach (LayoutAnchorable hidden in Hidden)
  579. hidden.ConsoleDump(tab + 1);
  580. }
  581. #endif
  582. }
  583. }