Click here to Skip to main content
15,896,557 members
Articles / Desktop Programming / WPF

Multiple Window Interface for WPF

Rate me:
Please Sign up or sign in to vote.
4.88/5 (51 votes)
18 Jan 2008CPOL12 min read 376K   19.8K   134  
An article on the creation of multiple attachable/detachable windows inside of a WPF control.
#region Using Region

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Documents;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Input;
using System.Windows.Controls;

#endregion

namespace WPFMwiWindows.Controls
{
    /// <summary>
    /// This class is used to resize the Window and Children of the MwiWindow.
    /// This should really be split up into two separate classes and made generic so
    /// that any class can be used not just a MwiChild.
    /// </summary>
    public class ResizeAdorner : Adorner
    {
        #region Private Variables

        private Rect[] mHotSpots = new Rect[12]; // number of hot-spots on an object
        private Point mCurrentPoint;             // the current point clicked
        private Point mLastPoint;                // the last point clicked
        private HotSpot mCurrentHotSpot = HotSpot.None; // the current hot-spot clicked, default none
        private bool mIsDown = false;            // true if the mouse is currently down
        private Window mWindow = null;           // contains the window object if there is one
        private double mMinWidth = 30.0d;        // the minimum width of the object
        private double mMinHeight = 30.0d;       // the minimum height of the object
        private double mMaxWidth = 300.0d;       // the maximum width of the object
        private double mMaxHeight = 300.0d;      // the maximum height of the object

        #endregion

        #region Enums

        /// <summary>
        /// An enum containing all of the hotspots found on an objectes bounding rectangle
        /// </summary>
        private enum HotSpot
        {
            TopLeft1 = 0, // vertical line of the top-left corner
            TopLeft2,     // horizontal line of the top-left corner
            Top,
            TopRight1,    // horizontal line of the top-right corner
            TopRight2,    // vertical line of the top-right corner
            Right,
            BottomRight1, // vertical line of the bottom-right corner
            BottomRight2, // horizontal line of the bottom-right corner
            Bottom,
            BottomLeft1,  // horizontal line of the bottom-left corner
            BottomLeft2,  // vertical line of the bottom-left corner
            Left,
            None          // default
        }

        #endregion 

        #region Constructors

        /// <summary>
        /// Constructor used when a attached MwiChild is being resized.
        /// </summary>
        /// <param name="adornedElement"></param>
        public ResizeAdorner(UIElement adornedElement)
            : base(adornedElement)
        {
            if (AdornedElement.GetType().Equals(typeof(MwiChild)))
            {
                mMinWidth = ((MwiChild)AdornedElement).MinSize.Width;
                mMinHeight = ((MwiChild)AdornedElement).MinSize.Height;
            }            
        }

        /// <summary>
        /// Constructor used when a detached MwiChild is being resized.
        /// </summary>
        /// <param name="adornedElement"></param>
        /// <param name="win"></param>
        public ResizeAdorner(UIElement adornedElement, Window win)
            : base(adornedElement)
        {
            mWindow = win; // the window containing the detached MwiChild

            try
            {
                mMinWidth = win.MinWidth;
                mMinHeight = win.MinHeight;
                mMaxWidth = win.MaxWidth;
                mMaxHeight = win.MaxHeight;
            }
            catch { }
        }

        #endregion

        #region Overrides

        /// <summary>
        /// When the left mouse button is down get the current point and capture the mouse.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnPreviewMouseLeftButtonDown(e);

            if (!mIsDown && mCurrentHotSpot != HotSpot.None)
            {
                if (AdornedElement.GetType().Equals(typeof(MwiChild)))
                    ((MwiChild)AdornedElement).IsSelected = true; // promote the child to the front since it has been selected.
                mIsDown = true; // mouse button is down
                mCurrentPoint = e.GetPosition(this);
                mLastPoint = mCurrentPoint;
                this.CaptureMouse();
            }
        }

