Moving and Sizing Pictureboxes

Often, when working with pictureboxes, you need to give users the ability to manipulate the size and position of the picturebox. In this article, you are going to look at two different situations that may arise:

  • The image is larger than the form, and you need to be able to scroll the image around.
  • Resize and move a smaller image over a larger one.

You are going to cover each problem individually The project download will contain both methods.

Problem 1: Image is Larger than the Form or Screen

This is a very real problem faced by any number of applications, especially image editing/viewing software. You need to add the ability to view a small section of the image and have a scroller to move around it. Your first task is to create your form and add two Pictureboxes, a Horizontal and a Vertical scroller, to it. Your goal here is to use one picturebox as a simple holder and the second as your image object.

I've used the Anchor properties of .NET to lock the location and sizing of the Scrollers and the holder picturebox; this allows you to concentrate more on the values of the controls and not so much the location and size. This is turn reduces the amount of code needed.

The first thing you do is set the Parent for your image object to the holder.

   PictureBox2.Parent = PictureBox1

Now, you look at what happens when the form is resized. You create a sub to handle the values required for your Scrollers in relation to the size difference between your image and your form, the image holder in this case. All the values are derived from the difference of the two picturebox sizes.

You create a sub, 'ScrollAdjust', to handle these calculations that you can call as and when required.

Private Sub ScrollAdjust()
   'used for resizing the parent form & holder !!
   With PictureBox2
      If .Width < PictureBox1.Width Then
         .Left = (PictureBox1.Width - .Width) / 2
         HScrollBar1.Enabled = False
      Else
         If .Left > 0 Then .Left = 0
         HScrollBar1.Minimum = 0
         HScrollBar1.Maximum = .Width - PictureBox1.Width
         HScrollBar1.SmallChange = 1
         HScrollBar1.LargeChange = 10
         HScrollBar1.Enabled = True
      End If
      If .Height < PictureBox1.Height Then
         .Top = (PictureBox1.Height - .Height) / 2
         VScrollBar1.Enabled = False
      Else
         If .Top > 0 Then .Top = 0
         VScrollBar1.Minimum = 0
         VScrollBar1.Maximum = .Height - PictureBox1.Height
         VScrollBar1.SmallChange = 1
         VScrollBar1.LargeChange = 10
         VScrollBar1.Enabled = True
      End If
   End With
End Sub

A first look at this sub may intimidate you; however, it's very simple. The basics are, if the image is smaller than the holder, place it in the centre and disable the scroller; if the image is larger than the holder, make sure there is no border, and set the scroller maximum to the size difference. You do this individually for the width and the height of the image.

Next, you look at what happens when you want to move the image with the Scrollers. Because all of the critical calculations have been done, this is the easy part. All that is needed is to reposition the image.

Private Sub VScrollBar1_Scroll(ByVal sender As System.Object, _
   ByVal e As System.Windows.Forms.ScrollEventArgs)_
   Handles VScrollBar1.Scroll
   PictureBox2.Top = -VScrollBar1.Value

End Sub

Private Sub HScrollBar1_Scroll(ByVal sender As System.Object, _
   ByVal e As System.Windows.Forms.ScrollEventArgs)_
   Handles HScrollBar1.Scroll
   PictureBox2.Left = -HScrollBar1.Value

End Sub

One big question I've been asked several times is why do you use the Negative value and not a positive value. That's simple; the top left corner of a control is always 0,0 (X,Y) with the positive moving down and to the right. So, if you use the positive you move the top left corner of the image down and to the right, pushing your bottom right corner further away from view. However, if you use the negative and push the top left corner out of view, you get closer to the bottom right corner. Once you've moved it by the total calculated difference, the bottom right corner should be in full view.

In theory, this is true; in practice, you need to look at a few other items, namely docked objects. If there are any docked objects in the holder, you need to compensate for them. There are many variants, and to display the code for each will take time; however, a brief description should help you if need be.

The width or height of the docked object needs to be added to the difference in size. This is critical because the docked object will lay over the image and hide portions of it. Handling the movement of the image also requires some special thought.

Docked object on the top or left: You need to offset the top or left of the image to slip past the docked object. Something like the following code will be needed.

   PictureBox2.Top = -VScrollBar1.Value + DockedObject.height

Docked object on the bottom or right: Here, you are a little more fortunate. By adding the size to your calculated difference, the normal scrolling will pull the bottom right corner past the holder's corner and just up to where the docked object stands. There is no need to adjust anything else here.

One very important thing to remember here is that any docked object will affect only the visible height or width of the holder, but not both. Also, multiple docked objects must be taken into consideration.

Okay, moving on, you also need to allow the user to click on the image and move it. This is not as critical but is always a nice feature to include. Because the left click is generally used to start or move a selection, you going to use the Scroll click (or middle button click) to move the complete image.

