Click here to Skip to main content
15,886,026 members
Articles / Desktop Programming / WPF

AvalonDock [2.0] Tutorial Part 1 - Adding a Tool Window

Rate me:
Please Sign up or sign in to vote.
4.97/5 (37 votes)
6 Nov 2018CPOL9 min read 290.8K   12K   125  
How to create a new tool window in AvalonDock [2.0]
//Copyright (c) 2007-2012, 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.Linq;
using System.Text;
using System.Windows;
using System.Xml.Serialization;
using System.Windows.Controls;
using System.Globalization;
using System.ComponentModel;

namespace AvalonDock.Layout
{
    [Serializable]
    public class LayoutAnchorable : LayoutContent
    {
        #region IsVisible
        [XmlIgnore]
        public bool IsVisible
        {
            get
            {
                return Parent != null && !(Parent is LayoutRoot);
            }
            set
            {
                if (value)
                {
                    Show();
                }
                else
                {
                    Hide();
                }
            }
        }

        public event EventHandler IsVisibleChanged;

        void NotifyIsVisibleChanged()
        {
            if (IsVisibleChanged != null)
                IsVisibleChanged(this, EventArgs.Empty);
        }

        [XmlIgnore]
        public bool IsHidden
        {
            get
            {
                return (Parent is LayoutRoot);
            }
        }

        protected override void OnParentChanged(ILayoutContainer oldValue, ILayoutContainer newValue)
        {
            UpdateParentVisibility();
            RaisePropertyChanged("IsVisible");
            NotifyIsVisibleChanged();
            RaisePropertyChanged("IsHidden");
            RaisePropertyChanged("IsAutoHidden");
            base.OnParentChanged(oldValue, newValue);
        }

        void UpdateParentVisibility()
        {
            var parentPane = Parent as ILayoutElementWithVisibility;
            if (parentPane != null)
                parentPane.ComputeVisibility();
        }

        #endregion

        #region AutoHideWidth

        private double _autohideWidth = 0.0;
        public double AutoHideWidth
        {
            get { return _autohideWidth; }
            set
            {
                if (_autohideWidth != value)
                {
                    RaisePropertyChanging("AutoHideWidth");
                    value = Math.Max(value, _autohideMinWidth);
                    _autohideWidth = value;
                    RaisePropertyChanged("AutoHideWidth");
                }
            }
        }

        #endregion

        #region AutoHideMinWidth

        private double _autohideMinWidth = 100.0;
        public double AutoHideMinWidth
        {
            get { return _autohideMinWidth; }
            set
            {
                if (_autohideMinWidth != value)
                {
                    RaisePropertyChanging("AutoHideMinWidth");
                    if (value < 0)
                        throw new ArgumentException("value");
                    _autohideMinWidth = value;
                    RaisePropertyChanged("AutoHideMinWidth");
                }
            }
        }

        #endregion

        #region AutoHideHeight

        private double _autohideHeight = 0.0;
        public double AutoHideHeight
        {
            get { return _autohideHeight; }
            set
            {
                if (_autohideHeight != value)
                {
                    RaisePropertyChanging("AutoHideHeight");
                    value = Math.Max(value, _autohideMinHeight);
                    _autohideHeight = value;
                    RaisePropertyChanged("AutoHideHeight");
                }
            }
        }

        #endregion

        #region AutoHideMinHeight

        private double _autohideMinHeight = 100.0;
        public double AutoHideMinHeight
        {
            get { return _autohideMinHeight; }
            set
            {
                if (_autohideMinHeight != value)
                {
                    RaisePropertyChanging("AutoHideMinHeight");
                    if (value < 0)
                        throw new ArgumentException("value");
                    _autohideMinHeight = value;
                    RaisePropertyChanged("AutoHideMinHeight");
                }
            }
        }

        #endregion

