//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
}
}