Click here to Skip to main content
Click here to Skip to main content

Runtime Control Resizer

, 19 Jan 2007
Rate this:
Please Sign up or sign in to vote.
An unfinished class that allows for runtime resizing of controls.

Sample Image - rtcontrolresizer.jpg

Introduction

An unfinished, simple class I started to write in response to a recent post on the microsoft.public.dotnet.languages.vb newsgroup.

Background

Not too long ago, I was browsing the VB.NET newsgroup like I normally do, and ran across a question that stumped me. The question was on how to provide the ability to resize a control during runtime. It seemed fairly straightforward, so I fired up C# (forgetting which newsgroup I read the question in!) and started typing up a quick solution - and it failed.

So I spent more and more time tweaking this late at night, and got most things worked out. Eventually, the OP stated he found a solution and wrote a CodeProject article about it. "Ah hah," I said, "I'll do the same!". And so I quit working on the code, and figured this would at least get someone else started. (Maybe, I'll finish it later - who knows?)

The Principle Components

The User Control

Nothing too complex here. I just needed a control that would represent the little white boxes that "float" around the outside of the control to be resized. The main job of the control is to just change the mouse cursor based on the position of the control (top left, top, etc.).

The Controller Class

Here, things get a little more verbose, but still not too complex. In a few sentences, the controller class first creates the necessary resize boxes (the user controls) and places them around the target control. The rest of the class simply handles the mouse-move events, and resizes the target control based on the movement of the specific resize box.

The Resize Box's Code

First off, I created an enum that listed all the available positions (relative to the target) that the resize box could have. Then, of course, I used this enum as a parameter in the control's constructor.

public enum BoxPosition
{
    Top,
    Bottom,
    Left,
    Right,
    TopLeft,
    TopRight,
    BottomLeft,
    BottomRight
}

public ResizeBox(BoxPosition position)
{
    InitializeComponent();
    this.Position = position;
}

OK, nothing complex there. Next up, I handled both the MouseEnter and MouseLeave events of the control in order to set the appropriate arrow cursor depending on the Position property (not shown) of the control. Sounds like a job for a switch statement to me!

private void ResizeBox_MouseEnter(object sender, EventArgs e)
{
    switch (this.Position)
    {
        case BoxPosition.Top:
            this.Cursor = Cursors.SizeNS;
            break;
        case BoxPosition.Bottom:
            this.Cursor = Cursors.SizeNS;
            break;
        case BoxPosition.Left:
            this.Cursor = Cursors.SizeWE;
            break;
        case BoxPosition.Right:
            this.Cursor = Cursors.SizeWE;
            break;
        case BoxPosition.TopLeft:
            this.Cursor = Cursors.SizeNWSE;
            break;
        case BoxPosition.BottomRight:
            this.Cursor = Cursors.SizeNWSE;
            break;
        case BoxPosition.TopRight:
            this.Cursor = Cursors.SizeNESW;
            break;
        case BoxPosition.BottomLeft:
            this.Cursor = Cursors.SizeNESW;
            break;
        default:
            this.Cursor = Cursors.No;
            break;
    }
}

private void ResizeBox_MouseLeave(object sender, EventArgs e)
{
    this.Cursor = Cursors.Default;
}

That pretty much takes care of the juicy bits for the user control - like I said, it's pretty simple.

The Controller Class

First of all, I need to create some variables to represent the actual resize boxes I'll place around the target.

private ResizeBox topBox;
private ResizeBox bottomBox;
private ResizeBox leftBox;
private ResizeBox rightBox;
private ResizeBox topLeftBox;
private ResizeBox topRightBox;
private ResizeBox bottomLeftBox;
private ResizeBox bottomRightBox;

In the constructor, the target control is passed as a parameter. I set this into a property, instantiate the ResizeBox objects, and map the the necessary events to the methods that will handle them. The constructor also provides an option to immediately show the resize handles.

public ResizeControl(Control target, Boolean showResizeBoxes)
{
    this._Target = target;

    target.Parent.Paint += new PaintEventHandler(Parent_Paint);

    topBox = new ResizeBox(ResizeBox.BoxPosition.Top);
    bottomBox = new ResizeBox(ResizeBox.BoxPosition.Bottom);
    leftBox = new ResizeBox(ResizeBox.BoxPosition.Left);
    rightBox = new ResizeBox(ResizeBox.BoxPosition.Right);
    topLeftBox = new ResizeBox(ResizeBox.BoxPosition.TopLeft);
    topRightBox = new ResizeBox(ResizeBox.BoxPosition.TopRight);
    bottomLeftBox = new ResizeBox(ResizeBox.BoxPosition.BottomLeft);
    bottomRightBox = new ResizeBox(ResizeBox.BoxPosition.BottomRight);

    this.topLeftBox.MouseDown += new MouseEventHandler(Boxes_MouseDown);
    this.topBox.MouseDown += new MouseEventHandler(Boxes_MouseDown);
    this.topRightBox.MouseDown += new MouseEventHandler(Boxes_MouseDown);
    this.leftBox.MouseDown += new MouseEventHandler(Boxes_MouseDown);
    this.rightBox.MouseDown += new MouseEventHandler(Boxes_MouseDown);
    this.bottomLeftBox.MouseDown += new MouseEventHandler(Boxes_MouseDown);
    this.bottomBox.MouseDown += new MouseEventHandler(Boxes_MouseDown);
    this.bottomRightBox.MouseDown += new MouseEventHandler(Boxes_MouseDown);

    this.topLeftBox.MouseMove += new MouseEventHandler(topLeftBox_MouseMove);
    this.topBox.MouseMove += new MouseEventHandler(topBox_MouseMove);
    this.topRightBox.MouseMove += new MouseEventHandler(topRightBox_MouseMove);
    this.leftBox.MouseMove += new MouseEventHandler(leftBox_MouseMove);
    this.rightBox.MouseMove += new MouseEventHandler(rightBox_MouseMove);
    this.bottomLeftBox.MouseMove += 
         new MouseEventHandler(bottomLeftBox_MouseMove);
    this.bottomBox.MouseMove += new MouseEventHandler(bottomBox_MouseMove);
    this.bottomRightBox.MouseMove += 
         new MouseEventHandler(bottomRightBox_MouseMove);

    if (showResizeBoxes)
        ShowResizeBoxes();
}

Next is the code for the main methods for the controller, the second of which you'll see is empty. Like I said, I never finished the class, just the main parts.

public void ShowResizeBoxes()
{
    PositionTopLeftBox();
    PositionTopBox();
    PositionTopRightBox();
    PositionLeftBox();
    PositionRightBox();
    PositionBottomLeftBox();
    PositionBottomBox();
    PositionBottomRightBox();
    Target.Parent.Controls.Add(topBox);
    Target.Parent.Controls.Add(bottomBox);
    Target.Parent.Controls.Add(leftBox);
    Target.Parent.Controls.Add(rightBox);
    Target.Parent.Controls.Add(topLeftBox);
    Target.Parent.Controls.Add(topRightBox);
    Target.Parent.Controls.Add(bottomLeftBox);
    Target.Parent.Controls.Add(bottomRightBox);
}

public void HideResizeBoxes()
{

}

As you can see, the ShowResizeBoxes() starts out by calling the position commands for all of the resize boxes and then adding the controls to the parent of the target control. They're basically all the same, so I'll just post the code for one.

private void PositionTopLeftBox()
{
    topLeftBox.Top = Target.Top - topLeftBox.Height - 1;
    topLeftBox.Left = Target.Left - topLeftBox.Width - 1;
}

As you saw above, the MouseDown events for all of the ResizeBoxes is mapped to the same method. This method just sets the mouse location point for tracking movement.

private Point mouseLocation;

private void Boxes_MouseDown(object sender, MouseEventArgs e)
{
    mouseLocation.X = e.X;
    mouseLocation.Y = e.Y;
}

And now for the heart of the class, the methods that actually resize the target control. Let's take the top-box as an example:

First and foremost, we need to make sure the left mouse button was the one that was clicked, so we use a simple if block. Next, I set three variables:

if (e.Button == MouseButtons.Left)
{
    Int32 newBoxTop = topBox.Top + (e.Y - mouseLocation.Y);
    Int32 oldTargetTop = Target.Top;
    Int32 newTargetHeight = Target.Height + 
         (Target.Top - (topBox.Top + topBox.Height + 1));

The first variable, newBoxTop, will be used to check whether the size of the control will increase or decrease in size. The second, oldTargetTop, just saves the current top value so later when we change the top value of the target, we will know how much we should increase/decrease the height value by. Finally, newTargetHeight is the value that the height value of the target will be after we resize it. I set it here so I can check its value to prevent the target from going below a certain size. The minimum height and width values, I had wanted to put in a property, but I was lazy and just hard-coded them in. Here's, the if block that makes sure we don't drop below the minimum size, and the code that sets the new values and re-positions the effected resize-boxes:

if (newTargetHeight > 15 || newBoxTop <= topBox.Top)
{
    Target.Top = newBoxTop + topBox.Height + 1;
    Target.Height += (oldTargetTop - Target.Top);
    topBox.Top = newBoxTop;
    PositionTopLeftBox();
    PositionTopRightBox();
    PositionLeftBox();
    PositionRightBox();
}
Target.Parent.Invalidate(new Rectangle(Target.Left - 6, 
       Target.Top - 6, Target.Width + 12, Target.Height + 12));

Here's the code for the entire method:

private void topBox_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        Int32 newBoxTop = topBox.Top + (e.Y - mouseLocation.Y);
        Int32 oldTargetTop = Target.Top;
        Int32 newTargetHeight = Target.Height + 
              (Target.Top - (topBox.Top + topBox.Height + 1));
        
        if (newTargetHeight > 15 || newBoxTop <= topBox.Top)
        {
            Target.Top = newBoxTop + topBox.Height + 1;
            Target.Height += (oldTargetTop - Target.Top);
            topBox.Top = newBoxTop;
            PositionTopLeftBox();
            PositionTopRightBox();
            PositionLeftBox();
            PositionRightBox();
        }
        Target.Parent.Invalidate(new Rectangle(Target.Left - 6, 
               Target.Top - 6, Target.Width + 12, Target.Height + 12));
    }
}