The first step is to detect this mouse click, when it's pressed and when it's released. You use the 'Mousedown' and 'Mouseup' events. You set a Boolean value to represent this state, and record the starting location of the click.

Private StartCord As Drawing.Point
Private MoveMain As Boolean

Private Sub PictureBox2_MouseDown(ByVal sender As Object, _
   ByVal e As System.Windows.Forms.MouseEventArgs)_
   Handles PictureBox2.MouseDown
   If e.Button = Windows.Forms.MouseButtons.Middle Then
      MoveMain = True
      StartCord = e.Location
   End If
End Sub

Private Sub PictureBox2_MouseUp(ByVal sender As Object, _
   ByVal e As System.Windows.Forms.MouseEventArgs)_
   Handles PictureBox2.MouseUp
   MoveMain = False
End Sub

Thereafter, in the 'Mousemove' event you check the state of the Boolean variable and adjust the location of the image according to the difference between the current position and start position.

Private Sub PictureBox2_MouseMove(ByVal sender As Object, _
   ByVal e As System.Windows.Forms.MouseEventArgs)_
   Handles PictureBox2.MouseMove
   If MoveMain Then
      If VScrollBar1.Enabled Then
         newCord.Y = PictureBox2.Top - (StartCord.Y - e.Location.Y)
         If newCord.Y > 0 Then newCord.Y = 0
         If newCord.Y < -VScrollBar1.Maximum Then newCord.Y = _
                        -VScrollBar1.Maximum
         PictureBox2.Top = newCord.Y
         VScrollBar1.Value = -newCord.Y
      End If
      If HScrollBar1.Enabled Then
         newCord.X = PictureBox2.Left - (StartCord.X - e.Location.X)
         If newCord.X > 0 Then newCord.X = 0
         If newCord.X < -HScrollBar1.Maximum Then _
            newCord.X = -HScrollBar1.Maximum
         PictureBox2.Left = newCord.X
         HScrollBar1.Value = -newCord.X
      End If
   End If
End Sub

Having done all of this, you should have a project now that will allow any image loaded into Picturebox2 to be viewed inside a sizable form and scrolled to any corner.

Moving and Sizing Pictureboxes

Problem 2: Sizing and Moving One Image on Top of Another

Now, you are going to look at what needs be done to allow a smaller image to be placed over a larger image. This situation arises in many forms and ways. For the reason of supplying the code required to enable this, I will be working with a picturebox within a picturebox. Firstly, this allows you to evaluate only the code required to move and resize the object, without the Stretch and zoom code there to complicate it. Secondly, with the Picturebox taking care of zooming and stretching the image, you don't need to complicate the project with APIs, Buffering and Graphics objects.

This example is taken from a real program requirement, and was written when a work colleague asked me to assist with code to allow a user to place a company logo on top of an image. The rest of the requirements are not important to this article, but some included the ability to reproduce the final selection, and to alter the inserted image but leave it in the same location.

Okay, you are going to place this smaller image on top of the image you have in the previous example. So, in the project you now add a new picturebox to the form. You also set it as a child object of the picturebox.

   PictureBox3.Parent = PictureBox2
   PictureBox3.SizeMode = PictureBoxSizeMode.StretchImage

Setting the 'Sizemode' to Stretchimage allows the picturebox control to manipulate the image to fit the selected size. Hence, no code is required to zoom and stretch the image.

Now, where do you start with the smaller image? You start with location. You want to be able to move the image to wherever it is required. This is done with a left click on the image and moving the mouse.

Private Sub PictureBox3_MouseDown(ByVal sender As Object, _
   ByVal e As System.Windows.Forms.MouseEventArgs)_
   Handles PictureBox3.MouseDown
   If e.Button = Windows.Forms.MouseButtons.Left Then
      StartCord = e.Location
      MovePic = True
   End If
End Sub

Private Sub PictureBox3_MouseMove(ByVal sender As Object, _
   ByVal e As System.Windows.Forms.MouseEventArgs)_
   Handles PictureBox3.MouseMove
   ' Mouse move inside Image Region... - NB. Child Picturebox
   If e.Button = Windows.Forms.MouseButtons.Left Then
      If MovePic Then
         PictureBox3.Top = PictureBox3.Top + _
            (e.Location.Y - StartCord.Y)
         PictureBox3.Left = PictureBox3.Left + _
            (e.Location.X - StartCord.X)
      End If
   End If
End Sub

Private Sub PictureBox3_MouseUp(ByVal sender As Object, _
   ByVal e As System.Windows.Forms.MouseEventArgs)_
   Handles PictureBox3.MouseUp
   MovePic = False
End Sub