        /// <summary>
        /// Hide this contents
        /// </summary>
        /// <remarks>Add this content to <see cref="ILayoutRoot.Hidden"/> collection of parent root.</remarks>
        public void Hide(bool cancelable = true)
        {
            if (!IsVisible)
            {
                IsSelected = true;
                IsActive = true;
                return;
            }

            if (cancelable)
            {
                CancelEventArgs args = new CancelEventArgs();
                OnHiding(args);
                if (args.Cancel)
                    return;
            }

            RaisePropertyChanging("IsHidden");
            RaisePropertyChanging("IsVisible");
            //if (Parent is ILayoutPane)
            {
                var parentAsGroup = Parent as ILayoutGroup;
                PreviousContainer = parentAsGroup;
                PreviousContainerIndex = parentAsGroup.IndexOfChild(this);
            }
            Root.Hidden.Add(this);
            RaisePropertyChanged("IsVisible");
            RaisePropertyChanged("IsHidden");
            NotifyIsVisibleChanged();
        }

        public event EventHandler<CancelEventArgs> Hiding;

        protected virtual void OnHiding(CancelEventArgs args)
        {
            if (Hiding != null)
                Hiding(this, args);
        }


        /// <summary>
        /// Show the content
        /// </summary>
        /// <remarks>Try to show the content where it was previously hidden.</remarks>
        public void Show()
        {
            if (IsVisible)
                return;

            if (!IsHidden)
                throw new InvalidOperationException();

            RaisePropertyChanging("IsHidden");
            RaisePropertyChanging("IsVisible");

            bool added = false;
            var root = Root;
            if (root != null && root.Manager != null)
            {
                if (root.Manager.LayoutUpdateStrategy != null)
                    added = root.Manager.LayoutUpdateStrategy.BeforeInsertAnchorable(root as LayoutRoot, this, PreviousContainer);
            }

            if (!added && PreviousContainer != null)
            {
                var previousContainerAsLayoutGroup = PreviousContainer as ILayoutGroup;
                if (PreviousContainerIndex < previousContainerAsLayoutGroup.ChildrenCount)
                    previousContainerAsLayoutGroup.InsertChildAt(PreviousContainerIndex, this);
                else
                    previousContainerAsLayoutGroup.InsertChildAt(previousContainerAsLayoutGroup.ChildrenCount, this);
                IsSelected = true;
                IsActive = true;
            }

            if (root != null && root.Manager != null)
            {
                if (root.Manager.LayoutUpdateStrategy != null)
                {
                    root.Manager.LayoutUpdateStrategy.AfterInsertAnchorable(root as LayoutRoot, this);
                }
            }

            PreviousContainer = null;
            PreviousContainerIndex = -1;

            RaisePropertyChanged("IsVisible");
            RaisePropertyChanged("IsHidden");
            NotifyIsVisibleChanged();
        }

        protected override void InternalDock()
        {
            var root = Root as LayoutRoot;
            LayoutAnchorablePane anchorablePane = null;

            if (root.ActiveContent != null &&
                root.ActiveContent != this)
            {
                //look for active content parent pane
                anchorablePane = root.ActiveContent.Parent as LayoutAnchorablePane;
            }

            if (anchorablePane == null)
            {
                //look for a pane on the right side
                anchorablePane = root.Descendents().OfType<LayoutAnchorablePane>().Where(pane => !pane.IsHostedInFloatingWindow && pane.GetSide() == AnchorSide.Right).FirstOrDefault();
            }

            if (anchorablePane == null)
            {
                //look for an available pane
                anchorablePane = root.Descendents().OfType<LayoutAnchorablePane>().FirstOrDefault();
            }


            bool added = false;
            if (root.Manager.LayoutUpdateStrategy != null)
            {
                added = root.Manager.LayoutUpdateStrategy.BeforeInsertAnchorable(root, this, anchorablePane);
            }

            if (!added)
            {
                if (anchorablePane == null)
                {
                    var mainLayoutPanel = new LayoutPanel() { Orientation = Orientation.Horizontal };
                    if (root.RootPanel != null)
                    {
                        mainLayoutPanel.Children.Add(root.RootPanel);
                    }

                    root.RootPanel = mainLayoutPanel;
                    anchorablePane = new LayoutAnchorablePane() { DockWidth = new GridLength(200.0, GridUnitType.Pixel) };
                    mainLayoutPanel.Children.Add(anchorablePane);
                }

                anchorablePane.Children.Add(this);
                added = true;
            }

            if (root.Manager.LayoutUpdateStrategy != null)
            {
                root.Manager.LayoutUpdateStrategy.AfterInsertAnchorable(root, this);
            }

            base.InternalDock();
        }

