Y'herd thisun? 

“There is nothing inherently expensive about rockets. It's just that those who have built and operated them in the past have done so with horrendously poor efficiency.”
-Elon Musk

Your own image control and App part 10

TaggedCoding, VB, Imaging

Originally published December 2002 on using VB7.0/2002. Updated for VB7.1 February 2005

Links for compiled demo versions, all required resources and source code are included at the end of this article.

Plus, get the complete eBook in Adobe Acrobat 7 format ... all here.


10) Feature 3: Zooming

"Zooming" is really just making an image bigger or smaller. If it's too big to fit the picturebox area then it appears that you're seeing it close-up and if it takes up just a portion of the picturebox then it looks far away. Simple enough, "zooming" means resizing.

You've already figured that the same types of options for rotating apply to zooming, we'll just agree that while it can be done with Win32 it's best to stick with a ".Net way" ... but it can also be done "The VB way".

.Net's DrawImage has a lot of overloaded versions including ones that let you squish or expand the original bits to a differently sized destination bitmap. If you do this with a good enough interpolation mode you can get very nice results but if your users like to play with their buttons there is a chance of degradation over time. 

I released a version of this control that went the DrawImage route and got around the loss of quality by using a custom rotation enum (as described earlier) and applying the calculated final rotation to the OriginalImage then using DrawImage for the zoom. In the end the results were very good because DrawImage gives you more control but switching pages on multipage tifs did take a split second longer than the way we'll be doing it today.

 In the classic days of VB, when developers were known for their use of the tool in ways Microsoft hadn't foreseen, the lowly picturebox was used to do fast image resizing, and that's the path we'll be taking.

Bring up the GUI designer for the UC, select the pbDisplay picturebox and in the property sheet change the SizeMode from AutoSize to StretchImage.

The zoom will be applied dynamically as a page is displayed so switch to codeview and create a private sub for the processing:


Private Sub UpdateZoomDisplay()
Dim tmpWorkingImage As Image
tmpWorkingImage = m_ImagePages(m_CurrentPageNumber).WorkingImage
pbDisplay.Width = tmpWorkingImage.Width * m_ImagePages(m_CurrentPageNumber).CurrentZoom \ 100
pbDisplay.Height = tmpWorkingImage.Height * m_ImagePages(m_CurrentPageNumber).CurrentZoom \ 100
pbDisplay.Image = tmpWorkingImage
Catch ex As Exception
'Todo: remove after testing
Throw ex
If Not tmpWorkingImage Is Nothing Then
tmpWorkingImage = Nothing
End If
End Try
End Sub


Call the method from ResetImage, RotateDisplay and CurrentPageNumber's Set, replacing the line "pbDisplay.Image = m_ImagePages(m_CurrentPageNumber).WorkingImage"

Unlike rotations, I've found no real-world usage need for resetting just the zooms of all images in the page collection so a property more fits this feature than a sub. Move to the "Public Properties" region and add a read/write for the value.


Public Property ZoomPercent() As Integer
If Not m_ImagePages Is Nothing AndAlso m_ImagePages.Length > 0 Then
Return m_ImagePages(m_CurrentPageNumber).CurrentZoom
'default for empty control
Return 100
End If
End Get
Set(ByVal Value As Integer)
If Not m_ImagePages Is Nothing AndAlso m_ImagePages.Length > 0 Then
Dim tmpCurZoom As Integer = m_ImagePages(m_CurrentPageNumber).CurrentZoom
m_ImagePages(m_CurrentPageNumber).CurrentZoom += Value
RaiseEvent ZoomChanged(Me, _
New svImageZoomChangeEventArgs(tmpCurZoom, _
m_ImagePages (m_CurrentPageNumber).CurrentZoom))
End If
End Set
End Property

To help the dev-user make an intuitive GUI, there's a new custom event in that code called "ZoomChanged" which exposes a new EventArgs-derived object. Open the svEventArguments.vb file and add a definition for the svImageZoomChangeEventArgs class:


Public Class svImageZoomChangeEventArgs
Inherits EventArgs
Private m_CurrentZoom As Integer = 100
Public Sub New(ByVal currentZoom As Integer)
m_CurrentZoom = currentZoom
End Sub
Public ReadOnly Property CurrentZoom() As Integer
 Return m_CurrentZoom
End Get
End Property
End Class

Back in the UC, click into the Declarations>Events sub-region and declare the event:

#Region "Events"
Public Event ImageLoaded(ByVal sender As Object, ByVal e As svImageLoadEventArgs)
Public Event PageChanged(ByVal sender As Object, ByVal e As svImagePageChangeEventArgs)
Public Event ZoomChanged(ByVal sender As Object, ByVal e As svImageZoomChangeEventArgs)
#End Region

You'll want that event to fire when images are loaded and reset and as multipage images are displayed into. A raise in the ResetImage method covers the first two situations:


End If
RaiseEvent ZoomChanged(Me, _
New svImageZoomChangeEventArgs(m_ImagePages(m_CurrentPageNumber).CurrentZoom))
 End If

And a raise in CurrentPageNumber's Set wraps it up:


'tell the host
RaiseEvent PageChanged(Me, _
New svImagePageChangeEventArgs(TotalPageCount, _
initialPage,  m_CurrentPageNumber + 1))
RaiseEvent ZoomChanged(Me, _
New svImageZoomChangeEventArgs(m_ImagePages(m_CurrentPageNumber).CurrentZoom)

Rebuild and Save and open the testHarness GUI designer. Add a label (Name="lblZoomInfo", Text="Zoom: 100%"), a textbox (Name="txtZoomAmount" Text="100") and a button (Name="butSetZoom" Text="set zoom").

Double click on the button and fill in its click event, giving it minimal protection by just making sure a number has been entered:

Private Sub butSetZoom_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles butSetZoom.Click
If IsNumeric(txtZoomAmount.Text) Then
SvImageEditor1.ZoomPercent = CInt(txtZoomAmount.Text)
End If
Catch ex As Exception
End Try
End Sub

Use the object and event combos to create the ZoomChanged stub and fill it in:

Private Sub SvImageEditor1_ZoomChanged(ByVal sender As Object, _
ByVal e As Smithvoice.svImageZoomChangeEventArgs) _
Handles SvImageEditor1.ZoomChanged
lblZoomInfo.Text = "Zoom: " & e.CurrentZoom & "%"
End Sub

Rebuild and Save (saving is important here as you'll soon find out). Now have a go with all of the image types, using a negative value to shrink and a positive to expand the image.





It shouldn't take too long for you to come up with three things that need addressing: (1) a style decision; (2) a usability issue; (3) a big honking problem.

1) We've made our zoom additive instead of cumulative so applying 25% twice will bring you to 150% of the original not to a 156.25% (125 + 25percent of 125). Both ways are valid and different imaging tools always use one or the other. If you users scream then it's not a big deal to switch, just change the Zoom property Set to calculate the new percentage based on the current zoom. (You will hit rounding issues so 25% twice followed by -25% won't bring you back exactly unless you change the zoom values to use higher precision than an integer, and even then perfection will take some futzing which is why many image tools end up not hitting the percentages exactly.) For this version we'll keep it simple with additive.

2) Zooming out too far is no help to a user. Load an image and set the zoom to -100, no more image - at 0/0 there's not even a speck for the picturebox border. Letting them know that they can go small is good and keeping it realistic is best. We'll add a minimum limit in a second.

3) Zooming in too far crashes .Net. OUCH!

Next: Handling the unhandleable exception

Robert Smith
Kirkland, WA

added to smithvoice march 2005

jump to:

  • 1) The spec
  • 2) Setting up the workspace
  • 3) Feature 1: Loading an image
  • 4) Custom Exceptions
  • 5) "Fax images" and Multipage TIFFs
  • 6) Custom events
  • 7) Selecting specific fax pages
  • 8) Feature 2: Rotating image displays
  • 9) The most useful tool in GDI+: DrawImage
  • 10) Feature 3: Zooming
  • 11) Handling the unhandleable exception
  • 12) Fixing the squish
  • 13) Zooming to fit the control
  • 14) You're already beating the Pros
  • 15) Feature 4: Cropping
  • 16) Bonus Feature: StickyMouse
  • 17a) Final Cleanup
  • 17b) Passing the current display image
  • 18) Making the application
  • 19) Source and result viewports
  • 20) A better toolbar
  • 21) Hooking the toolbar to the project
  • 22) Adding ImageEditors
  • 23) The toolbar ZoomCombo
  • 24) The final solution
  • 25) Saving to image files
  • 26) An integer-only textbox
  • 27) Passing save options between forms
  • 28) Dealing with that last exception
  • 29) Offer more options with menus
  • 30 The downloads and ebook

  • home     who is smith    contact smith     rss feed π
    Since 1997 a place for my stuff, and it if helps you too then all the better