Wow, that's easy, you may say. Yes, it is that easy to move any object on a form. Although there are a few things that need to known, 'e.Location' holds the location of the mouse pointer on Picturebox3. As the picturebox moves, so does the 'Location' of the mouse on top of it. So now, if you look at the code a little closer with this information, you can see that what you doing is moving the picturebox so that your pointer is always on the same spot. This is achieved by adding the difference of the original position and the current position to the location of the object. In other words, if the mouse moved 3 pixels to the left, move the object 3 pixels to the left.

With the 'mousedown' event, you set a Boolean Movepic variable so that you can use this state in the 'mousemove' event to decide what action to take.

Note: This code is specific to move an object on an object; alternate code is used to move a rectangular region on an object.

Now, you need to start with the code to size this image. The first thing you need to do is detect the edges of the image and change the pointer to show that you are ready to size.

Private wEdge As Boolean
Private eEdge As Boolean
Private nEdge As Boolean
Private sEdge As Boolean
Private Sizing As Boolean
Private Const EdgeSize As Integer = 6
Private SizePic As Boolean

Private Sub PictureBox3_MouseMove(ByVal sender As Object, _
   ByVal e As System.Windows.Forms.MouseEventArgs)_
   Handles PictureBox3.MouseMove
   ' Mouse move inside Image Region... - NB. Child Picturebox
   If e.Button = Windows.Forms.MouseButtons.None Then
      If e.Location.X < EdgeSize And e.Location.X > 0 Then
         wEdge = True
      Else
         wEdge = False
      End If
      If e.Location.X > PictureBox3.Size.Width - EdgeSize And_
         e.Location.X < PictureBox3.Size.Width Then
         eEdge = True
      Else
         eEdge = False
      End If
      If e.Location.Y < EdgeSize And e.Location.Y > 0 Then
         nEdge = True
      Else
         nEdge = False
      End If
      If e.Location.Y > PictureBox3.Size.Height - EdgeSize And_
         e.Location.Y < PictureBox3.Size.Height Then
         sEdge = True
      Else
         sEdge = False
      End If
      Sizing = True
   ElseIf e.Button = Windows.Forms.MouseButtons.Left Then
      If MovePic Then
         PictureBox3.Top = PictureBox3.Top + _
            (e.Location.Y - StartCord.Y)
         PictureBox3.Left = PictureBox3.Left + _
            (e.Location.X - StartCord.X)
      End If
   End If

   ' The code following works when used with a child Picturebox...
   If (nEdge Or sEdge) And Not (wEdge Or eEdge) Then
      PictureBox3.Cursor = Cursors.SizeNS
   ElseIf (wEdge Or eEdge) And Not (nEdge Or sEdge) Then
      PictureBox3.Cursor = Cursors.SizeWE
   ElseIf (nEdge And eEdge) Or (sEdge And wEdge) Then
      PictureBox3.Cursor = Cursors.SizeNESW
   ElseIf (nEdge And wEdge) Or (sEdge And eEdge) Then
      PictureBox3.Cursor = Cursors.SizeNWSE
   Else
      PictureBox3.Cursor = Cursors.Default
      Sizing = False
   End If

End Sub

With the additional code in the 'mousemove' event, you look for the edge (with an adjustable size) and show the appropriate cursor. You also set a Boolean variable Sizing when at the edge. This gives you the ability to use this state in the 'mousedown' event.

Private Sub PictureBox3_MouseDown(ByVal sender As Object, _
   ByVal e As System.Windows.Forms.MouseEventArgs)_
   Handles PictureBox3.MouseDown
   ' Click on Sizable Image Region - NB. Child picturebox
   If e.Button = Windows.Forms.MouseButtons.Left Then
      OldCord = e.Location
      StartCord = e.Location
      If Sizing Then
         SizePic = True
      Else
         MovePic = True
      End If
   End If
End Sub

Private Sub PictureBox3_MouseUp(ByVal sender As Object, _
   ByVal e As System.Windows.Forms.MouseEventArgs)_
   Handles PictureBox3.MouseUp
   ' Release Click in Sizable Image Region - NB. Child picturebox
   SizePic = False
   MovePic = False
End Sub

With the sizing variable, you now can check whether the mouse click is to size or move the image, and you set the relevant variables for this. All that is left is to code is the sizing.