The one call I didn't explain earlier is the Target.Parent.Invalidate(...) call. You see, I'm trapping the target's parent's Paint event in order to use some simple GDI+ to draw the dotted line connecting the ResizeBoxes.

public void Parent_Paint(object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;
    Pen pen = new Pen(Brushes.Black, 1);
    pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
    g.DrawRectangle(pen, new Rectangle(Target.Left - 3, 
          Target.Top - 3, Target.Width + 6, Target.Height + 6));
}

And that pretty much sums up the class! Basically, the user control handles the cursors, and the mouse-move events handle the actual resizing of the target control and re-positioning the resize boxes. Then, invalidating the region containing the target control forces the updating of the dotted line.

Using the Class

Nothing complex here, just create an instance of the class and pass the control you want to be resizable to the constructor. Setting the ShowResizeBoxes to show them immediately, or just calling it manually. Also, since the class implements IDisposable, using blocks could come in handy since the Dispose methods calls the HideResizeBoxes method.

new ResizeControl(this.button1, true);

ResizeControl rc = new ResizeControl(this.button1, false);
rc.ShowResizeBoxes();

using (new ResizeControl(this.button1, true))
{
    //Do whatever you want here
}

The Problems

Too many boxes!

The class doesn't work well when a control doesn't allow either the height or width property to be set. A good example of this is a single line textbox. Pass that as the target and then drag the bottom resize box down. You'll see what I mean!