        /// <summary>
        /// When the left mouse button is released then stop the mouse capture. 
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            base.OnPreviewMouseLeftButtonUp(e);

            if (mIsDown)
            {
                mIsDown = false; // mouse button is no longer down 
                Mouse.Capture(null);
            }
        }

        /// <summary>
        /// When the mouse is moved and while the left mouse button is down, check to see if any hotspot is selected. 
        /// If not then select a hotspot if one is found and change the cursor accrodingly. If one is selected then
        /// update the objects size and location.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPreviewMouseMove(System.Windows.Input.MouseEventArgs e)
        {
            base.OnPreviewMouseMove(e);

            Point p = e.GetPosition(this);
            if (!mIsDown)
            {                
                mCurrentHotSpot = HotSpot.None;
                for (int i = 0; i < mHotSpots.Length; i++)
                {
                    if (mHotSpots[i].Contains(p))
                    {
                        mCurrentHotSpot = (HotSpot)i;
                        UpdateCursor(mCurrentHotSpot);
                        break;
                    }
                }
            }
            else
            {
                UpdateSize(mCurrentHotSpot, p);
            }
        }

        /// <summary>
        /// Calculates the hotspots that make up the bounding rectangle. Draws the bounding rectangle as Transparent
        /// rectangles to the screen.
        /// </summary>
        /// <param name="drawingContext"></param>
        protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            // Edge and size where intended to be used to denote the size of the bounding line and the length of the 
            // corners. However as things where updated size and edge took on different meanings and now are used as a
            // ratio to get the bounding rectangle to fit the current object.
            double size = 4.0d;
            double edge = 6.0d;

            Rect rect = new Rect(AdornedElement.DesiredSize); 
            if (mWindow != null)
            {
                rect = new Rect(new Size(mWindow.Width, mWindow.Height));
                size = 8.0d;
                edge = 3.0d;
            }
            
            mHotSpots[(int)HotSpot.TopLeft1] = new Rect(new Point(-size / 2.0d, size / 2.0d), new Size(Math.Max(size, 0), Math.Max(edge * size, 0)));
            mHotSpots[(int)HotSpot.TopLeft2] = new Rect(new Point(-size / 2.0d, -size / 2.0d), new Size(Math.Max(edge * size, 0), Math.Max(size, 0)));
            mHotSpots[(int)HotSpot.Top] = new Rect(new Point(size * (edge - 0.5d), -size / 2.0d), new Size(Math.Max(rect.Width - (2.0d * (size * (edge - 0.5d))), 0), Math.Max(size, 0)));
            mHotSpots[(int)HotSpot.TopRight1] = new Rect(new Point(rect.Width - (size * (edge - 0.5d)), -size / 2.0d), new Size(Math.Max(edge * size, 0), Math.Max(size, 0)));
            mHotSpots[(int)HotSpot.TopRight2] = new Rect(new Point(rect.Width - size / 2.0d, size / 2.0d), new Size(Math.Max(size, 0), Math.Max(edge * size, 0)));
            mHotSpots[(int)HotSpot.Right] = new Rect(new Point(rect.Width - (size / 2.0d), size * (edge + 0.5d)), new Size(Math.Max(size, 0), Math.Max(rect.Height - (2.0d * (size * (edge + 0.5d))), 0)));
            mHotSpots[(int)HotSpot.BottomRight1] = new Rect(new Point(rect.Width - size / 2.0d, rect.Height - (size * (edge + 0.5d))), new Size(Math.Max(size, 0), Math.Max(edge * size, 0)));
            mHotSpots[(int)HotSpot.BottomRight2] = new Rect(new Point(rect.Width - (size * (edge - 0.5d)), rect.Height - size / 2.0d), new Size(Math.Max(edge * size, 0), Math.Max(size, 0)));
            mHotSpots[(int)HotSpot.Bottom] = new Rect(new Point((size * (edge - 0.5d)), rect.Height - size / 2.0d), new Size(Math.Max(rect.Width - (2.0d * (size * (edge - 0.5d))), 0), Math.Max(size, 0)));
            mHotSpots[(int)HotSpot.BottomLeft1] = new Rect(new Point(-size / 2.0d, rect.Height - size / 2.0d), new Size(Math.Max(edge * size, 0), Math.Max(size, 0)));
            mHotSpots[(int)HotSpot.BottomLeft2] = new Rect(new Point(-size / 2.0d, rect.Height - (size * (edge + 0.5d))), new Size(Math.Max(size, 0), Math.Max(edge * size, 0)));
            mHotSpots[(int)HotSpot.Left] = new Rect(new Point(-size / 2.0d, (size * (edge + 0.5d))), new Size(Math.Max(size, 0), Math.Max(rect.Height - (2.0d * (size * (edge + 0.5d))), 0)));
                    
            for (int i = 0; i < mHotSpots.Length; i++)
            {
                drawingContext.DrawRectangle(new SolidColorBrush(Colors.Transparent), null, mHotSpots[i]);
                //drawingContext.DrawRectangle(new SolidColorBrush(i % 2 == 0 ? Colors.Red : Colors.White), null, mHotSpots[i]); // used to see the bounding rectangle visually
            }
        }

        #endregion 

        #region Private Methods

        /// <summary>
        /// Update the size and location of the object based on the hotspot that is selected.
        /// </summary>
        /// <param name="hspot"></param>
        /// <param name="p"></param>
        private void UpdateSize(HotSpot hspot, Point p)
        {
            double width = (double)AdornedElement.GetValue(WidthProperty);
            double height = (double)AdornedElement.GetValue(HeightProperty);
            double left = Canvas.GetLeft(AdornedElement);
            double top = Canvas.GetTop(AdornedElement);
            if (mWindow != null)
            {
                width = mWindow.Width;
                height = mWindow.Height;
                left = mWindow.Left;
                top = mWindow.Top;                
            }
            
            switch (hspot)
            {
                case HotSpot.TopLeft1:
                case HotSpot.TopLeft2:      
                    UpdateWidth(width + (mCurrentPoint.X - p.X), left + (p.X - mCurrentPoint.X));
                    UpdateHeight(height + (mCurrentPoint.Y - p.Y), top + (p.Y - mCurrentPoint.Y));
                    break;
                case HotSpot.Top:                    
                    UpdateHeight(height + (mCurrentPoint.Y - p.Y), top + (p.Y - mCurrentPoint.Y));
                    break;
                case HotSpot.TopRight1:
                case HotSpot.TopRight2:
                    UpdateWidth(width - (mLastPoint.X - p.X), p);
                    UpdateHeight(height + (mCurrentPoint.Y - p.Y), top + (p.Y - mCurrentPoint.Y));
                    mLastPoint = p; 
                    break;
                case HotSpot.Right:
                    UpdateWidth(width - (mLastPoint.X - p.X), p);
                    mLastPoint = p; 
                    break;
                case HotSpot.BottomRight1:
                case HotSpot.BottomRight2:
                    UpdateWidth(width - (mLastPoint.X - p.X), p);
                    UpdateHeight(height - (mLastPoint.Y - p.Y), p);
                    mLastPoint = p; 
                    break;
                case HotSpot.Bottom:
                    UpdateHeight(height - (mLastPoint.Y - p.Y), p);
                    mLastPoint = p; 
                    break;
                case HotSpot.BottomLeft1:
                case HotSpot.BottomLeft2:
                    UpdateWidth(width + (mCurrentPoint.X - p.X), left + (p.X - mCurrentPoint.X));
                    UpdateHeight(height - (mLastPoint.Y - p.Y), p);
                    mLastPoint = p; 
                    break;
                case HotSpot.Left:
                    UpdateWidth(width + (mCurrentPoint.X - p.X), left + (p.X - mCurrentPoint.X));
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Updates the cursor based on the hotspot that is currently selected or hovered over.
        /// </summary>
        /// <param name="hspot"></param>
        private void UpdateCursor(HotSpot hspot)
        {
            switch (hspot)
            {
                case HotSpot.TopLeft1:
                case HotSpot.TopLeft2:
                    this.Cursor = Cursors.SizeNWSE;
                    break;
                case HotSpot.Top:
                    this.Cursor = Cursors.SizeNS;
                    break;
                case HotSpot.TopRight1:
                case HotSpot.TopRight2:
                    this.Cursor = Cursors.SizeNESW;
                    break;
                case HotSpot.Right:
                    this.Cursor = Cursors.SizeWE;
                    break;
                case HotSpot.BottomRight1:
                case HotSpot.BottomRight2:
                    this.Cursor = Cursors.SizeNWSE;
                    break;
                case HotSpot.Bottom:
                    this.Cursor = Cursors.SizeNS;
                    break;
                case HotSpot.BottomLeft1:
                case HotSpot.BottomLeft2:
                    this.Cursor = Cursors.SizeNESW;
                    break;
                case HotSpot.Left:
                    this.Cursor = Cursors.SizeWE;
                    break;
                default:
                    this.Cursor = Cursors.Arrow;
                    break;
            }
        }

        /// <summary>
        /// Updates the width to the newwidth provided based on the current mouse point.
        /// </summary>
        /// <param name="newwidth"></param>
        /// <param name="p"></param>
        private void UpdateWidth(double newwidth, Point p)
        {
            if (AdornedElement.GetType().Equals(typeof(MwiChild)))
                mMaxWidth = ((MwiChild)AdornedElement).MwiParent.ActualWidth - 3 - Canvas.GetLeft(AdornedElement);

            // if within the min and max width then update the width to the new width
            if ((newwidth >= mMinWidth && p.X >= mMinWidth) &&
                (newwidth <= mMaxWidth && p.X <= mMaxWidth))
            {
                if (mWindow != null)
                    mWindow.Width = newwidth;
                else                    
                    AdornedElement.SetValue(MwiChild.WidthProperty, newwidth);
            }
            else // otherwise set the width to the min or max bound accordingly
            {
                if (mWindow != null)
                {
                    if (!(newwidth <= mMaxWidth && p.X <= mMaxWidth))
                        mWindow.Width = mMaxWidth;
                    else
                        mWindow.Width = mMinWidth;
                }
                else
                {
                    if (!(newwidth <= mMaxWidth && p.X <= mMaxWidth))
                        AdornedElement.SetValue(MwiChild.WidthProperty, mMaxWidth);
                    else
                        AdornedElement.SetValue(MwiChild.WidthProperty, mMinWidth);
                }
            }
        }

        /// <summary>
        /// Update the width to the newwidth and the left location to newleft.
        /// </summary>
        /// <param name="newwidth"></param>
        /// <param name="newleft"></param>
        private void UpdateWidth(double newwidth, double newleft)
        {
            // if within the min width and a location greater than or equal to zero then update the width and the left location
            if ((newwidth >= mMinWidth) &&
                (newleft >= 0))
            {
                if (mWindow != null)
                {
                    mWindow.SetValue(Window.WidthProperty, newwidth); 
                    mWindow.SetValue(Window.LeftProperty, newleft); 
                }
                else
                {
                    Canvas.SetLeft(AdornedElement, newleft);
                    AdornedElement.SetValue(MwiChild.WidthProperty, newwidth);
                }                
            }
            else // otherwise set the width the the min or max bound accordingly
            {
                if (mWindow != null)
                {
                    double width = mWindow.Width;
                    double left = mWindow.Left;
                    if (!(newleft >= 0))
                    {
                        mWindow.Left = 0;
                        mWindow.Width = left + width;
                    }
                    else
                    {
                        mWindow.Left += (mWindow.Width - mMinWidth);
                        mWindow.Width = mMinWidth;
                    }
                }
                else
                {
                    double width = (double)AdornedElement.GetValue(WidthProperty);
                    double left = Canvas.GetLeft(AdornedElement);
                    if (!(newleft >= 0))
                    {
                        Canvas.SetLeft(AdornedElement, 0);
                        AdornedElement.SetValue(MwiChild.WidthProperty, left + width);
                    }
                    else
                    {
                        Canvas.SetLeft(AdornedElement, left + (width - mMinWidth));
                        AdornedElement.SetValue(MwiChild.WidthProperty, mMinWidth);
                    }
                }                
            }
        }

        /// <summary>
        /// Update the height with the newheight based on the current mouse point.
        /// </summary>
        /// <param name="newheight"></param>
        /// <param name="p"></param>
        private void UpdateHeight(double newheight, Point p)
        {
            if (AdornedElement.GetType().Equals(typeof(MwiChild)))
                mMaxHeight = ((MwiChild)AdornedElement).MwiParent.ActualHeight - 3 - Canvas.GetTop(AdornedElement) ;

            // if within the min and max height then update the height to the new height
            if ((newheight >= mMinHeight && p.Y >= mMinHeight) && 
                (newheight <= mMaxHeight && p.Y <= mMaxHeight))
            {
                if (mWindow != null)
                    mWindow.Height = newheight;
                else
                    AdornedElement.SetValue(MwiChild.HeightProperty, newheight);
            }
            else // otherwise set the height to the min or max bound accordingly
            {
                if (mWindow != null)
                {
                    if (!(newheight <= mMaxHeight && p.Y <= mMaxHeight))
                        mWindow.Height = mMaxHeight;
                    else
                        mWindow.Height = mMinHeight;
                }
                else
                {
                    if (!(newheight <= mMaxHeight && p.Y <= mMaxHeight))
                        AdornedElement.SetValue(MwiChild.HeightProperty, mMaxHeight);
                    else
                        AdornedElement.SetValue(MwiChild.HeightProperty, mMinHeight);
                }
            }
        }

        /// <summary>
        /// Update the height to the newheight and the top location to newtop.
        /// </summary>
        /// <param name="newwidth"></param>
        /// <param name="newleft"></param>
        private void UpdateHeight(double newheight, double newtop)
        {
            // if within the min height and a location greater than or equal to zero then update the height and the top location
            if ((newheight >= mMinHeight) &&
                (newtop >= 0))
            {
                if (mWindow != null)
                {                    
                    mWindow.Height = newheight;
                    mWindow.Top = newtop;
                }
                else
                {
                    Canvas.SetTop(AdornedElement, newtop);
                    AdornedElement.SetValue(MwiChild.HeightProperty, newheight);
                }
            }
            else // otherwise set the height the the min or max bound accordingly
            {
                if (mWindow != null)
                {
                    double height = mWindow.Height;
                    double top = mWindow.Top;
                    if (!(newtop >= 0))
                    {
                        mWindow.Top = 0;
                        mWindow.Height = top + height;
                    }
                    else
                    {
                        mWindow.Top += mWindow.Height - mMinHeight;
                        mWindow.Height = mMinHeight;
                    }
                }
                else
                {
                    double height = (double)AdornedElement.GetValue(HeightProperty);
                    double top = Canvas.GetTop(AdornedElement);
                    if (!(newtop >= 0))
                    {
                        Canvas.SetTop(AdornedElement, 0);
                        AdornedElement.SetValue(MwiChild.HeightProperty, top + height);
                    }
                    else
                    {
                        Canvas.SetTop(AdornedElement, top + (height - mMinHeight));
                        AdornedElement.SetValue(MwiChild.HeightProperty, mMinHeight);
                    }
                }
            }            
        }

        #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
Unknown
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions