//Copyright (c) 2007-2009, Adolfo Marinucci //All rights reserved. //Redistribution and use in source and binary forms, with or without modification, //are permitted provided that the following conditions are met: // //* Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. //* Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. //* Neither the name of Adolfo Marinucci nor the names of its contributors may // be used to endorse or promote products derived from this software without // specific prior written permission. // //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" //AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED //WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. //IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, //PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) //HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, //OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, //EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using System.Diagnostics; using System.Xml; namespace AvalonDock { /// /// Enumerates all the possible states of /// public enum DockableContentState { /// /// Content is docked to a border of a within as control /// Docked, /// /// Content is hosted by a flyout window and is visible only when user move mouse over an anchor thumb located to a controlo border /// AutoHide, /// /// Content is hosted by a floating window and user can redock is on its container control /// DockableWindow, /// /// Content is hosted by a floating window that can't be docked to a its container control /// FloatingWindow, /// /// Content is hosted into a /// Document, /// /// Content is hidden /// Hidden, } /// /// Defines how a dockable content can be dragged over a docking manager /// /// This style can be composed with the 'or' operator. public enum DockableStyle : uint { /// /// Content is not dockable at all /// None = 0x0000, /// /// Dockable as document /// Document = 0x0001, /// /// Dockable to the left border of /// LeftBorder = 0x0002, /// /// Dockable to the right border of /// RightBorder = 0x0004, /// /// Dockable to the top border of /// TopBorder = 0x0008, /// /// Dockable to the bottom border of /// BottomBorder= 0x0010, /// /// A with this style can be hosted in a /// Floating = 0x0020, /// /// A with this style can be the only one content in a pane (NOT YET SUPPORTED) /// /// This style is not compatible with style Single = 0x0040, /// /// A with this style can be autohidden. /// AutoHide = 0x0080, /// /// Dockable only to a border of a /// DockableToBorders = LeftBorder | RightBorder | TopBorder | BottomBorder | AutoHide, /// /// Dockable to a border of a and into a /// Dockable = DockableToBorders | Document | Floating, /// /// Dockable to a border of a and into a but not in autohidden mode (WinForms controls) /// DockableButNotAutoHidden = Dockable & ~AutoHide } /// /// Represent a state of a dockable content that can be used to restore it after it's hidden /// internal class DockableContentStateAndPosition { public readonly Pane ContainerPane = null; public readonly int ChildIndex = -1; public readonly double Width; public readonly double Height; public readonly AnchorStyle Anchor = AnchorStyle.None; public DockableContentStateAndPosition( Pane containerPane, int childIndex, double width, double height, AnchorStyle anchor) { ContainerPane = containerPane; ChildIndex = childIndex; Width = width; Height = height; Anchor = anchor; } public DockableContentStateAndPosition( DockableContent cntToSave) { ContainerPane = cntToSave.ContainerPane; ChildIndex = ContainerPane.Items.IndexOf(cntToSave); Width = ContainerPane.ActualWidth; Height = ContainerPane.ActualHeight; DockablePane dockablePane = ContainerPane as DockablePane; if (dockablePane != null) { Anchor = dockablePane.Anchor; } } } /// /// Identifies a content that can be drag over a control or hosted by a window floating over it (). /// public class DockableContent : ManagedContent { static DockableContent() { DefaultStyleKeyProperty.OverrideMetadata(typeof(DockableContent), new FrameworkPropertyMetadata(typeof(DockableContent))); } public DockableContent() { } #region Drag content protected override void OnDragMouseMove(object sender, MouseEventArgs e) { base.OnDragMouseMove(sender, e); } protected override void OnDragStart(Point ptMouse, Point ptRelativeMouse) { if (DockableStyle != DockableStyle.None && State != DockableContentState.AutoHide) { Manager.Drag(this, HelperFunc.PointToScreenWithoutFlowDirection(this, ptMouse), ptRelativeMouse); } base.OnDragStart(ptMouse, ptRelativeMouse); } #endregion #region State Properties & Events public DockableContentState State { get { return (DockableContentState)GetValue(StatePropertyKey.DependencyProperty); } protected set { SetValue(StatePropertyKey, value); } } // Using a DependencyProperty as the backing store for State. This enables animation, styling, binding, etc... public static readonly DependencyPropertyKey StatePropertyKey = DependencyProperty.RegisterReadOnly("State", typeof(DockableContentState), typeof(DockableContent), new UIPropertyMetadata(DockableContentState.Docked)); DockableStyle _dockableStyle = DockableStyle.Dockable; /// /// Get or sets a value that indicates how a dockable content can be dragged over and docked to a /// public DockableStyle DockableStyle { get { return _dockableStyle; } set { _dockableStyle = value; } } #endregion #region StateMachine internal void SetStateToAutoHide() { State = DockableContentState.AutoHide; } internal void SetStateToDock() { State = DockableContentState.Docked; } internal void SetStateToDockableWindow() { if (State == DockableContentState.DockableWindow) return; Debug.Assert( State == DockableContentState.Document || State == DockableContentState.Docked || State == DockableContentState.FloatingWindow); State = DockableContentState.DockableWindow; } internal void SetStateToFloatingWindow() { if (State == DockableContentState.FloatingWindow) return; Debug.Assert( State == DockableContentState.Document || State == DockableContentState.Docked || State == DockableContentState.DockableWindow); State = DockableContentState.FloatingWindow; } internal void SetStateToHidden() { State = DockableContentState.Hidden; } internal void SetStateToDocument() { State = DockableContentState.Document; } #endregion protected override void OnInitialized(EventArgs e) { base.OnInitialized(e); this.CommandBindings.Add( new CommandBinding(FloatingCommand, this.OnExecuteCommand, this.OnCanExecuteCommand)); this.CommandBindings.Add( new CommandBinding(DockableCommand, this.OnExecuteCommand, this.OnCanExecuteCommand)); this.CommandBindings.Add( new CommandBinding(ShowAsDocumentCommand, this.OnExecuteCommand, this.OnCanExecuteCommand)); this.CommandBindings.Add( new CommandBinding(DockablePane.ToggleAutoHideCommand, this.OnExecuteCommand, this.OnCanExecuteCommand)); this.CommandBindings.Add( new CommandBinding(HideCommand, this.OnExecuteCommand, this.OnCanExecuteCommand)); } #region Commands static object syncRoot = new object(); private static RoutedUICommand documentCommand = null; public static RoutedUICommand ShowAsDocumentCommand { get { lock (syncRoot) { if (null == documentCommand) { documentCommand = new RoutedUICommand("Tabbed Document", "Document", typeof(DockableContent)); } } return documentCommand; } } private static RoutedUICommand dockableCommand = null; public static RoutedUICommand DockableCommand { get { lock (syncRoot) { if (null == dockableCommand) { dockableCommand = new RoutedUICommand("Dockable", "Dockable", typeof(DockablePane)); } } return dockableCommand; } } private static RoutedUICommand floatingCommand = null; public static RoutedUICommand FloatingCommand { get { lock (syncRoot) { if (null == floatingCommand) { floatingCommand = new RoutedUICommand("F_loating", "Floating", typeof(DockableContent)); } } return floatingCommand; } } private static RoutedUICommand hideCommand = null; public static RoutedUICommand HideCommand { get { lock (syncRoot) { if (null == hideCommand) { hideCommand = new RoutedUICommand("H_ide", "Hide", typeof(DockableContent)); } } return hideCommand; } } internal virtual void OnExecuteCommand(object sender, ExecutedRoutedEventArgs e) { if (!e.Handled && e.Command == DockablePane.ToggleAutoHideCommand) { if ((DockableStyle & DockableStyle.AutoHide) > 0) { ((ContainerPane as DockablePane)).ToggleAutoHide(); e.Handled = true; } } else if (!e.Handled && e.Command == DockableContent.HideCommand) { Manager.Hide(this); e.Handled = true; } else if (!e.Handled && e.Command == DockableContent.FloatingCommand) { Manager.Show(this, DockableContentState.FloatingWindow); e.Handled = true; } else if (!e.Handled && e.Command == DockableContent.DockableCommand) { Manager.Show(this, DockableContentState.DockableWindow); e.Handled = true; } else if (!e.Handled && e.Command == DockableContent.ShowAsDocumentCommand) { Manager.Show(this, DockableContentState.Document); e.Handled = true; } } protected virtual void OnCanExecuteCommand(object sender, CanExecuteRoutedEventArgs e) { if (State == DockableContentState.AutoHide) { if (e.Command == DockablePane.ToggleAutoHideCommand || e.Command == DockablePane.CloseCommand || e.Command == DockableContent.HideCommand) e.CanExecute = true; else e.CanExecute = false; } else e.CanExecute = true; } #endregion #region Operations on content /// /// Remove this content from its parent container pane /// /// internal DockableContent DetachFromContainerPane() { if (ContainerPane != null) { int indexOfContent = ContainerPane.Items.IndexOf(this); return ContainerPane.RemoveContent(indexOfContent) as DockableContent; } return null; } DockableContentStateAndPosition _savedStateAndPosition = null; internal DockableContentStateAndPosition SavedStateAndPosition { get { return _savedStateAndPosition; } } internal void SaveCurrentStateAndPosition() { if (State == DockableContentState.Docked) { _savedStateAndPosition = new DockableContentStateAndPosition( this); } else { _savedStateAndPosition = null; } } /// /// Reset internal state and position of the content /// /// After a is hidden AvalonDock save its state and position in order to /// restore it correctly when user wants to reshow it calling function. Call this method /// if you want to reset these data and provide your state and anchor style calling one of the overloads of the function /// . public void ResetSavedStateAndPosition() { _savedStateAndPosition = null; } #endregion #region Save/Restore Content Layout /// /// Save content specific layout settings /// /// Backend store writer /// Custom derived class can overloads this method to handle custom layout persistence. public virtual void SaveLayout(XmlWriter storeWriter) { if (!FloatingWindowSize.IsEmpty) { storeWriter.WriteAttributeString( "FloatingWindowSize", new SizeConverter().ConvertToInvariantString(FloatingWindowSize)); } if (SavedStateAndPosition != null) { storeWriter.WriteAttributeString( "ChildIndex", SavedStateAndPosition.ChildIndex.ToString()); storeWriter.WriteAttributeString( "Width", SavedStateAndPosition.Width.ToString()); storeWriter.WriteAttributeString( "Height", SavedStateAndPosition.Height.ToString()); storeWriter.WriteAttributeString( "Anchor", SavedStateAndPosition.Anchor.ToString()); } } /// /// Restore content specific layout settings /// /// Saved xml element containg content layout settings /// Custom derived class must overload this method to restore custom layout settings previously saved trought . public virtual void RestoreLayout(XmlElement contentElement) { if (contentElement.HasAttribute("FloatingWindowSize")) FloatingWindowSize = (Size)(new SizeConverter()).ConvertFromInvariantString(contentElement.GetAttribute("FloatingWindowSize")); Size effectiveSize = new Size(0d, 0d); if (contentElement.HasAttribute("EffectiveSize")) { // Store effectiveSize = (Size)(new SizeConverter()).ConvertFromInvariantString(contentElement.GetAttribute("EffectiveSize")); } ResizingPanel.SetEffectiveSize(this, effectiveSize); if (contentElement.HasAttribute("ChildIndex")) { _savedStateAndPosition = new DockableContentStateAndPosition( ContainerPane, int.Parse(contentElement.GetAttribute("ChildIndex")), double.Parse(contentElement.GetAttribute("Width")), double.Parse(contentElement.GetAttribute("Height")), (AnchorStyle) Enum.Parse(typeof(AnchorStyle), contentElement.GetAttribute("Anchor")) ); } } #endregion } }