        /// <summary>
        /// Add the anchorable to a DockingManager layout
        /// </summary>
        /// <param name="manager"></param>
        /// <param name="strategy"></param>
        public void AddToLayout(DockingManager manager, AnchorableShowStrategy strategy)
        {
            if (IsVisible ||
                IsHidden)
                throw new InvalidOperationException();


            bool most = (strategy & AnchorableShowStrategy.Most) == AnchorableShowStrategy.Most;
            bool left = (strategy & AnchorableShowStrategy.Left) == AnchorableShowStrategy.Left;
            bool right = (strategy & AnchorableShowStrategy.Right) == AnchorableShowStrategy.Right;
            bool top = (strategy & AnchorableShowStrategy.Top) == AnchorableShowStrategy.Top;
            bool bottom = (strategy & AnchorableShowStrategy.Bottom) == AnchorableShowStrategy.Bottom;

            if (!most)
            { 
                var side = AnchorSide.Left;
                if (left)
                    side = AnchorSide.Left;
                if (right)
                    side = AnchorSide.Right;
                if (top)
                    side = AnchorSide.Top;
                if (bottom)
                    side = AnchorSide.Bottom;

                var anchorablePane = manager.Layout.Descendents().OfType<LayoutAnchorablePane>().FirstOrDefault(p => p.GetSide() == side);
                if (anchorablePane != null)
                    anchorablePane.Children.Add(this);
                else
                    most = true;
            }


            if (most)
            {
                if (manager.Layout.RootPanel == null)
                    manager.Layout.RootPanel = new LayoutPanel() { Orientation = (left || right ? Orientation.Horizontal : Orientation.Vertical) };

                if (left || right)
                {
                    if (manager.Layout.RootPanel.Orientation == Orientation.Vertical &&
                        manager.Layout.RootPanel.ChildrenCount > 1)
                    {
                        manager.Layout.RootPanel = new LayoutPanel(manager.Layout.RootPanel);
                    }

                    manager.Layout.RootPanel.Orientation = Orientation.Horizontal;

                    if (left)
                        manager.Layout.RootPanel.Children.Insert(0, new LayoutAnchorablePane(this));
                    else
                        manager.Layout.RootPanel.Children.Add(new LayoutAnchorablePane(this));
                }
                else
                {
                    if (manager.Layout.RootPanel.Orientation == Orientation.Horizontal &&
                        manager.Layout.RootPanel.ChildrenCount > 1)
                    {
                        manager.Layout.RootPanel = new LayoutPanel(manager.Layout.RootPanel);
                    }

                    manager.Layout.RootPanel.Orientation = Orientation.Vertical;

                    if (top)
                        manager.Layout.RootPanel.Children.Insert(0, new LayoutAnchorablePane(this));
                    else
                        manager.Layout.RootPanel.Children.Add(new LayoutAnchorablePane(this));
                }
                
            }
        }


        /// <summary>
        /// Get a value indicating if the anchorable is anchored to a border in an autohide status
        /// </summary>
        public bool IsAutoHidden
        {
            get { return Parent != null && Parent is LayoutAnchorGroup; }
        }