Private Sub PictureBox3_MouseMove(ByVal sender As Object, _
   ByVal e As System.Windows.Forms.MouseEventArgs)_
   Handles PictureBox3.MouseMove
   ' Mouse move inside Image Region... - NB. Child Picturebox
   If e.Button = Windows.Forms.MouseButtons.None Then
      If e.Location.X < EdgeSize And e.Location.X > 0 Then
         wEdge = True
      Else
         wEdge = False
      End If
      If e.Location.X > PictureBox3.Size.Width - EdgeSize And_
         e.Location.X < PictureBox3.Size.Width Then
         eEdge = True
      Else
         eEdge = False
      End If
      If e.Location.Y < EdgeSize And e.Location.Y > 0 Then
         nEdge = True
      Else
         nEdge = False
      End If
      If e.Location.Y > PictureBox3.Size.Height - EdgeSize And_
         e.Location.Y < PictureBox3.Size.Height Then
         sEdge = True
      Else
         sEdge = False
      End If
      Sizing = True
   ElseIf e.Button = Windows.Forms.MouseButtons.Left Then
      If MovePic Then
         PictureBox3.Top = PictureBox3.Top + _
            (e.Location.Y - StartCord.Y)
         PictureBox3.Left = PictureBox3.Left + _
            (e.Location.X - StartCord.X)
      End If
      ' The code following works when used with a child
      ' Picturebox...
      ' NB. Alternate code will apply if working on a rect region
      ' selection.....
      If SizePic Then
         Rect.Top = PictureBox3.Top
         Rect.Height = PictureBox3.Height
         Rect.Left = PictureBox3.Left
         Rect.Width = PictureBox3.Width
         If eEdge Then
            Rect.Width = e.Location.X
         End If
         If sEdge Then
            Rect.Height = e.Location.Y
         End If
         If wEdge Then
            Rect.Left = Rect.Left - (StartCord.X - e.Location.X)
            Rect.Width = Rect.Width + (StartCord.X - e.Location.X)
         End If
         If nEdge Then
            Rect.Top = Rect.Top - (StartCord.Y - e.Location.Y)
            Rect.Height = Rect.Height + (StartCord.Y - e.Location.Y)
         End If
         If Rect.Height < 0 Then
            Rect.Top = Rect.Top + Rect.Height
            Rect.Height = Math.Abs(Rect.Height)
            nEdge = Not nEdge
            sEdge = Not sEdge
            StartCord.Y = 0
         End If
         If Rect.Width < 0 Then
            Rect.Left = Rect.Left + Rect.Width
            Rect.Width = Math.Abs(Rect.Width)
            eEdge = Not eEdge
            wEdge = Not wEdge
            StartCord.X = 0
         End If
         PictureBox3.Top = Rect.Top
         PictureBox3.Height = Rect.Height
         PictureBox3.Left = Rect.Left
         PictureBox3.Width = Rect.Width
      End If
   End If
   If (nEdge Or sEdge) And Not (wEdge Or eEdge) Then
      PictureBox3.Cursor = Cursors.SizeNS
   ElseIf (wEdge Or eEdge) And Not (nEdge Or sEdge) Then
      PictureBox3.Cursor = Cursors.SizeWE
   ElseIf (nEdge And eEdge) Or (sEdge And wEdge) Then
      PictureBox3.Cursor = Cursors.SizeNESW
   ElseIf (nEdge And wEdge) Or (sEdge And eEdge) Then
      PictureBox3.Cursor = Cursors.SizeNWSE
   Else
      PictureBox3.Cursor = Cursors.Default
      Sizing = False
   End If

   OldCord = e.Location
End Sub

There's a lot happening in the sizing code. The first thing that you do is check which edge you are sizing, allowing for corners where you size two edges.

Thereafter, you check to see whether you crossed over the opposite edge. Because a picture box with a width of -1 will always default to zero, you use a rectangle type to temporarily store your values. If you have crossed over the opposite edge, you flip the negative size and the variables, and then, where needed, the cursor.

If all was done correctly, you should have a project with perfect image object move and size code. The attached project shows a working example of both methods been used on a single form.

In Conclusion

Hannes du Preez wrote a beautiful series of articles about image editing. This is a perfect addin to that project, available here on Part 4 .



About the Author

Richard Newcombe

Richard Newcombe has been involved in computers since the time of the Commodore 64. Today, he has excelled in programming, and designs. Richard is in his mid 30's and, if or when you looking for him look no further than his computer. Always willing to help and give advice where he can in regard to computer related subjects. At present he is working as a .NET 2008 Software Developer for Syncrony Web Services, South Africa.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Hybrid cloud platforms need to think in terms of sweet spots when it comes to application platform interface (API) integration. Cloud Velocity has taken a unique approach to tight integration with the API sweet spot; enough to support the agility of physical and virtual apps, including multi-tier environments and databases, while reducing capital and operating costs. Read this case study to learn how a global-level Fortune 1000 company was able to deploy an entire 6+ TB Oracle eCommerce stack in Amazon Web …

  • Corporate e-Learning technology has a long and diverse pedigree. As far back as the 1980s, companies were adopting computer-based training to supplement traditional classroom activities. More recently, rich web-based applications have added streaming audio and video, real-time collaboration and other new tools to the e-Learning mix. At the same time, the growing availability of informal learning tools--a category that includes everything from web searches to social media posts--are having a major impact on …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds