Tag: GDI+

  • Drag and drop GDI+ objects on a Panel

    This adventure takes place on a Panel that resides on a Form. I have a class that represents a GDI+ object (Thing). The Draw method that does the graphical representation of the object is very, very primitive, just enough to make it visible.

    Public Class Thing
    
       Private m_X As Integer
       Private m_Y As Integer
       Private m_Width As Integer
       Private m_Height As Integer
       Private m_IsDragging As Boolean
    
       Public Sub New(ByVal Rnd As Random, ByVal Width As Integer, _
    ByVal Height As Integer)
          Me.m_X = Rnd.Next(Width - 50)
          Me.m_Y = Rnd.Next(Height - 25)
          Me.m_Width = 100
          Me.m_Height = 50
       End Sub
    
       Public ReadOnly Property CenterX() As Integer
          Get
             Return Me.X + CType(Me.Width / 2, Integer)
          End Get
       End Property
    
       Public ReadOnly Property CenterY() As Integer
          Get
             Return Me.Y + CType(Me.Height / 2, Integer)
          End Get
       End Property
    
       Public Function GetDistance(ByVal FromX _
    As Integer, ByVal FromY As Integer) As Double
          Dim DistX As Double = Math.Abs(Me.CenterX - FromX)
          Dim DistY As Double = Math.Abs(Me.CenterY - FromY)
          Dim Dist As Double = (DistX + DistY)
          If Dist = 0 Then
             Return 0
          Else
             Return Dist / 2
          End If
       End Function
    
       Public Property X() As Integer
          Get
             Return Me.m_X
          End Get
          Set(ByVal value As Integer)
             Me.m_X = value
          End Set
       End Property
    
       Public Property Y() As Integer
          Get
             Return Me.m_Y
          End Get
          Set(ByVal value As Integer)
             Me.m_Y = value
          End Set
       End Property
    
       Public Property Width() As Integer
          Get
             Return Me.m_Width
          End Get
          Set(ByVal value As Integer)
             Me.m_Width = value
          End Set
       End Property
    
       Public Property Height() As Integer
          Get
             Return Me.m_Height
          End Get
          Set(ByVal value As Integer)
             Me.m_Height = value
          End Set
       End Property
    
       Public Property IsDragging() As Boolean
          Get
             Return Me.m_IsDragging
          End Get
          Set(ByVal value As Boolean)
             Me.m_IsDragging = value
          End Set
       End Property
    
       Public Sub Draw(ByVal G As System.Drawing.Graphics)
          G.FillRectangle(Brushes.Green, Me.X, Me.Y, Me.Width, Me.Height)
          G.DrawRectangle(Pens.Black, Me.X, Me.Y, Me.Width, Me.Height)
          If Me.IsDragging Then
             G.DrawRectangle(Pens.Black, Me.X - 2, Me.Y - 2, Me.Width + 4, Me.Height + 4)
          End If
       End Sub
    
       Public Function HitTest(ByVal PosX As Integer, ByVal PosY As Integer) As Boolean
          Return (PosX >= Me.X _
    AndAlso PosX <= (Me.X + Me.Width) _
    AndAlso PosY >= Me.Y AndAlso PosY <= (Me.Y + Me.Height))
       End Function
    
    End Class
    

    The ThingList keeps track of all dragable objects, and makes sure that all are positioned within the visible area of the Panel.

    Public Class ThingList
       Inherits System.Collections.CollectionBase
    
       Public Function Add(ByVal O As Thing) As Integer
          Return List.Add(O)
       End Function
    
       Default Public ReadOnly Property Item(ByVal Index As Integer) As Thing
          Get
             Return CType(List(Index), Thing)
          End Get
       End Property
    
       Public Sub Draw(ByVal G As System.Drawing.Graphics)
          For Each O As Thing In Me
             O.Draw(G)
          Next
       End Sub
    
       Public Function GetObjectAt(ByVal MouseX As Integer, ByVal MouseY As_
     Integer) As Thing
          If Me.Count > 0 Then
             For Each O As Thing In Me
                If O.HitTest(MouseX, MouseY) Then
                   Return O
                End If
             Next
             Return Nothing
          Else
             Return Nothing
          End If
       End Function
    
       Public Function GetObjectIfClose(ByVal MouseX As Integer, _
    ByVal MouseY As Integer) As Thing
          If Me.Count > 0 Then
             Dim CloseIndex As Integer = -1
             Dim CloseDistance As Double = 60
             For I As Integer = 0 To Me.Count - 1
                Dim Distance As Double = Me(I).GetDistance(MouseX, MouseY)
                If Distance < CloseDistance Then
                   CloseDistance = Distance
                   CloseIndex = I
                End If
             Next
             If CloseIndex >= 0 Then
                Return Me(CloseIndex)
             Else
                Return Nothing
             End If
          Else
             Return Nothing
          End If
       End Function
    
       Public Sub ResetIsDragging()
          For Each O As Thing In Me
             O.IsDragging = False
          Next
       End Sub
    
       Public Function MoveToVisibleArea(ByVal S As Size) As Boolean
          Dim Ret As Boolean = False
          For Each O As Thing In Me
             If O.X < 0 Then
                O.X += 2
                Ret = True
             End If
             If O.Y < 0 Then
                O.Y += 2
                Ret = True
             End If
             If (O.X + O.Width) > S.Width Then
                O.X -= 2
                Ret = True
             End If
             If (O.Y + O.Height) > S.Height Then
                O.Y -= 2
                Ret = True
             End If
          Next
          Return Ret
       End Function
    
    End Class
    

    The form (contains a panel named Panel1) basically keeps track of mouse actions. This does not include the drag and drop events of the Control class, because that deals with dragging and dropping controls, not GDI+ objects.

    Public Class Form1
    
       Private L As ThingList
       Private IsDragging As Boolean
       Private DragObject As Thing = Nothing
       Private DragOffsetX As Integer
       Private DragOffsetY As Integer
    
       Private Sub Form1_Load(ByVal sender As _
    System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
          L = New ThingList()
          Dim Rnd As New Random()
          L.Add(New Thing(Rnd, Panel1.Width, Panel1.Height))
          L.Add(New Thing(Rnd, Panel1.Width, Panel1.Height))
          L.Add(New Thing(Rnd, Panel1.Width, Panel1.Height))
       End Sub
    
       Private Sub Panel1_MouseDown(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.MouseEventArgs) Handles Panel1.MouseDown
          Me.DragObject = L.GetObjectIfClose(e.X, e.Y)
          If Not DragObject Is Nothing Then
             Me.DragOffsetX = e.X - Me.DragObject.X
             Me.DragOffsetY = e.Y - Me.DragObject.Y
             Me.DragObject.IsDragging = True
             Me.IsDragging = True
             Panel1.Invalidate()
          End If
       End Sub
    
       Private Sub Panel1_MouseMove(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.MouseEventArgs) Handles Panel1.MouseMove
          If Me.IsDragging And Not Me.DragObject Is Nothing Then
             Me.DragObject.X = e.X - Me.DragOffsetX
             Me.DragObject.Y = e.Y - Me.DragOffsetY
             Panel1.Invalidate()
          End If
       End Sub
    
       Private Sub Panel1_MouseUp(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.MouseEventArgs) Handles Panel1.MouseUp
          If Me.IsDragging Then
             Me.IsDragging = False
             L.ResetIsDragging()
             Me.DragObject = Nothing
          End If
          L.MoveToVisibleArea(Panel1.Size)
          Panel1.Invalidate()
       End Sub
    
       Private Sub Panel1_Paint(ByVal sender As System.Object, _
    ByVal e As System.Windows.Forms.PaintEventArgs) Handles Panel1.Paint
          e.Graphics.FillRectangle(Brushes.White, 0, 0, Me.Width, Me.Height)
          L.Draw(e.Graphics)
       End Sub
    
       Private Sub Panel1_Resize(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles MyBase.Resize
          If Not L Is Nothing Then
             If L.MoveToVisibleArea(Panel1.Size) Then
                Panel1.Invalidate()
             End If
          End If
       End Sub
    
    End Class
    

    Now you can play around with three green boxes in the panel.