Speedy Moves = Unpredictable!

The MouseMove/MouseDown events don't fire near often enough to track fast movements. As a result, the minimum height/width tests can be ignored on fast moves, causing super small controls that may not be recoverable in some cases. Also, quickly "growing" the control can cause missed events that mess up the GDI+ rectangle, and can also leave orphaned or misplaced resize boxes. It seems the boxes move, but the target never resizes and the other resize boxes don't reposition.

The Complete Code

Since I know a lot of people don't want to download a code file, for a quick look-see, I figured I'd include this:

using System;
using System.Drawing;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

class ResizeControl : IDisposable
{
    #region "Private Controls"

    private ResizeBox topBox;
    private ResizeBox bottomBox;
    private ResizeBox leftBox;
    private ResizeBox rightBox;
    private ResizeBox topLeftBox;
    private ResizeBox topRightBox;
    private ResizeBox bottomLeftBox;
    private ResizeBox bottomRightBox;

    private class ResizeBox : UserControl
    {
        public enum BoxPosition
        {
            Top,
            Bottom,
            Left,
            Right,
            TopLeft,
            TopRight,
            BottomLeft,
            BottomRight
        }

        public ResizeBox(BoxPosition position)
        {
            InitializeComponent();
            this.Position = position;
        }

        private System.ComponentModel.IContainer components = null;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.SuspendLayout();
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.BackColor = System.Drawing.Color.White;
            this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.Name = "ResizeBox";
            this.Size = new System.Drawing.Size(6, 6);
            this.MouseEnter += 
                 new System.EventHandler(this.ResizeBox_MouseEnter);
            this.MouseLeave += 
                 new System.EventHandler(this.ResizeBox_MouseLeave);
            this.ResumeLayout(false);
        }

