65.9K
CodeProject is changing. Read more.
Home

Drawing Resizable Controls at Runtime

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.12/5 (14 votes)

Mar 4, 2008

CPOL

1 min read

viewsIcon

60783

downloadIcon

6785

How to draw resizable controls at runtime

resizablecontrols.gif

Introduction

This article will show how to draw borders around graphical objects and place them on the form, so users can manipulate the objects by dragging them around or resizing the controls. I hope this helps someone.

Background

I am involved in a project in which one tiny aspect of the application is where end users can place various objects on the forms and manipulate their properties. The application was developed in Delphi and I was curious what it would take to do something similar in .NET.

Using the Code

First, define the size of the drag handle that will appear around the control.

const int DRAG_HANDLE_SIZE = 7;

The method to actually draw the borders and the drag handles is as follows:

/// <summary>
/// Draw a border and drag handles around the control, on mouse down and up.
/// </summary>
/// <param name=""sender"" ></param >
private void DrawControlBorder(object sender)
{
    Control control = (Control)sender;
    //define the border to be drawn, it will be offset by DRAG_HANDLE_SIZE / 2
    //around the control, so when the drag handles are drawn they will seem to be
    //connected in the middle.
    Rectangle Border = new Rectangle(
        new Point(control.Location.X - DRAG_HANDLE_SIZE / 2, 
            control.Location.Y - DRAG_HANDLE_SIZE / 2), 
        new Size(control.Size.Width + DRAG_HANDLE_SIZE, 
            control.Size.Height + DRAG_HANDLE_SIZE));
    //define the 8 drag handles, that has the size of DRAG_HANDLE_SIZE
    Rectangle NW = new Rectangle(
        new Point(control.Location.X - DRAG_HANDLE_SIZE, 
            control.Location.Y - DRAG_HANDLE_SIZE), 
        new Size(DRAG_HANDLE_SIZE, DRAG_HANDLE_SIZE));
    Rectangle N = new Rectangle(
        new Point(control.Location.X + control.Width / 2 - DRAG_HANDLE_SIZE/2, 
            control.Location.Y - DRAG_HANDLE_SIZE), 
        new Size(DRAG_HANDLE_SIZE, DRAG_HANDLE_SIZE));
    Rectangle NE = new Rectangle(
        new Point(control.Location.X + control.Width, 
            control.Location.Y - DRAG_HANDLE_SIZE), 
        new Size(DRAG_HANDLE_SIZE, DRAG_HANDLE_SIZE));
    Rectangle W = new Rectangle(
        new Point(control.Location.X - DRAG_HANDLE_SIZE, 
            control.Location.Y + control.Height/2-DRAG_HANDLE_SIZE/2), 
        new Size(DRAG_HANDLE_SIZE, DRAG_HANDLE_SIZE));
    Rectangle E = new Rectangle(
        new Point(control.Location.X + control.Width, 
            control.Location.Y + control.Height / 2 - DRAG_HANDLE_SIZE / 2), 
        new Size(DRAG_HANDLE_SIZE, DRAG_HANDLE_SIZE));
    Rectangle SW = new Rectangle(
        new Point(control.Location.X - DRAG_HANDLE_SIZE, 
            control.Location.Y + control.Height), 
        new Size(DRAG_HANDLE_SIZE, DRAG_HANDLE_SIZE));
    Rectangle S = new Rectangle(
        new Point(control.Location.X + control.Width / 2 - DRAG_HANDLE_SIZE/2, 
            control.Location.Y + control.Height), 
        new Size(DRAG_HANDLE_SIZE, DRAG_HANDLE_SIZE));
    Rectangle SE = new Rectangle(
        new Point(control.Location.X + control.Width, 
            control.Location.Y + control.Height), 
        new Size(DRAG_HANDLE_SIZE, DRAG_HANDLE_SIZE));

    //get the form graphic
    Graphics g = this.CreateGraphics();
    //draw the border and drag handles
    ControlPaint.DrawBorder(g, Border, Color.Gray, ButtonBorderStyle.Dotted);
    ControlPaint.DrawGrabHandle(g, NW, true, true);
    ControlPaint.DrawGrabHandle(g, N, true, true);
    ControlPaint.DrawGrabHandle(g, NE, true, true);
    ControlPaint.DrawGrabHandle(g, W, true, true);
    ControlPaint.DrawGrabHandle(g, E, true, true);
    ControlPaint.DrawGrabHandle(g, SW, true, true);
    ControlPaint.DrawGrabHandle(g, S, true, true);
    ControlPaint.DrawGrabHandle(g, SE, true, true);
    g.Dispose();
}

Next, implement the various events of the Control object, for example, when I click on a control, I want a border and drag handles drawn around it, so in the MouseDown event:

private void control_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        this.Invalidate();  //unselect other control
        SelectedControl = (Control)sender;
        Control control = (Control)sender;
        mouseX = -e.X;
        mouseY = -e.Y;
        control.Invalidate();
        DrawControlBorder(sender);
        propertyGrid1.SelectedObject = SelectedControl;
    }  
}

Finally, to use it, create a control at runtime and have it subscribe to the various events, for example to create a button:

Button ctrl = new Button();
ctrl.Text = "New Button";
ctrl.Location = new Point(50, 50);
ctrl.MouseEnter += new EventHandler(control_MouseEnter);
ctrl.MouseLeave += new EventHandler(control_MouseLeave);
ctrl.MouseDown += new MouseEventHandler(control_MouseDown);
ctrl.MouseMove += new MouseEventHandler(control_MouseMove);
ctrl.MouseUp += new MouseEventHandler(control_MouseUp);
Controls.Add(ctrl);

That's it. When executed, you can move the objects around, resize them in all 8 directions.

Points of Interest

If you download the source code, you'll notice that I use a timer to track the movement of the cursor. I tried to use the MouseHover event of the form, but that executed only once when the cursor is over it or when it leaves one of the controls, and subsequently when the cursor is over one of the drag handles it will fail to recognize it. Using a timer does solve this problem, but if you have found a better way please let me know!

History

  • 3/4/2008: First released