Click here to Skip to main content
15,881,044 members
Articles / Web Development / HTML

AvalonDock [2.0] Tutorial Part 4 - Integrating AvalonEdit Options

Rate me:
Please Sign up or sign in to vote.
5.00/5 (9 votes)
31 Jan 2014CPOL3 min read 59.5K   1.8K   27  
Integrate AvalonEdit with text editing options into 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.Controls;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using AvalonDock.Layout;
using System.Diagnostics;
using System.Windows.Threading;

namespace AvalonDock.Controls
{
    public abstract class LayoutGridControl<T> : Grid, ILayoutControl where T : class, ILayoutPanelElement
    {
        static LayoutGridControl()
        {
        }

        internal LayoutGridControl(LayoutPositionableGroup<T> model, Orientation orientation)
        {
            if (model == null)
                throw new ArgumentNullException("model");

            _model = model;
            _orientation = orientation;

            FlowDirection = System.Windows.FlowDirection.LeftToRight;
        }

        LayoutPositionableGroup<T> _model;
        public ILayoutElement Model
        {
            get { return _model; }
        }

        Orientation _orientation;

        public Orientation Orientation
        {
            get { return (_model as ILayoutOrientableGroup).Orientation; }
        }

        bool _initialized;
        ChildrenTreeChange? _asyncRefreshCalled;

        bool AsyncRefreshCalled
        {
            get { return _asyncRefreshCalled != null; }
        }

        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);

            _model.ChildrenTreeChanged += (s, args) =>
                {
                    if (_asyncRefreshCalled.HasValue &&
                        _asyncRefreshCalled.Value == args.Change)
                        return;
                    _asyncRefreshCalled = args.Change;
                    Dispatcher.BeginInvoke(new Action(() =>
                        {
                            _asyncRefreshCalled = null;
                            UpdateChildren();
                        }), DispatcherPriority.Normal, null);
                };