        private BoxPosition _Position = BoxPosition.Top;
        public BoxPosition Position
        {
            get
            {
                return _Position;
            }
            set
            {
                _Position = value;
            }
        }

        private void ResizeBox_MouseEnter(object sender, EventArgs e)
        {
            switch (this.Position)
            {
                case BoxPosition.Top:
                    this.Cursor = Cursors.SizeNS;
                    break;
                case BoxPosition.Bottom:
                    this.Cursor = Cursors.SizeNS;
                    break;
                case BoxPosition.Left:
                    this.Cursor = Cursors.SizeWE;
                    break;
                case BoxPosition.Right:
                    this.Cursor = Cursors.SizeWE;
                    break;
                case BoxPosition.TopLeft:
                    this.Cursor = Cursors.SizeNWSE;
                    break;
                case BoxPosition.BottomRight:
                    this.Cursor = Cursors.SizeNWSE;
                    break;
                case BoxPosition.TopRight:
                    this.Cursor = Cursors.SizeNESW;
                    break;
                case BoxPosition.BottomLeft:
                    this.Cursor = Cursors.SizeNESW;
                    break;
                default:
                    this.Cursor = Cursors.No;
                    break;
            }
        }

        private void ResizeBox_MouseLeave(object sender, EventArgs e)
        {
            this.Cursor = Cursors.Default;
        }
    }

    #endregion

    #region "Public Methods"

    public ResizeControl(Control target, Boolean showResizeBoxes)
    {
        this._Target = target;

        target.Parent.Paint += new PaintEventHandler(Parent_Paint);

        topBox = new ResizeBox(ResizeBox.BoxPosition.Top);
        bottomBox = new ResizeBox(ResizeBox.BoxPosition.Bottom);
        leftBox = new ResizeBox(ResizeBox.BoxPosition.Left);
        rightBox = new ResizeBox(ResizeBox.BoxPosition.Right);
        topLeftBox = new ResizeBox(ResizeBox.BoxPosition.TopLeft);
        topRightBox = new ResizeBox(ResizeBox.BoxPosition.TopRight);
        bottomLeftBox = new ResizeBox(ResizeBox.BoxPosition.BottomLeft);
        bottomRightBox = new ResizeBox(ResizeBox.BoxPosition.BottomRight);

        this.topLeftBox.MouseDown += new MouseEventHandler(Boxes_MouseDown);
        this.topBox.MouseDown += new MouseEventHandler(Boxes_MouseDown);
        this.topRightBox.MouseDown += new MouseEventHandler(Boxes_MouseDown);
        this.leftBox.MouseDown += new MouseEventHandler(Boxes_MouseDown);
        this.rightBox.MouseDown += new MouseEventHandler(Boxes_MouseDown);
        this.bottomLeftBox.MouseDown += 
             new MouseEventHandler(Boxes_MouseDown);
        this.bottomBox.MouseDown += new MouseEventHandler(Boxes_MouseDown);
        this.bottomRightBox.MouseDown += 
             new MouseEventHandler(Boxes_MouseDown);

        this.topLeftBox.MouseMove += 
             new MouseEventHandler(topLeftBox_MouseMove);
        this.topBox.MouseMove += new MouseEventHandler(topBox_MouseMove);
        this.topRightBox.MouseMove += 
             new MouseEventHandler(topRightBox_MouseMove);
        this.leftBox.MouseMove += new MouseEventHandler(leftBox_MouseMove);
        this.rightBox.MouseMove += new MouseEventHandler(rightBox_MouseMove);
        this.bottomLeftBox.MouseMove += 
             new MouseEventHandler(bottomLeftBox_MouseMove);
        this.bottomBox.MouseMove += 
             new MouseEventHandler(bottomBox_MouseMove);
        this.bottomRightBox.MouseMove += 
             new MouseEventHandler(bottomRightBox_MouseMove);

        if (showResizeBoxes)
            ShowResizeBoxes();
    }

    public void Parent_Paint(object sender, PaintEventArgs e)
    {
        Graphics g = e.Graphics;
        Pen pen = new Pen(Brushes.Black, 1);
        pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
        g.DrawRectangle(pen, new Rectangle(Target.Left - 3, 
              Target.Top - 3, Target.Width + 6, Target.Height + 6));
    }

    public void ShowResizeBoxes()
    {
        PositionTopLeftBox();
        PositionTopBox();
        PositionTopRightBox();
        PositionLeftBox();
        PositionRightBox();
        PositionBottomLeftBox();
        PositionBottomBox();
        PositionBottomRightBox();
        Target.Parent.Controls.Add(topBox);
        Target.Parent.Controls.Add(bottomBox);
        Target.Parent.Controls.Add(leftBox);
        Target.Parent.Controls.Add(rightBox);
        Target.Parent.Controls.Add(topLeftBox);
        Target.Parent.Controls.Add(topRightBox);
        Target.Parent.Controls.Add(bottomLeftBox);
        Target.Parent.Controls.Add(bottomRightBox);
    }

    public void HideResizeBoxes()
    {
        topBox.Visible = false;
    }

    void IDisposable.Dispose()
    {
        HideResizeBoxes();
    }

    #endregion

    #region "Move Event Handlers"

    private Point mouseLocation;

    private void Boxes_MouseDown(object sender, MouseEventArgs e)
    {
        mouseLocation.X = e.X;
        mouseLocation.Y = e.Y;
    }

    private void topLeftBox_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            Int32 newBoxTop = topLeftBox.Top + (e.Y - mouseLocation.Y);
            Int32 oldTargetTop = Target.Top;
            Int32 newTargetHeight = Target.Height + 
                 (Target.Top - (topLeftBox.Top + topLeftBox.Height + 1));
            Int32 newBoxLeft = topLeftBox.Left + (e.X - mouseLocation.X);
            Int32 oldTargetLeft = Target.Left;
            Int32 newTargetWidth = Target.Width + (oldTargetLeft - Target.Left);

            if (newTargetWidth > 30 || newBoxLeft <= topLeftBox.Left)
            {
                Target.Left = newBoxLeft + topLeftBox.Width + 1;
                Target.Width += (oldTargetLeft - Target.Left);
                topLeftBox.Left = newBoxLeft;
                PositionTopLeftBox();
                PositionBottomLeftBox();
                PositionTopBox();
                PositionBottomBox();
                PositionLeftBox();
            }
            if (newTargetHeight > 15 || newBoxTop <= topBox.Top)
            {
                Target.Top = newBoxTop + topLeftBox.Height + 1;
                Target.Height += (oldTargetTop - Target.Top);
                topLeftBox.Top = newBoxTop;
                PositionTopLeftBox();
                PositionTopRightBox();
                PositionLeftBox();
                PositionRightBox();
                PositionTopBox();
            }
            Target.Parent.Invalidate(new Rectangle(Target.Left - 6, 
                   Target.Top - 6, Target.Width + 12, Target.Height + 12));
        }
    }

    private void topBox_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            Int32 newBoxTop = topBox.Top + (e.Y - mouseLocation.Y);
            Int32 oldTargetTop = Target.Top;
            Int32 newTargetHeight = Target.Height + 
                 (Target.Top - (topBox.Top + topBox.Height + 1));
            
            if (newTargetHeight > 15 || newBoxTop <= topBox.Top)
            {
                Target.Top = newBoxTop + topBox.Height + 1;
                Target.Height += (oldTargetTop - Target.Top);
                topBox.Top = newBoxTop;
                PositionTopLeftBox();
                PositionTopRightBox();
                PositionLeftBox();
                PositionRightBox();
            }
            Target.Parent.Invalidate(new Rectangle(Target.Left - 6, 
                   Target.Top - 6, Target.Width + 12, Target.Height + 12));
        }
    }

    private void topRightBox_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            Int32 newBoxTop = topRightBox.Top + (e.Y - mouseLocation.Y);
            Int32 oldTargetTop = Target.Top;
            Int32 newTargetHeight = Target.Height + 
                  (Target.Top - (topRightBox.Top + topRightBox.Height + 1));
            Int32 newBoxLeft = topRightBox.Left + (e.X - mouseLocation.X);
            Int32 newTargetWidth = topRightBox.Left - Target.Left - 1;

            if (newTargetWidth > 30 || newBoxLeft >= topRightBox.Left)
            {
                Target.Width = newTargetWidth;
                topRightBox.Left = newBoxLeft;
                PositionBottomRightBox();
                PositionTopBox();
                PositionBottomBox();
            }
            if (newTargetHeight > 15 || newBoxTop <= topRightBox.Top)
            {
                Target.Top = newBoxTop + topRightBox.Height + 1;
                Target.Height += (oldTargetTop - Target.Top);
                topRightBox.Top = newBoxTop;
                PositionTopLeftBox();
                PositionLeftBox();
                PositionRightBox();
            }
            Target.Parent.Invalidate(new Rectangle(Target.Left - 6, 
                   Target.Top - 6, Target.Width + 12, Target.Height + 12));
        }
    }

    private void leftBox_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            Int32 newBoxLeft = leftBox.Left + (e.X - mouseLocation.X);
            Int32 oldTargetLeft = Target.Left;
            Int32 newTargetWidth = Target.Width + (oldTargetLeft - Target.Left);

            if (newTargetWidth > 30 || newBoxLeft <= leftBox.Left)
            {
                Target.Left = newBoxLeft + leftBox.Width + 1;
                Target.Width += (oldTargetLeft - Target.Left);
                leftBox.Left = newBoxLeft;
                PositionTopLeftBox();
                PositionBottomLeftBox();
                PositionTopBox();
                PositionBottomBox();
            }
            Target.Parent.Invalidate(new Rectangle(Target.Left - 6, 
                   Target.Top - 6, Target.Width + 12, Target.Height + 12));
        }
    }

    private void rightBox_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            Int32 newBoxLeft = rightBox.Left + (e.X - mouseLocation.X);
            Int32 newTargetWidth = rightBox.Left - Target.Left - 1;

            if (newTargetWidth > 30 || newBoxLeft >= rightBox.Left)
            {
                Target.Width = newTargetWidth;
                rightBox.Left = newBoxLeft;
                PositionTopRightBox();
                PositionBottomRightBox();
                PositionTopBox();
                PositionBottomBox();
            }
            Target.Parent.Invalidate(new Rectangle(Target.Left - 6, 
                   Target.Top - 6, Target.Width + 12, Target.Height + 12));
        }
    }

    private void bottomLeftBox_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            Int32 newBoxTop = bottomLeftBox.Top + (e.Y - mouseLocation.Y);
            Int32 newTargetHeight = bottomLeftBox.Top - Target.Top - 1;
            Int32 newBoxLeft = bottomLeftBox.Left + (e.X - mouseLocation.X);
            Int32 oldTargetLeft = Target.Left;
            Int32 newTargetWidth = Target.Width + (oldTargetLeft - Target.Left);

            if (newTargetWidth > 30 || newBoxLeft <= bottomLeftBox.Left)
            {
                Target.Left = newBoxLeft + bottomLeftBox.Width + 1;
                Target.Width += (oldTargetLeft - Target.Left);
                bottomLeftBox.Left = newBoxLeft;
                PositionTopLeftBox();
                PositionTopBox();
                PositionBottomBox();
            }
            if (newTargetHeight > 15 || newBoxTop >= bottomLeftBox.Top)
            {
                Target.Height = newTargetHeight;
                bottomLeftBox.Top = newBoxTop;
                PositionBottomRightBox();
                PositionLeftBox();
                PositionRightBox();
            }
            Target.Parent.Invalidate(new Rectangle(Target.Left - 6, 
                   Target.Top - 6, Target.Width + 12, Target.Height + 12));
        }
    }

    private void bottomBox_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            Int32 newBoxTop = bottomBox.Top + (e.Y - mouseLocation.Y);
            Int32 newTargetHeight = bottomBox.Top - Target.Top - 1;

            if (newTargetHeight > 15 || newBoxTop >= bottomBox.Top)
            {
                Target.Height = newTargetHeight;
                bottomBox.Top = newBoxTop;
                PositionBottomLeftBox();
                PositionBottomRightBox();
                PositionLeftBox();
                PositionRightBox();
            }
            Target.Parent.Invalidate(new Rectangle(Target.Left - 6, 
                   Target.Top - 6, Target.Width + 12, Target.Height + 12));
        }
    }

    private void bottomRightBox_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            Int32 newBoxTop = bottomRightBox.Top + (e.Y - mouseLocation.Y);
            Int32 newTargetHeight = bottomRightBox.Top - Target.Top - 1;
            Int32 newBoxLeft = bottomRightBox.Left + (e.X - mouseLocation.X);
            Int32 newTargetWidth = bottomRightBox.Left - Target.Left - 1;

            if (newTargetWidth > 30 || newBoxLeft >= bottomRightBox.Left)
            {
                Target.Width = newTargetWidth;
                bottomRightBox.Left = newBoxLeft;
                PositionTopRightBox();
                PositionTopBox();
                PositionBottomBox();
            }
            if (newTargetHeight > 15 || newBoxTop >= bottomRightBox.Top)
            {
                Target.Height = newTargetHeight;
                bottomRightBox.Top = newBoxTop;
                PositionBottomLeftBox();
                PositionLeftBox();
                PositionRightBox();
            }
            Target.Parent.Invalidate(new Rectangle(Target.Left - 6, 
                   Target.Top - 6, Target.Width + 12, Target.Height + 12));
        }
    }

    #endregion

    #region "Positioning Commands"

    private void PositionTopLeftBox()
    {
        topLeftBox.Top = Target.Top - topLeftBox.Height - 1;
        topLeftBox.Left = Target.Left - topLeftBox.Width - 1;
    }

    private void PositionTopBox()
    {
        topBox.Top = Target.Top - topBox.Height - 1;
        topBox.Left = Target.Left + (Target.Width / 2) - (topBox.Width / 2);
    }

    private void PositionTopRightBox()
    {
        topRightBox.Top = Target.Top - topRightBox.Height - 1;
        topRightBox.Left = Target.Left + Target.Width + 1;
    }

    private void PositionLeftBox()
    {
        leftBox.Top = Target.Top + (Target.Height / 2) - (leftBox.Height / 2);
        leftBox.Left = Target.Left - leftBox.Width - 1;
    }

    private void PositionRightBox()
    {
        rightBox.Top = Target.Top + (Target.Height / 2) - (rightBox.Height / 2);
        rightBox.Left = Target.Left + Target.Width + 1;
    }

    private void PositionBottomLeftBox()
    {
        bottomLeftBox.Top = Target.Top + Target.Height + 1;
        bottomLeftBox.Left = Target.Left - leftBox.Width - 1;
    }

    private void PositionBottomBox()
    {
        bottomBox.Top = Target.Top + Target.Height + 1;
        bottomBox.Left = Target.Left + 
                 (Target.Width / 2) - (bottomBox.Width / 2);
    }

    private void PositionBottomRightBox()
    {
        bottomRightBox.Top = Target.Top + Target.Height + 1;
        bottomRightBox.Left = Target.Left + Target.Width + 1;
    }

    #endregion

    #region "Properties"

    private Control _Target;
    public Control Target
    {
        get
        {
            if (_Target == null)
                _Target = new Control();
            return _Target;
        }
    }

    #endregion
}

