/************************************************************************************* Extended WPF Toolkit Copyright (C) 2007-2013 Xceed Software Inc. This program is provided to you under the terms of the Microsoft Public License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license For more features, controls, and fast professional support, pick up the Plus Edition at http://xceed.com/wpf_toolkit Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids ***********************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Markup; using System.Xml.Serialization; using System.Windows; using System.Globalization; using System.Windows.Media; using System.ComponentModel; namespace Xceed.Wpf.AvalonDock.Layout { [ContentProperty("Content")] [Serializable] public abstract class LayoutContent : LayoutElement, IXmlSerializable, ILayoutElementForFloatingWindow, IComparable, ILayoutPreviousContainer { internal LayoutContent() { } #region Title public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( "Title", typeof( string ), typeof( LayoutContent ), new UIPropertyMetadata( null, OnTitlePropertyChanged, CoerceTitleValue ) ); public string Title { get { return ( string )GetValue( TitleProperty ); } set { SetValue( TitleProperty, value ); } } private static object CoerceTitleValue( DependencyObject obj, object value ) { var lc = ( LayoutContent )obj; if( ( ( string )value ) != lc.Title ) { lc.RaisePropertyChanging( LayoutContent.TitleProperty.Name ); } return value; } private static void OnTitlePropertyChanged( DependencyObject obj, DependencyPropertyChangedEventArgs args ) { ( ( LayoutContent )obj ).RaisePropertyChanged( LayoutContent.TitleProperty.Name ); } #endregion //Title #region Content [NonSerialized] private object _content = null; [XmlIgnore] public object Content { get { return _content; } set { if (_content != value) { RaisePropertyChanging("Content"); _content = value; RaisePropertyChanged("Content"); } } } #endregion #region ContentId private string _contentId = null; public string ContentId { get { if (_contentId == null) { var contentAsControl = _content as FrameworkElement; if (contentAsControl != null && !string.IsNullOrWhiteSpace(contentAsControl.Name)) return contentAsControl.Name; } return _contentId; } set { if (_contentId != value) { _contentId = value; RaisePropertyChanged("ContentId"); } } } #endregion #region IsSelected private bool _isSelected = false; public bool IsSelected { get { return _isSelected; } set { if (_isSelected != value) { bool oldValue = _isSelected; RaisePropertyChanging("IsSelected"); _isSelected = value; var parentSelector = (Parent as ILayoutContentSelector); if (parentSelector != null) parentSelector.SelectedContentIndex = _isSelected ? parentSelector.IndexOf(this) : -1; OnIsSelectedChanged(oldValue, value); RaisePropertyChanged("IsSelected"); } } } /// /// Provides derived classes an opportunity to handle changes to the IsSelected property. /// protected virtual void OnIsSelectedChanged(bool oldValue, bool newValue) { if (IsSelectedChanged != null) IsSelectedChanged(this, EventArgs.Empty); } public event EventHandler IsSelectedChanged; #endregion #region IsActive [field: NonSerialized] private bool _isActive = false; [XmlIgnore] public bool IsActive { get { return _isActive; } set { if (_isActive != value) { RaisePropertyChanging("IsActive"); bool oldValue = _isActive; _isActive = value; var root = Root; if (root != null && _isActive) root.ActiveContent = this; if (_isActive) IsSelected = true; OnIsActiveChanged(oldValue, value); RaisePropertyChanged("IsActive"); } } } /// /// Provides derived classes an opportunity to handle changes to the IsActive property. /// protected virtual void OnIsActiveChanged(bool oldValue, bool newValue) { if (newValue) LastActivationTimeStamp = DateTime.Now; if (IsActiveChanged != null) IsActiveChanged(this, EventArgs.Empty); } public event EventHandler IsActiveChanged; #endregion #region IsLastFocusedDocument private bool _isLastFocusedDocument = false; public bool IsLastFocusedDocument { get { return _isLastFocusedDocument; } internal set { if (_isLastFocusedDocument != value) { RaisePropertyChanging("IsLastFocusedDocument"); _isLastFocusedDocument = value; RaisePropertyChanged("IsLastFocusedDocument"); } } } #endregion #region PreviousContainer [field: NonSerialized] private ILayoutContainer _previousContainer = null; [XmlIgnore] ILayoutContainer ILayoutPreviousContainer.PreviousContainer { get { return _previousContainer; } set { if (_previousContainer != value) { _previousContainer = value; RaisePropertyChanged("PreviousContainer"); var paneSerializable = _previousContainer as ILayoutPaneSerializable; if (paneSerializable != null && paneSerializable.Id == null) paneSerializable.Id = Guid.NewGuid().ToString(); } } } protected ILayoutContainer PreviousContainer { get { return ((ILayoutPreviousContainer)this).PreviousContainer; } set { ((ILayoutPreviousContainer)this).PreviousContainer = value; } } [XmlIgnore] string ILayoutPreviousContainer.PreviousContainerId { get; set; } protected string PreviousContainerId { get { return ((ILayoutPreviousContainer)this).PreviousContainerId; } set { ((ILayoutPreviousContainer)this).PreviousContainerId = value; } } #endregion #region PreviousContainerIndex [field: NonSerialized] private int _previousContainerIndex = -1; [XmlIgnore] public int PreviousContainerIndex { get { return _previousContainerIndex; } set { if (_previousContainerIndex != value) { _previousContainerIndex = value; RaisePropertyChanged("PreviousContainerIndex"); } } } #endregion #region LastActivationTimeStamp private DateTime? _lastActivationTimeStamp = null; public DateTime? LastActivationTimeStamp { get { return _lastActivationTimeStamp; } set { if (_lastActivationTimeStamp != value) { _lastActivationTimeStamp = value; RaisePropertyChanged("LastActivationTimeStamp"); } } } #endregion protected override void OnParentChanging(ILayoutContainer oldValue, ILayoutContainer newValue) { var root = Root; if (oldValue != null) IsSelected = false; //if (root != null && _isActive && newValue == null) // root.ActiveContent = null; base.OnParentChanging(oldValue, newValue); } protected override void OnParentChanged(ILayoutContainer oldValue, ILayoutContainer newValue) { if (IsSelected && Parent != null && Parent is ILayoutContentSelector) { var parentSelector = (Parent as ILayoutContentSelector); parentSelector.SelectedContentIndex = parentSelector.IndexOf(this); } //var root = Root; //if (root != null && _isActive) // root.ActiveContent = this; base.OnParentChanged(oldValue, newValue); } /// /// Test if the content can be closed /// /// internal bool TestCanClose() { CancelEventArgs args = new CancelEventArgs(); OnClosing(args); if (args.Cancel) return false; return true; } internal void CloseInternal() { var root = Root; var parentAsContainer = Parent as ILayoutContainer; parentAsContainer.RemoveChild( this ); if( root != null ) root.CollectGarbage(); OnClosed(); } /// /// Close the content /// /// Please note that usually the anchorable is only hidden (not closed). By default when user click the X button it only hides the content. public abstract void Close(); /// /// Event fired when the content is closed (i.e. removed definitely from the layout) /// public event EventHandler Closed; protected virtual void OnClosed() { if (Closed != null) Closed(this, EventArgs.Empty); } /// /// Event fired when the content is about to be closed (i.e. removed definitely from the layout) /// /// Please note that LayoutAnchorable also can be hidden. Usually user hide anchorables when click the 'X' button. To completely close /// an anchorable the user should click the 'Close' menu item from the context menu. When an LayoutAnchorable is hidden its visibility changes to false and /// IsHidden property is set to true. /// Hanlde the Hiding event for the LayoutAnchorable to cancel the hide operation. public event EventHandler Closing; protected virtual void OnClosing(CancelEventArgs args) { if (Closing != null) Closing(this, args); } public System.Xml.Schema.XmlSchema GetSchema() { return null; } public virtual void ReadXml(System.Xml.XmlReader reader) { if (reader.MoveToAttribute("Title")) Title = reader.Value; //if (reader.MoveToAttribute("IconSource")) // IconSource = new Uri(reader.Value, UriKind.RelativeOrAbsolute); if (reader.MoveToAttribute("IsSelected")) IsSelected = bool.Parse(reader.Value); if (reader.MoveToAttribute("ContentId")) ContentId = reader.Value; if (reader.MoveToAttribute("IsLastFocusedDocument")) IsLastFocusedDocument = bool.Parse(reader.Value); if (reader.MoveToAttribute("PreviousContainerId")) PreviousContainerId = reader.Value; if (reader.MoveToAttribute("PreviousContainerIndex")) PreviousContainerIndex = int.Parse(reader.Value); if (reader.MoveToAttribute("FloatingLeft")) FloatingLeft = double.Parse(reader.Value, CultureInfo.InvariantCulture); if (reader.MoveToAttribute("FloatingTop")) FloatingTop = double.Parse(reader.Value, CultureInfo.InvariantCulture); if (reader.MoveToAttribute("FloatingWidth")) FloatingWidth = double.Parse(reader.Value, CultureInfo.InvariantCulture); if (reader.MoveToAttribute("FloatingHeight")) FloatingHeight = double.Parse(reader.Value, CultureInfo.InvariantCulture); if (reader.MoveToAttribute("IsMaximized")) IsMaximized = bool.Parse(reader.Value); if (reader.MoveToAttribute("CanClose")) CanClose = bool.Parse(reader.Value); if (reader.MoveToAttribute("CanFloat")) CanFloat = bool.Parse(reader.Value); if (reader.MoveToAttribute("LastActivationTimeStamp")) LastActivationTimeStamp = DateTime.Parse(reader.Value, CultureInfo.InvariantCulture); reader.Read(); } public virtual void WriteXml(System.Xml.XmlWriter writer) { if (!string.IsNullOrWhiteSpace(Title)) writer.WriteAttributeString("Title", Title); //if (IconSource != null) // writer.WriteAttributeString("IconSource", IconSource.ToString()); if (IsSelected) writer.WriteAttributeString("IsSelected", IsSelected.ToString()); if (IsLastFocusedDocument) writer.WriteAttributeString("IsLastFocusedDocument", IsLastFocusedDocument.ToString()); if (!string.IsNullOrWhiteSpace(ContentId)) writer.WriteAttributeString("ContentId", ContentId); if (ToolTip != null && ToolTip is string) if (!string.IsNullOrWhiteSpace((string)ToolTip)) writer.WriteAttributeString("ToolTip", (string)ToolTip); if (FloatingLeft != 0.0) writer.WriteAttributeString("FloatingLeft", FloatingLeft.ToString(CultureInfo.InvariantCulture)); if (FloatingTop != 0.0) writer.WriteAttributeString("FloatingTop", FloatingTop.ToString(CultureInfo.InvariantCulture)); if (FloatingWidth != 0.0) writer.WriteAttributeString("FloatingWidth", FloatingWidth.ToString(CultureInfo.InvariantCulture)); if (FloatingHeight != 0.0) writer.WriteAttributeString("FloatingHeight", FloatingHeight.ToString(CultureInfo.InvariantCulture)); if (IsMaximized) writer.WriteAttributeString("IsMaximized", IsMaximized.ToString()); if (!CanClose) writer.WriteAttributeString("CanClose", CanClose.ToString()); if (!CanFloat) writer.WriteAttributeString("CanFloat", CanFloat.ToString()); if (LastActivationTimeStamp != null) writer.WriteAttributeString("LastActivationTimeStamp", LastActivationTimeStamp.Value.ToString(CultureInfo.InvariantCulture)); if (_previousContainer != null) { var paneSerializable = _previousContainer as ILayoutPaneSerializable; if (paneSerializable != null) { writer.WriteAttributeString("PreviousContainerId", paneSerializable.Id); writer.WriteAttributeString("PreviousContainerIndex", _previousContainerIndex.ToString()); } } } #region FloatingWidth private double _floatingWidth = 0.0; public double FloatingWidth { get { return _floatingWidth; } set { if (_floatingWidth != value) { RaisePropertyChanging("FloatingWidth"); _floatingWidth = value; RaisePropertyChanged("FloatingWidth"); } } } #endregion #region FloatingHeight private double _floatingHeight = 0.0; public double FloatingHeight { get { return _floatingHeight; } set { if (_floatingHeight != value) { RaisePropertyChanging("FloatingHeight"); _floatingHeight = value; RaisePropertyChanged("FloatingHeight"); } } } #endregion #region FloatingLeft private double _floatingLeft = 0.0; public double FloatingLeft { get { return _floatingLeft; } set { if (_floatingLeft != value) { RaisePropertyChanging("FloatingLeft"); _floatingLeft = value; RaisePropertyChanged("FloatingLeft"); } } } #endregion #region FloatingTop private double _floatingTop = 0.0; public double FloatingTop { get { return _floatingTop; } set { if (_floatingTop != value) { RaisePropertyChanging("FloatingTop"); _floatingTop = value; RaisePropertyChanged("FloatingTop"); } } } #endregion #region IsMaximized private bool _isMaximized = false; public bool IsMaximized { get { return _isMaximized; } set { if (_isMaximized != value) { RaisePropertyChanging("IsMaximized"); _isMaximized = value; RaisePropertyChanged("IsMaximized"); } } } #endregion #region ToolTip private object _toolTip = null; public object ToolTip { get { return _toolTip; } set { if (_toolTip != value) { _toolTip = value; RaisePropertyChanged("ToolTip"); } } } #endregion public bool IsFloating { get { return this.FindParent() != null; } } #region IconSource private ImageSource _iconSource = null; public ImageSource IconSource { get { return _iconSource; } set { if (_iconSource != value) { _iconSource = value; RaisePropertyChanged("IconSource"); } } } #endregion public int CompareTo(LayoutContent other) { var contentAsComparable = Content as IComparable; if (contentAsComparable != null) { return contentAsComparable.CompareTo(other.Content); } return string.Compare(Title, other.Title); } /// /// Float the content in a popup window /// public void Float() { if (PreviousContainer != null && PreviousContainer.FindParent() != null) { var currentContainer = Parent as ILayoutPane; var currentContainerIndex = (currentContainer as ILayoutGroup).IndexOfChild(this); var previousContainerAsLayoutGroup = PreviousContainer as ILayoutGroup; if (PreviousContainerIndex < previousContainerAsLayoutGroup.ChildrenCount) previousContainerAsLayoutGroup.InsertChildAt(PreviousContainerIndex, this); else previousContainerAsLayoutGroup.InsertChildAt(previousContainerAsLayoutGroup.ChildrenCount, this); PreviousContainer = currentContainer; PreviousContainerIndex = currentContainerIndex; IsSelected = true; IsActive = true; Root.CollectGarbage(); } else { Root.Manager.StartDraggingFloatingWindowForContent(this, false); IsSelected = true; IsActive = true; } } /// /// Dock the content as document /// public void DockAsDocument() { var root = Root as LayoutRoot; if (root == null) throw new InvalidOperationException(); if (Parent is LayoutDocumentPane) return; if (PreviousContainer is LayoutDocumentPane) { Dock(); return; } LayoutDocumentPane newParentPane; if (root.LastFocusedDocument != null) { newParentPane = root.LastFocusedDocument.Parent as LayoutDocumentPane; } else { newParentPane = root.Descendents().OfType().FirstOrDefault(); } if (newParentPane != null) { newParentPane.Children.Add(this); root.CollectGarbage(); } IsSelected = true; IsActive = true; } /// /// Re-dock the content to its previous container /// public void Dock() { if (PreviousContainer != null) { var currentContainer = Parent as ILayoutContainer; var currentContainerIndex = (currentContainer is ILayoutGroup) ? (currentContainer as ILayoutGroup).IndexOfChild(this) : -1; var previousContainerAsLayoutGroup = PreviousContainer as ILayoutGroup; if (PreviousContainerIndex < previousContainerAsLayoutGroup.ChildrenCount) previousContainerAsLayoutGroup.InsertChildAt(PreviousContainerIndex, this); else previousContainerAsLayoutGroup.InsertChildAt(previousContainerAsLayoutGroup.ChildrenCount, this); if (currentContainerIndex > -1) { PreviousContainer = currentContainer; PreviousContainerIndex = currentContainerIndex; } else { PreviousContainer = null; PreviousContainerIndex = 0; } IsSelected = true; IsActive = true; } else { InternalDock(); } Root.CollectGarbage(); } protected virtual void InternalDock() { } #region CanClose private bool _canClose = true; public bool CanClose { get { return _canClose; } set { if (_canClose != value) { _canClose = value; RaisePropertyChanged("CanClose"); } } } #endregion #region CanFloat private bool _canFloat = true; public bool CanFloat { get { return _canFloat; } set { if (_canFloat != value) { _canFloat = value; RaisePropertyChanged("CanFloat"); } } } #endregion } }