        #region AutoHide
        public void ToggleAutoHide()
        {
            #region Anchorable is already auto hidden
            if (IsAutoHidden)
            {
                var parentGroup = Parent as LayoutAnchorGroup;
                var parentSide = parentGroup.Parent as LayoutAnchorSide;
                var previousContainer = ((ILayoutPreviousContainer)parentGroup).PreviousContainer as LayoutAnchorablePane;

                if (previousContainer == null)
                {
                    AnchorSide side = (parentGroup.Parent as LayoutAnchorSide).Side;
                    switch (side)
                    {
                        case AnchorSide.Right:
                            if (parentGroup.Root.RootPanel.Orientation == Orientation.Horizontal)
                            {
                                previousContainer = new LayoutAnchorablePane();
                                parentGroup.Root.RootPanel.Children.Add(previousContainer);
                            }
                            else
                            {
                                previousContainer = new LayoutAnchorablePane();
                                LayoutPanel panel = new LayoutPanel() { Orientation = Orientation.Horizontal };
                                LayoutRoot root = parentGroup.Root as LayoutRoot;
                                LayoutPanel oldRootPanel = parentGroup.Root.RootPanel as LayoutPanel;
                                root.RootPanel = panel;
                                panel.Children.Add(oldRootPanel);
                                panel.Children.Add(previousContainer);
                            }
                            break;
                        case AnchorSide.Left:
                            if (parentGroup.Root.RootPanel.Orientation == Orientation.Horizontal)
                            {
                                previousContainer = new LayoutAnchorablePane();
                                parentGroup.Root.RootPanel.Children.Insert(0, previousContainer);
                            }
                            else
                            {
                                previousContainer = new LayoutAnchorablePane();
                                LayoutPanel panel = new LayoutPanel() { Orientation = Orientation.Horizontal };
                                LayoutRoot root = parentGroup.Root as LayoutRoot;
                                LayoutPanel oldRootPanel = parentGroup.Root.RootPanel as LayoutPanel;
                                root.RootPanel = panel;
                                panel.Children.Add(previousContainer);
                                panel.Children.Add(oldRootPanel);
                            }
                            break;
                        case AnchorSide.Top:
                            if (parentGroup.Root.RootPanel.Orientation == Orientation.Vertical)
                            {
                                previousContainer = new LayoutAnchorablePane();
                                parentGroup.Root.RootPanel.Children.Insert(0, previousContainer);
                            }
                            else
                            {
                                previousContainer = new LayoutAnchorablePane();
                                LayoutPanel panel = new LayoutPanel() { Orientation = Orientation.Vertical };
                                LayoutRoot root = parentGroup.Root as LayoutRoot;
                                LayoutPanel oldRootPanel = parentGroup.Root.RootPanel as LayoutPanel;
                                root.RootPanel = panel;
                                panel.Children.Add(previousContainer);
                                panel.Children.Add(oldRootPanel);
                            }
                            break;
                        case AnchorSide.Bottom:
                            if (parentGroup.Root.RootPanel.Orientation == Orientation.Vertical)
                            {
                                previousContainer = new LayoutAnchorablePane();
                                parentGroup.Root.RootPanel.Children.Add(previousContainer);
                            }
                            else
                            {
                                previousContainer = new LayoutAnchorablePane();
                                LayoutPanel panel = new LayoutPanel() { Orientation = Orientation.Vertical };
                                LayoutRoot root = parentGroup.Root as LayoutRoot;
                                LayoutPanel oldRootPanel = parentGroup.Root.RootPanel as LayoutPanel;
                                root.RootPanel = panel;
                                panel.Children.Add(oldRootPanel);
                                panel.Children.Add(previousContainer);
                            }
                            break;
                    }
                }
                else
                { 
                    //I'm about to remove parentGroup, redirect any content (ie hidden contents) that point to it
                    //to previousContainer
                    LayoutRoot root = parentGroup.Root as LayoutRoot;
                    foreach (var cnt in root.Descendents().OfType<ILayoutPreviousContainer>().Where(c => c.PreviousContainer == parentGroup))
                    {
                        cnt.PreviousContainer = previousContainer;
                    }
                }


                foreach (var anchorableToToggle in parentGroup.Children.ToArray())
                {
                    previousContainer.Children.Add(anchorableToToggle);
                }

                parentSide.Children.Remove(parentGroup);
            }
            #endregion
            #region Anchorable is docked
            else if (Parent is LayoutAnchorablePane)
            {
                var root = Root;
                var parentPane = Parent as LayoutAnchorablePane;

                var newAnchorGroup = new LayoutAnchorGroup();

                ((ILayoutPreviousContainer)newAnchorGroup).PreviousContainer = parentPane;

                foreach (var anchorableToImport in parentPane.Children.ToArray())
                    newAnchorGroup.Children.Add(anchorableToImport);

                //detect anchor side for the pane
                var anchorSide = parentPane.GetSide();

                switch (anchorSide)
                {
                    case AnchorSide.Right:
                        root.RightSide.Children.Add(newAnchorGroup);
                        break;
                    case AnchorSide.Left:
                        root.LeftSide.Children.Add(newAnchorGroup);
                        break;
                    case AnchorSide.Top:
                        root.TopSide.Children.Add(newAnchorGroup);
                        break;
                    case AnchorSide.Bottom:
                        root.BottomSide.Children.Add(newAnchorGroup);
                        break;
                }
            }
            #endregion
        }

