2

I want to add some extensions like move, resize,... to PictureBox, Label, Panel like this:

public class LiveControl: PictureBox
{
    private Point cur = new Point(0, 0);
    public LiveControl()
    {
        ResizeRedraw = true;
        MouseDown += (s, e) => { cur = new Point(e.X, e.Y); };
        MouseMove += (s, e) => {
            if (e.Button == MouseButtons.Left)
            {
                Control x = (Control)s;
                x.SuspendLayout();
                x.Location = new Point(x.Left + e.X - cur.X, x.Top + e.Y - cur.Y);
                x.ResumeLayout();
            }
        };
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        var rc = new Rectangle(this.ClientSize.Width - grab, this.ClientSize.Height - grab, grab, grab);
        ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, rc);
    }
    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        if (m.Msg == 0x84)
        {  
            var pos = this.PointToClient(new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16));
            if (pos.X >= this.ClientSize.Width - grab && pos.Y >= this.ClientSize.Height - grab)
                m.Result = new IntPtr(17);  
        }
    }
    private const int grab = 16;
}

Is there anyway that I write it like a class and inherit it for all of them, or should I write 3 separate classes like the one I have written for the PictureBox?

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
Ashkan Mobayen Khiabani
  • 33,575
  • 33
  • 102
  • 171
  • I usually write a controller class that the controls can register to to get these services. See [here for an example of a Drag&Drop controller!](http://stackoverflow.com/questions/33284997/drag-and-drop-using-a-class-method/33288228?s=2|0.2710#33288228) – TaW Oct 09 '16 at 21:52
  • @HansPassant I didn't get what you mean – Ashkan Mobayen Khiabani Oct 09 '16 at 22:20
  • well some part of it I copied from elsewhere, I don't need to waste time writing what is already written, and I'm not intended to demand that the code is all mine, this was as an example, the code is much bigger. – Ashkan Mobayen Khiabani Oct 09 '16 at 22:55
  • You may be interested to [`FrameControl`](https://stackoverflow.com/a/53774101/3110834). – Reza Aghaei Jan 19 '19 at 10:38

2 Answers2

0

You can encapsulate the logic in a helper class deriving from NativeWindow class. This way you can do the job without creating a derived class for each control which you want to move/resize.

You can pass the control which you want to extend to the constructor of helper class and assign your control handle to the native window. Then overriding WndProc of native window will handle messages of the control.

Also other stuffs like handling event of the control is simply possible by keeping a reference to the control which you passed in constructor and assigning event handlers.

After creating such native window helper class the usage would be:

var h1 = new LiveControlHelper(this.pictureBox1);
var h2 = new LiveControlHelper(this.button1);

Or you can use the helper for all controls of a container in a loop.

Example

In below example, I refactored the code which you posted. This way you can use the code for all controls without need to inheritance.

using System;
using System.Drawing;
using System.Windows.Forms;
public class LiveControlHelper : NativeWindow
{
    private Control control;
    private Point cur = new Point(0, 0);
    private const int grab = 16;
    public LiveControlHelper(Control c)
    {
        control = c;
        this.AssignHandle(c.Handle);
        control.MouseDown += (s, e) => { cur = new Point(e.X, e.Y); };
        control.MouseMove += (s, e) => {
            if (e.Button == MouseButtons.Left) {
                Control x = (Control)s;
                x.SuspendLayout();
                x.Location = new Point(x.Left + e.X - cur.X, x.Top + e.Y - cur.Y);
                x.ResumeLayout();
            }
        };
        control.Paint += (s, e) => {
            var rc = new Rectangle(control.ClientSize.Width - grab, 
                control.ClientSize.Height - grab, grab, grab);
            ControlPaint.DrawSizeGrip(e.Graphics, control.BackColor, rc);
        };
        control.Resize += (s, e) => { control.Invalidate(); };
    }
    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        if (m.Msg == 0x84) {
            var pos = control.PointToClient(new Point(m.LParam.ToInt32() & 0xffff,
                m.LParam.ToInt32() >> 16));
            if (pos.X >= control.ClientSize.Width - grab &&
                pos.Y >= control.ClientSize.Height - grab)
                m.Result = new IntPtr(17);
        }
    }
}

Note

1- To be able to revert the control to its normal state (non-resizable non-movable) it's better to assign event handlers using methods and not using lambda. You need to revome event handlers to revert the control to its normal state. Also to do so, you need to call DestroyHanlde method of helper class.

2- I just refactored the posted code to make it reusable for controls without need to implement a derived version of all controls. But you can enhance the code by:

  • Enable moving the control and resizing it using surface, edges and corners of control by setting m.Result to suitable values.

  • Draw grab borders/handlers on control.

3- If you need to call SetStyle on control, you can simply use an extension method from this post and call it this way:

control.SetStyle(ControlStyles.OptimizedDoubleBuffer |
    ControlStyles.AllPaintingInWmPaint |
    ControlStyles.ResizeRedraw, true);
Community
  • 1
  • 1
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
0

I think using T4 Text Templates can solve your problem. If you are not familiar with, these links may be useful: 1. Code Generation and T4 Text Templates 2. Design-Time Code Generation by using T4 Text Templates

Amin
  • 221
  • 3
  • 13