History

  • 1/19/2007 - First release.

License

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

About the Author

Seth Rowe
Web Developer
United States United States
While young, at only 22 years of age, I constantly am trying to improve my programming skill set. I currently work for an IT technology leader as an ASP.NET/.NET developer in Columbus, Ohio. I was also awarded Microsoft MVP status for Visual Basic for the first time in 2008. It is a great honor to join the team of MVP's worldwide.
 
Apart from programming, I spend all my time with my lovely and supportive wife (who is carrying our first child). Besides from programming, you can also find me running around the virtual world of World of Warcraft.

Comments and Discussions

 
GeneralHmm: I downloaded and tried but... Pinmembertwesterd6-Jun-08 22:13 
GeneralRe: Hmm: I downloaded and tried but... Pinmembertwesterd6-Jun-08 22:18 
GeneralRe: Hmm: I downloaded and tried but... PinmemberSeth Rowe7-Jun-08 2:39 
GeneralRe: Hmm: I downloaded and tried but... Pinmembertwesterd7-Jun-08 7:20 
GeneralRe: Hmm: I downloaded and tried but... PinmemberSeth Rowe7-Jun-08 15:18 
GeneralRe: Hmm: I downloaded and tried but... Pinmembertwesterd8-Jun-08 0:35 
I FOUND THE ANSWER!!
 
Very long story short: This is a Windows problem. Any control (not a form) trying to resize within a MouseMove event that contains will receive two paint messages. It is virtually impossible using regular code to get rid of ghosting/ flicker. You can if you control the entire Graphics, but if you create a custom UserControl and drag other controls onto it - forget it!
 
However, double messages are NOT sent to forms... thus, the answer is to send messages that tell .NET to treat the control as a form, not a control.
 
This is where I found the answer:
 
http://slingkid.blogsome.com/2007/08/17/resizing-and-dragging/[^]
 
There is a link to a VS solution that demonstrates how to resize controls properly within the link above.
 
Here is the direct link to the solution:
 
http://teleios.bz/download/SizablePictureBox.zip[^]
 

Please, try it - it's absolutely beautiful!
 
I have now added this code to a UserControl and any UserControl implementing this code can now be resized and moved at runtime by the end-user flicker-free.
 
SWEET!!
GeneralRe: Hmm: I downloaded and tried but... PinmemberSeth Rowe8-Jun-08 5:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140721.1 | Last Updated 19 Jan 2007
Article Copyright 2007 by Seth Rowe
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid