Drag & Drop in Mono Winforms

This part of the Mono Winforms tutorial will be dedicated to the drag & drop operations.

In computer graphical user interfaces, drag-and-drop is the action of (or support for the action of) clicking on a virtual object and dragging it to a different location or onto another virtual object. In general, it can be used to invoke many kinds of actions, or create various types of associations between two abstract objects. (Wikipedia)

Drag and drop functionality is one of the most visible aspects of the graphical user interface. Drag and drop operation enables you to do complex things intuitively.

Dragging a button

In the first example, we will do the drag & drop operation on the button control. The example does the job outside the drag & drop protocol.

dragbutton.cs
using System;
using System.Drawing;
using System.Windows.Forms;

public class MForm : Form
{
  private bool isDragging = false;
  private int oldX, oldY;
  private Button button;

  public MForm()
  {
    Text = "Drag & drop button";
    Size = new Size(270, 180);

    button = new Button();
    button.Parent = this;
    button.Cursor = Cursors.Hand;
    button.Text = "Button";
    button.Location = new Point(20, 20);

    button.MouseDown += new MouseEventHandler(OnMouseDown);
    button.MouseUp += new MouseEventHandler(OnMouseUp);
    button.MouseMove += new MouseEventHandler(OnMouseMove);

    CenterToScreen();
  }

  public static void Main() 
  {
    Application.Run(new MForm());
  }

  private void OnMouseDown(object sender, MouseEventArgs e) 
  {
    isDragging = true;
    oldX = e.X;
    oldY = e.Y;
  }

  private void OnMouseMove(object sender, MouseEventArgs e) 
  {
    if (isDragging) 
    {
        button.Top = button.Top + (e.Y - oldY); 
        button.Left = button.Left + (e.X - oldX); 
    }
  }

  private void OnMouseUp(object sender, MouseEventArgs e) 
  {
    isDragging = false;
  }
}

The code example puts a regular button control on the form container. By clicking on the button surface and simultaneously dragging it with a mouse we can relocate the button.

private bool isDragging = false;
private int oldX, oldY;

These are the supporting variables for our example. The isDragging variable tells us, whether we are in the process of dragging an object. The oldX and oldY variables store the x, y coordinates just before the dragging process begins.

button.MouseDown += new MouseEventHandler(OnMouseDown);
button.MouseUp += new MouseEventHandler(OnMouseUp);
button.MouseMove += new MouseEventHandler(OnMouseMove);

We plug in three different mouse handlers for our button. They implement three different stages of the drag & drop process. The process begins, when we click on the button. This is handled by the OnMouseDown() method. The second part is the movement. This is when we move the object to a new position. It is handled in the OnMouseMove() method. The final part is when the process stops. It happens when we release the mouse button. The appropriate task is delegated to the OnMouseUp() method.

private void OnMouseDown(object sender, MouseEventArgs e) 
{
  isDragging = true;
  oldX = e.X;
  oldY = e.Y;
}

The OnMouseDown() method implements the first part of the process. It sets three necessary variables.

private void OnMouseMove(object sender, MouseEventArgs e) 
{
  if (isDragging) 
  {
      button.Top = button.Top + (e.Y - oldY); 
      button.Left = button.Left + (e.X - oldX); 
  }
}

In the OnMouseMove() method, we relocate the button. We calculate the difference between the stored x, y coordinates and the new coordinates of the mouse pointer. The difference is added to the Top and Left properties of the button, thus moving it to a new position.

Dragging a button
Figure: Dragging a button

Dragging Text

In the previous example, we did drag & drop on the control. Next we will do a drag & drop operation on the textual data. Here we will use the drag & drop protocol provided by the Winforms library.

Drag & drop operation is a standardized communication protocol in Winforms. We have two basic objects. The drag source and the drop target.

dragtext.cs
using System;
using System.Drawing;
using System.Windows.Forms;

public class MForm : Form
{

    private TextBox textBox;
    private Button button;

    public MForm()
    {
        InitForm();
        CenterToScreen();
    }

    private void OnMouseDown(object sender, MouseEventArgs e)
    {
        TextBox txt = (TextBox) sender;
        txt.DoDragDrop(txt.Text, DragDropEffects.Copy);
    }

    private void OnDragEnter(object sender, DragEventArgs e)
    {
        e.Effect = DragDropEffects.Copy;
    }

    private void OnDragDrop(object sender, DragEventArgs e)
    {
        Button button = (Button) sender;
        button.Text = (string) e.Data.GetData(DataFormats.Text);
    }


    private void InitForm()
    {
        Text = "Drag & drop";
        button = new Button();
        textBox = new TextBox();
        SuspendLayout();

        button.AllowDrop = true;
        button.Location = new Point(150, 50);
        textBox.Location = new Point(15, 50);

        button.DragDrop += new DragEventHandler(OnDragDrop);
        button.DragEnter += new DragEventHandler(OnDragEnter);
        textBox.MouseDown += new MouseEventHandler(OnMouseDown);

        ClientSize = new Size(250, 200);
        Controls.Add(button);
        Controls.Add(textBox);
        ResumeLayout();
    }

    public static void Main(string[] args)
    {
        Application.Run(new MForm());
    }

}

We have two controls on the form. A button and a text box. We will drag text from the text box and drop it on the button.

InitForm();

Setting up of the form is delegated to the InitForm() method. This is usually done in larger applications.

SuspendLayout();
...
ResumeLayout();

We lay out our controls between these two method calls. This is an optimalization. It should eliminate flicker.

button.AllowDrop = true;

We set the AllowDrop property to true. Dropping is not enabled by default.

button.DragDrop += new DragEventHandler(OnDragDrop);
button.DragEnter += new DragEventHandler(OnDragEnter);
textBox.MouseDown += new MouseEventHandler(OnMouseDown);

Again, the drag & drop process is divided into three steps. We have three methods for each particular step.

private void OnMouseDown(object sender, MouseEventArgs e)
{
    TextBox txt = (TextBox) sender;
    txt.DoDragDrop(txt.Text, DragDropEffects.Copy);
}

In the OnMouseDown() method we initialize the drap & drop process. We initiate the process with the DoDragDrop() method. The DragDropEffects.Copy parameter specifies the type of the operation. Esentially, we can either copy the text or move it during the drag & drop operation.

private void OnDragEnter(object sender, DragEventArgs e)
{
    e.Effect = DragDropEffects.Copy;
}

The DragEnter event is launched when the mouse pointer enters the area of the drop target control. The Effect property must be set. The DragDropEffects of the drag source and drop target must be equal. Otherwise the operation will not work.

private void OnDragDrop(object sender, DragEventArgs e)
{
    Button button = (Button) sender;
    button.Text = (string) e.Data.GetData(DataFormats.Text);
}

Finally we have the OnDragDrop() method. Here we get the data from the event object and set it to the button Text property.

Drag & drop of text
Figure: Drag & drop of text

Dragging Image

In our last example, we will drag & drop image on the form.

dragimage.cs
using System;
using System.Drawing;
using System.Windows.Forms;

public class ImageDragDrop : Form
{
  private bool isDragging;
  private int oldX, oldY;

  private Rectangle dropRect;
  private PictureBox picBox;
  private Bitmap image;
  private Brush brush;

  public ImageDragDrop()
  {
    ClientSize = new Size(350, 250);
    Text = "Dragging Image";
    Paint += new PaintEventHandler(OnPaint);
    
    isDragging = false;
    dropRect = new Rectangle(10, 10, 200, 160);
    brush = Brushes.Gray;
    picBox = new PictureBox();
    loadImage();

    picBox.Parent = this;
    picBox.Location = new Point(100, 50);
    picBox.Size = new Size(image.Width, image.Height);
    picBox.Image = image;
    picBox.Cursor = Cursors.Hand;

    picBox.MouseDown += new MouseEventHandler(OnMouseDown);
    picBox.MouseUp += new MouseEventHandler(OnMouseUp);
    picBox.MouseMove += new MouseEventHandler(OnMouseMove);

    CenterToScreen();
  }


  void loadImage() {
      try {
          image = new Bitmap("image.jpg");
      } catch {
          Console.WriteLine("Error reading image");
          Environment.Exit(1);
      }
  }

  public static void Main() 
  {
    Application.Run(new ImageDragDrop());
  }

  private void OnMouseDown(object sender, MouseEventArgs e) 
  {
    isDragging = true;
    oldX = e.X;
    oldY = e.Y;
  }

  private void OnMouseMove(object sender, MouseEventArgs e) 
  {
    if (isDragging) 
    {
      picBox.Top = picBox.Top + (e.Y - oldY);
      picBox.Left = picBox.Left + (e.X - oldX);
    }
  }

  private void OnMouseUp(object sender, MouseEventArgs e) 
  {
    isDragging = false;

    if(dropRect.Contains(picBox.Bounds)) {
        brush = Brushes.Gold;
    } else {
        brush = Brushes.Gray;
    }

    Refresh();
  }

  private void OnPaint(object sender, PaintEventArgs e)
  {
    Graphics g = e.Graphics;
    g.FillRectangle(brush, dropRect);
  }
}

In our example we have a PictureBox and we draw a gray rectangle. If we drop the picture inside the rectangle, the color of the rectangle changes to gold.

brush = Brushes.Gray;

The brush variable holds the brush of the rectangle. It is a gray color by default.

void loadImage() {
    try {
        image = new Bitmap("image.jpg");
    } catch {
        Console.WriteLine("Error reading image");
        Environment.Exit(1);
    }
}

The loadImage() loads a bitmap for the PictureBox control.

if (dropRect.Contains(picBox.Bounds)) {
    brush = Brushes.Gold;
} else {
    brush = Brushes.Gray;
}

In the OnMouseUp() method, we determine the brush of the rectangle. If the bounds of the picture box are inside the rectangle, the brush is of gold color; gray otherwise.

Refresh();

We must call the Refresh() method to activate the new brush color.

Drag & drop image
Figure: Drag & drop image

This chapter was dedicated to drag & drop operations using the Mono Winforms library.