        #endregion

        #region CanHide

        private bool _canHide = true;
        public bool CanHide
        {
            get { return _canHide; }
            set
            {
                if (_canHide != value)
                {
                    _canHide = value;
                    RaisePropertyChanged("CanHide");
                }
            }
        }

        #endregion

        #region CanAutoHide

        private bool _canAutoHide = true;
        public bool CanAutoHide
        {
            get { return _canAutoHide; }
            set
            {
                if (_canAutoHide != value)
                {
                    _canAutoHide = value;
                    RaisePropertyChanged("CanAutoHide");
                }
            }
        }

        #endregion


        public override void ReadXml(System.Xml.XmlReader reader)
        {
            if (reader.MoveToAttribute("CanHide"))
                CanHide = bool.Parse(reader.Value);
            if (reader.MoveToAttribute("CanAutoHide"))
                CanAutoHide = bool.Parse(reader.Value);
            if (reader.MoveToAttribute("AutoHideWidth"))
                AutoHideWidth = double.Parse(reader.Value, CultureInfo.InvariantCulture);
            if (reader.MoveToAttribute("AutoHideHeight"))
                AutoHideHeight = double.Parse(reader.Value, CultureInfo.InvariantCulture);
            if (reader.MoveToAttribute("AutoHideMinWidth"))
                AutoHideMinWidth = double.Parse(reader.Value, CultureInfo.InvariantCulture);
            if (reader.MoveToAttribute("AutoHideMinHeight"))
                AutoHideMinHeight = double.Parse(reader.Value, CultureInfo.InvariantCulture);

            base.ReadXml(reader);
        }

        public override void WriteXml(System.Xml.XmlWriter writer)
        {
            if (!CanHide)
                writer.WriteAttributeString("CanHide", CanHide.ToString());
            if (!CanAutoHide)
                writer.WriteAttributeString("CanAutoHide", CanAutoHide.ToString(CultureInfo.InvariantCulture));
            if (AutoHideWidth > 0)
                writer.WriteAttributeString("AutoHideWidth", AutoHideWidth.ToString(CultureInfo.InvariantCulture));
            if (AutoHideHeight > 0)
                writer.WriteAttributeString("AutoHideHeight", AutoHideHeight.ToString(CultureInfo.InvariantCulture));
            if (AutoHideMinWidth != 25.0)
                writer.WriteAttributeString("AutoHideMinWidth", AutoHideMinWidth.ToString(CultureInfo.InvariantCulture));
            if (AutoHideMinHeight != 25.0)
                writer.WriteAttributeString("AutoHideMinHeight", AutoHideMinHeight.ToString(CultureInfo.InvariantCulture));

            
            base.WriteXml(writer);
        }


#if DEBUG
        public override void ConsoleDump(int tab)
        {
            System.Diagnostics.Debug.Write(new string(' ', tab * 4));
            System.Diagnostics.Debug.WriteLine("Anchorable()");
        }
#endif
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Germany Germany
The Windows Presentation Foundation (WPF) and C# are among my favorites and so I developed Edi

and a few other projects on GitHub. I am normally an algorithms and structure type but WPF has such interesting UI sides that I cannot help myself but get into it.

https://de.linkedin.com/in/dirkbahle

Comments and Discussions