Drag & drop in Mono Winforms

This part of the IronPython 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.py
#!/usr/bin/ipy

import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import Application, Form, Button
from System.Drawing import Size, Point

class IForm(Form):

    def __init__(self):
        self.Text = 'Drag & Drop'

        button = Button()
        button.Parent = self
        button.Text = 'Button'
        button.MouseDown += self.OnMousDown
        button.MouseUp += self.OnMousUp
        button.MouseMove += self.OnMousMove

        button.Location = Point(20, 20)

        self.isDragging = False
        self.CenterToScreen()

    def OnMousDown(self, sender,  event):
  
        self.isDragging = True
        self.oldX = event.X
        self.oldY = event.Y

    def OnMousMove(self, sender, event):
  
        if self.isDragging: 
            sender.Top = sender.Top + (event.Y - self.oldY)
            sender.Left = sender.Left + (event.X - self.oldX)
    

    def OnMousUp(self, sender,  event):
        self.isDragging = False
  

Application.Run(IForm())

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.

There are some supporting variables in 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 += self.OnMousDown
button.MouseUp += self.OnMousUp
button.MouseMove += self.OnMousMove

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 OnMousDown() method. The second part is the movement. This is when we move the object to a new position. It is handled in the OnMousMove() method. The final part is when the process stops. It happens when we release the mouse button. The appropriate task is delegated to the OnMousUp() method.

def OnMousDown(self, sender,  event):

self.isDragging = True
self.oldX = event.X
self.oldY = event.Y

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

def OnMousMove(self, sender, event):

    if self.isDragging: 
        sender.Top = sender.Top + (event.Y - self.oldY)
        sender.Left = sender.Left + (event.X - self.oldX)

In the OnMousMove() 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 standardised communication protocol in Winforms. We have two basic objects. The drag source and the drop target.

dragtext.py
#!/usr/bin/ipy

import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")


from System.Windows.Forms import Application, Form, Button
from System.Windows.Forms import TextBox, DragDropEffects, DataFormats
from System.Drawing import Size, Point

class IForm(Form):

    def __init__(self):
        self.Text = 'Drag & Drop'
        self.AllowDrop = True

        button = Button()
        button.Parent = self
        textBox = TextBox()
        textBox.Parent = self
 
        button.AllowDrop = True
        button.Location = Point(150, 50)
        button.DragDrop += self.OnDragDrop
        button.DragEnter += self.OnDragEnter

        textBox.Location = Point(15, 50)
        textBox.MouseDown += self.OnMousDown

        self.ClientSize = Size(250, 200)
        self.CenterToScreen()


    def OnMousDown(self, sender, event):
        sender.SelectAll()
        sender.DoDragDrop(sender.Text, DragDropEffects.Copy)
    

    def OnDragEnter(self, sender, event):
    
        if event.Data.GetDataPresent(DataFormats.Text):
            event.Effect = DragDropEffects.Copy
      

    def OnDragDrop(self, sender, event):
        sender.Text =  event.Data.GetData(DataFormats.Text)
    

Application.Run(IForm())

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.

self.AllowDrop = True

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

button.DragDrop += self.OnDragDrop
button.DragEnter += self.OnDragEnter
...       
extBox.MouseDown += self.OnMousDown

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

def OnMousDown(self, sender, event):
    sender.SelectAll()
    sender.DoDragDrop(sender.Text, DragDropEffects.Copy)

In the OnMousDown() 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.

def OnDragEnter(self, sender, event):

    if event.Data.GetDataPresent(DataFormats.Text):
        event.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.

def OnDragDrop(self, sender, event):
    sender.Text =  event.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.py
#!/usr/bin/ipy

import sys
import clr
clr.AddReference("System.Windows.Forms")
clr.AddReference("System.Drawing")

from System.Windows.Forms import Application, Form, PictureBox, PictureBoxSizeMode
from System.Windows.Forms import Cursors
from System.Drawing import Size, Point, Rectangle, Brushes, Bitmap

class IForm(Form):

    def __init__(self):

        self.ClientSize = Size(350, 250)
        self.Text = "Dragging Images"
        self.Paint += self.OnPaint
    
        self.isDragging = False
        self.dropRect = Rectangle(10, 10, 200, 160)
        self.brush = Brushes.Gray
        picBox = PictureBox()

        self.loadImage()

        self.isDragging = False
        self.CenterToScreen()

        picBox.Parent = self
        picBox.Location = Point(100, 50)
        picBox.Size = Size(self.image.Width, self.image.Height)
        picBox.Image = self.image
        picBox.MouseDown += self.OnMousDown
        picBox.MouseUp += self.OnMousUp
        picBox.MouseMove += self.OnMousMove
        picBox.Cursor = Cursors.Hand


    def loadImage(self):
        try:
            self.image = Bitmap("image.jpg")
        except Exception, e: 
            print "Error reading image"
            print e.msg
            sys.exit(1)


    def OnMousMove(self, sender, event): 
        if self.isDragging:
            sender.Top = sender.Top + (event.Y - self.oldY)
            sender.Left = sender.Left + (event.X - self.oldX)


    def OnMousUp(self, sender, event):
        self.isDragging = False

        if self.dropRect.Contains(sender.Bounds):
            self.brush = Brushes.Gold
        else: 
            self.brush = Brushes.Gray

        self.Refresh()


    def OnMousDown(self, sender, event):
        self.isDragging = True
        self.oldX = event.X
        self.oldY = event.Y
 

    def OnPaint(self, event): 
        g = event.Graphics
        g.FillRectangle(self.brush, self.dropRect)
 
  
Application.Run(IForm()) 

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

self.brush = Brushes.Gray

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

def loadImage(self):
    try:
        self.image = Bitmap("image.jpg")
    except Exception, e: 
        print "Error reading image"
        print e.msg
        sys.exit(1)

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

if self.dropRect.Contains(sender.Bounds):
    self.brush = Brushes.Gold
else: 
    self.brush = Brushes.Gray

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

self.Refresh()

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

Drag & drop image
Figure: Drag & drop image

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