            this.LayoutUpdated += new EventHandler(OnLayoutUpdated);
        }

        void OnLayoutUpdated(object sender, EventArgs e)
        {
            var modelWithAtcualSize = _model as ILayoutPositionableElementWithActualSize;
            modelWithAtcualSize.ActualWidth = ActualWidth;
            modelWithAtcualSize.ActualHeight = ActualHeight;

            if (!_initialized)
            {
                _initialized = true;
                UpdateChildren();
            }
        }

        void UpdateChildren()
        {
            var alreadyContainedChildren = Children.OfType<ILayoutControl>().ToArray();

            DetachOldSplitters();
            DetachPropertChangeHandler();

            Children.Clear();
            ColumnDefinitions.Clear();
            RowDefinitions.Clear();

            if (_model == null ||
                _model.Root == null)
                return;

            var manager = _model.Root.Manager;
            if (manager == null)
                return;


            foreach (ILayoutElement child in _model.Children)
            {
                var foundContainedChild = alreadyContainedChildren.FirstOrDefault(chVM => chVM.Model == child);
                if (foundContainedChild != null)
                    Children.Add(foundContainedChild as UIElement);
                else
                    Children.Add(manager.CreateUIElementForModel(child));
            }

            CreateSplitters();

            UpdateRowColDefinitions();

            AttachNewSplitters();
            AttachPropertyChangeHandler();
        }

        private void AttachPropertyChangeHandler()
        {
            foreach (var child in InternalChildren.OfType<ILayoutControl>())
            {
                child.Model.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(this.OnChildModelPropertyChanged);
            }
        }

        private void DetachPropertChangeHandler()
        {
            foreach (var child in InternalChildren.OfType<ILayoutControl>())
            {
                child.Model.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(this.OnChildModelPropertyChanged);
            }
        }

        void OnChildModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (AsyncRefreshCalled)
                return;

            if (_fixingChildrenDockLengths.CanEnter && e.PropertyName == "DockWidth" && Orientation == System.Windows.Controls.Orientation.Horizontal)
            {
                if (ColumnDefinitions.Count == InternalChildren.Count)
                {
                    var changedElement = sender as ILayoutPositionableElement;
                    var childFromModel = InternalChildren.OfType<ILayoutControl>().First(ch => ch.Model == changedElement) as UIElement;
                    int indexOfChild = InternalChildren.IndexOf(childFromModel);
                    ColumnDefinitions[indexOfChild].Width = changedElement.DockWidth;
                }
            }
            else if (_fixingChildrenDockLengths.CanEnter && e.PropertyName == "DockHeight" && Orientation == System.Windows.Controls.Orientation.Vertical)
            {
                if (RowDefinitions.Count == InternalChildren.Count)
                {
                    var changedElement = sender as ILayoutPositionableElement;
                    var childFromModel = InternalChildren.OfType<ILayoutControl>().First(ch => ch.Model == changedElement) as UIElement;
                    int indexOfChild = InternalChildren.IndexOf(childFromModel);
                    RowDefinitions[indexOfChild].Height = changedElement.DockHeight;
                }
            }
            else if (e.PropertyName == "IsVisible")
            {
                UpdateRowColDefinitions();
            }
        }


        void UpdateRowColDefinitions()
        {
            var root = _model.Root;
            if (root == null)
                return;
            var manager = root.Manager;
            if (manager == null)
                return;

            FixChildrenDockLengths();

            //Debug.Assert(InternalChildren.Count == _model.ChildrenCount + (_model.ChildrenCount - 1));

            #region Setup GridRows/Cols
            RowDefinitions.Clear();
            ColumnDefinitions.Clear();
            if (Orientation == Orientation.Horizontal)
            {
                int iColumn = 0;
                int iChild = 0;
                for (int iChildModel = 0; iChildModel < _model.Children.Count; iChildModel++, iColumn++, iChild++)
                {
                    var childModel = _model.Children[iChildModel] as ILayoutPositionableElement;
                    ColumnDefinitions.Add(new ColumnDefinition()
                    {
                        Width = childModel.IsVisible ? childModel.DockWidth : new GridLength(0.0, GridUnitType.Pixel),
                        MinWidth = childModel.IsVisible ? childModel.DockMinWidth : 0.0
                    });
                    Grid.SetColumn(InternalChildren[iChild], iColumn);

                    //append column for splitter
                    if (iChild < InternalChildren.Count - 1)
                    {
                        iChild++;
                        iColumn++;

                        bool nextChildModelVisibleExist = false;
                        for (int i = iChildModel + 1; i < _model.Children.Count; i++)
                        {
                            var nextChildModel = _model.Children[i] as ILayoutPositionableElement;
                            if (nextChildModel.IsVisible)
                            {
                                nextChildModelVisibleExist = true;
                                break;
                            }
                        }

                        ColumnDefinitions.Add(new ColumnDefinition()
                        {
                            Width = childModel.IsVisible && nextChildModelVisibleExist ? new GridLength(manager.GridSplitterWidth) : new GridLength(0.0, GridUnitType.Pixel)
                        });
                        Grid.SetColumn(InternalChildren[iChild], iColumn);
                    }
                }
            }
            else //if (_model.Orientation == Orientation.Vertical)
            {
                int iRow = 0;
                int iChild = 0;
                for (int iChildModel = 0; iChildModel < _model.Children.Count; iChildModel++, iRow++, iChild++)
                {
                    var childModel = _model.Children[iChildModel] as ILayoutPositionableElement;
                    RowDefinitions.Add(new RowDefinition()
                    {
                        Height = childModel.IsVisible ? childModel.DockHeight : new GridLength(0.0, GridUnitType.Pixel),
                        MinHeight = childModel.IsVisible ? childModel.DockMinHeight : 0.0
                    });
                    Grid.SetRow(InternalChildren[iChild], iRow);

                    //if (RowDefinitions.Last().Height.Value == 0.0)
                    //    System.Diagnostics.Debugger.Break();

                    //append row for splitter (if necessary)
                    if (iChild < InternalChildren.Count - 1)
                    {
                        iChild++;
                        iRow++;

                        bool nextChildModelVisibleExist = false;
                        for (int i = iChildModel + 1; i < _model.Children.Count; i++)
                        {
                            var nextChildModel = _model.Children[i] as ILayoutPositionableElement;
                            if (nextChildModel.IsVisible)
                            {
                                nextChildModelVisibleExist = true;
                                break;
                            }
                        }

                        RowDefinitions.Add(new RowDefinition()
                        {
                            Height = childModel.IsVisible && nextChildModelVisibleExist ? new GridLength(manager.GridSplitterHeight) : new GridLength(0.0, GridUnitType.Pixel)
                        });
                        //if (RowDefinitions.Last().Height.Value == 0.0)
                        //    System.Diagnostics.Debugger.Break();
                        Grid.SetRow(InternalChildren[iChild], iRow);
                    }
                }
            }

            #endregion
        }

        ReentrantFlag _fixingChildrenDockLengths = new ReentrantFlag();
        protected void FixChildrenDockLengths()
        {
            using (_fixingChildrenDockLengths.Enter())
                OnFixChildrenDockLengths();
        }

        protected abstract void OnFixChildrenDockLengths();

        #region Splitters

        void CreateSplitters()
        {
            for (int iChild = 1; iChild < Children.Count; iChild++)
            {
                var splitter = new LayoutGridResizerControl();
                splitter.Cursor = this.Orientation == Orientation.Horizontal ? Cursors.SizeWE : Cursors.SizeNS;
                Children.Insert(iChild, splitter);
                iChild++;
            }
        }

        void DetachOldSplitters()
        {
            foreach (var splitter in Children.OfType<LayoutGridResizerControl>())
            {
                splitter.DragStarted -= new System.Windows.Controls.Primitives.DragStartedEventHandler(OnSplitterDragStarted);
                splitter.DragDelta -= new System.Windows.Controls.Primitives.DragDeltaEventHandler(OnSplitterDragDelta);
                splitter.DragCompleted -= new System.Windows.Controls.Primitives.DragCompletedEventHandler(OnSplitterDragCompleted);
            }
        }

        void AttachNewSplitters()
        {
            foreach (var splitter in Children.OfType<LayoutGridResizerControl>())
            {
                splitter.DragStarted += new System.Windows.Controls.Primitives.DragStartedEventHandler(OnSplitterDragStarted);
                splitter.DragDelta += new System.Windows.Controls.Primitives.DragDeltaEventHandler(OnSplitterDragDelta);
                splitter.DragCompleted += new System.Windows.Controls.Primitives.DragCompletedEventHandler(OnSplitterDragCompleted);
            }
        }

        void OnSplitterDragStarted(object sender, System.Windows.Controls.Primitives.DragStartedEventArgs e)
        {
            var resizer = sender as LayoutGridResizerControl;
            ShowResizerOverlayWindow(resizer);
        }

        void OnSplitterDragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
        {
            LayoutGridResizerControl splitter = sender as LayoutGridResizerControl;
            var rootVisual = this.FindVisualTreeRoot() as Visual;

            var trToWnd = TransformToAncestor(rootVisual);
            Vector transformedDelta = trToWnd.Transform(new Point(e.HorizontalChange, e.VerticalChange)) -
                trToWnd.Transform(new Point());

            if (Orientation == System.Windows.Controls.Orientation.Horizontal)
            {
                Canvas.SetLeft(_resizerGhost, MathHelper.MinMax(_initialStartPoint.X + transformedDelta.X, 0.0, _resizerWindowHost.Width - _resizerGhost.Width));
            }
            else
            {
                Canvas.SetTop(_resizerGhost, MathHelper.MinMax(_initialStartPoint.Y + transformedDelta.Y, 0.0, _resizerWindowHost.Height - _resizerGhost.Height));
            }
        }

        void OnSplitterDragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e)
        {
            LayoutGridResizerControl splitter = sender as LayoutGridResizerControl;
            var rootVisual = this.FindVisualTreeRoot() as Visual;

            var trToWnd = TransformToAncestor(rootVisual);
            Vector transformedDelta = trToWnd.Transform(new Point(e.HorizontalChange, e.VerticalChange)) -
                trToWnd.Transform(new Point());

            double delta;
            if (Orientation == System.Windows.Controls.Orientation.Horizontal)
                delta = Canvas.GetLeft(_resizerGhost) - _initialStartPoint.X;
            else
                delta = Canvas.GetTop(_resizerGhost) - _initialStartPoint.Y;

            int indexOfResizer = InternalChildren.IndexOf(splitter);

            var prevChild = InternalChildren[indexOfResizer - 1] as FrameworkElement;
            var nextChild = GetNextVisibleChild(indexOfResizer);

            var prevChildActualSize = prevChild.TransformActualSizeToAncestor();
            var nextChildActualSize = nextChild.TransformActualSizeToAncestor();

            var prevChildModel = (ILayoutPositionableElement)(prevChild as ILayoutControl).Model;
            var nextChildModel = (ILayoutPositionableElement)(nextChild as ILayoutControl).Model;

            if (Orientation == System.Windows.Controls.Orientation.Horizontal)
            {
                //Debug.WriteLine(string.Format("PrevChild From {0}", prevChildModel.DockWidth));
                if (prevChildModel.DockWidth.IsStar)
                {
                    prevChildModel.DockWidth = new GridLength(prevChildModel.DockWidth.Value * (prevChildActualSize.Width + delta) / prevChildActualSize.Width, GridUnitType.Star);
                }
                else
                {
                    prevChildModel.DockWidth = new GridLength(prevChildModel.DockWidth.Value + delta, GridUnitType.Pixel);
                }
                //Debug.WriteLine(string.Format("PrevChild To {0}", prevChildModel.DockWidth));

                //Debug.WriteLine(string.Format("NextChild From {0}", nextChildModel.DockWidth));
                if (nextChildModel.DockWidth.IsStar)
                {
                    nextChildModel.DockWidth = new GridLength(nextChildModel.DockWidth.Value * (nextChildActualSize.Width - delta) / nextChildActualSize.Width, GridUnitType.Star);
                }
                else
                {
                    nextChildModel.DockWidth = new GridLength(nextChildModel.DockWidth.Value - delta, GridUnitType.Pixel);
                }
                //Debug.WriteLine(string.Format("NextChild To {0}", nextChildModel.DockWidth));
            }
            else
            {
                //Debug.WriteLine(string.Format("PrevChild From {0}", prevChildModel.DockHeight));
                if (prevChildModel.DockHeight.IsStar)
                {
                    prevChildModel.DockHeight = new GridLength(prevChildModel.DockHeight.Value * (prevChildActualSize.Height + delta) / prevChildActualSize.Height, GridUnitType.Star);
                }
                else
                {
                    prevChildModel.DockHeight = new GridLength(prevChildModel.DockHeight.Value + delta, GridUnitType.Pixel);
                }
                //Debug.WriteLine(string.Format("PrevChild To {0}", prevChildModel.DockHeight));

                //Debug.WriteLine(string.Format("NextChild From {0}", nextChildModel.DockHeight));
                if (nextChildModel.DockHeight.IsStar)
                {
                    nextChildModel.DockHeight = new GridLength(nextChildModel.DockHeight.Value * (nextChildActualSize.Height - delta) / nextChildActualSize.Height, GridUnitType.Star);
                }
                else
                {
                    nextChildModel.DockHeight = new GridLength(nextChildModel.DockHeight.Value - delta, GridUnitType.Pixel);
                }
                //Debug.WriteLine(string.Format("NextChild To {0}", nextChildModel.DockHeight));
            }

            HideResizerOverlayWindow();
        }

        Border _resizerGhost = null;
        Window _resizerWindowHost = null;
        Vector _initialStartPoint;

        FrameworkElement GetNextVisibleChild(int index)
        {
            for (int i = index + 1; i < InternalChildren.Count; i++)
            {
                if (InternalChildren[i] is LayoutGridResizerControl)
                    continue;

                if (Orientation == System.Windows.Controls.Orientation.Horizontal)
                {
                    if (ColumnDefinitions[i].Width.IsStar || ColumnDefinitions[i].Width.Value > 0)
                        return InternalChildren[i] as FrameworkElement;
                }
                else
                {
                    if (RowDefinitions[i].Height.IsStar || RowDefinitions[i].Height.Value > 0)
                        return InternalChildren[i] as FrameworkElement;
                }
            }

            return null;
        }

        void ShowResizerOverlayWindow(LayoutGridResizerControl splitter)
        {
            _resizerGhost = new Border()
            {
                Background = splitter.BackgroundWhileDragging,
                Opacity = splitter.OpacityWhileDragging
            };

            int indexOfResizer = InternalChildren.IndexOf(splitter);

            var prevChild = InternalChildren[indexOfResizer - 1] as FrameworkElement;
            var nextChild = GetNextVisibleChild(indexOfResizer);

            var prevChildActualSize = prevChild.TransformActualSizeToAncestor();
            var nextChildActualSize = nextChild.TransformActualSizeToAncestor();

            var prevChildModel = (ILayoutPositionableElement)(prevChild as ILayoutControl).Model;
            var nextChildModel = (ILayoutPositionableElement)(nextChild as ILayoutControl).Model;

            Point ptTopLeftScreen = prevChild.PointToScreenDPIWithoutFlowDirection(new Point());

            Size actualSize;

            if (Orientation == System.Windows.Controls.Orientation.Horizontal)
            {
                actualSize = new Size(
                    prevChildActualSize.Width - prevChildModel.DockMinWidth + splitter.ActualWidth + nextChildActualSize.Width - nextChildModel.DockMinWidth,
                    nextChildActualSize.Height);
                
                _resizerGhost.Width = splitter.ActualWidth;
                _resizerGhost.Height = actualSize.Height;
                ptTopLeftScreen.Offset(prevChildModel.DockMinWidth, 0.0);
            }
            else
            {
                actualSize = new Size(
                    prevChildActualSize.Width,
                    prevChildActualSize.Height - prevChildModel.DockMinHeight + splitter.ActualHeight + nextChildActualSize.Height - nextChildModel.DockMinHeight);

                _resizerGhost.Height = splitter.ActualHeight;
                _resizerGhost.Width = actualSize.Width;
                
                ptTopLeftScreen.Offset(0.0, prevChildModel.DockMinHeight);
            }

            _initialStartPoint = splitter.PointToScreenDPIWithoutFlowDirection(new Point()) - ptTopLeftScreen;

            if (Orientation == System.Windows.Controls.Orientation.Horizontal)
            {
                Canvas.SetLeft(_resizerGhost, _initialStartPoint.X);
            }
            else
            {
                Canvas.SetTop(_resizerGhost, _initialStartPoint.Y);
            }

            Canvas panelHostResizer = new Canvas()
            {
                HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
                VerticalAlignment = System.Windows.VerticalAlignment.Stretch
            };

            panelHostResizer.Children.Add(_resizerGhost);


            _resizerWindowHost = new Window()
            {
                ResizeMode = ResizeMode.NoResize,
                WindowStyle = System.Windows.WindowStyle.None,
                ShowInTaskbar = false,
                AllowsTransparency = true,
                Background = null,
                Width = actualSize.Width,
                Height = actualSize.Height,
                Left = ptTopLeftScreen.X,
                Top = ptTopLeftScreen.Y,
                ShowActivated = false,
                //Owner = Window.GetWindow(this),
                Content = panelHostResizer
            };
            _resizerWindowHost.Loaded += (s, e) =>
                {
                    _resizerWindowHost.SetParentToMainWindowOf(this);
                };
            _resizerWindowHost.Show();
        }

        void HideResizerOverlayWindow()
        {
            if (_resizerWindowHost != null)
            {
                _resizerWindowHost.Close();
                _resizerWindowHost = null;
            }
        }
        
        #endregion


        

    }
}

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