Monday, January 05, 2009
Home
.Net
Return of the Repeater
Complete image control & app
Part 2 - Starting the control
Part 3 - Loading images
Part 4 - Custom exceptions
Part 5 - Fax/Multipage tifs
Part 6 - Custom events
Part 7 - Selecting fax pages
Part 8 - Rotating displays
Part 9 - The power of GDI+
Part 10 - Zooming
Part 11 - .Net's fatal exception
Part 12 - Fix the squishies
Part 13 - Zoom to fit
Part 14 - You beat the pros
Part 15 - Cropping
Part 16 - Bonus: StickyMouse
Part 17 - Finishing touches?
Part 18 - Make the application
Part 19 - Adding viewports
Part 20 - A better toolbar
Part 21 - Connect the toolbar
Part 22 - Adding ImageEditors
Part 23 - Toolbar ZoomCombo
Part 24 - VB3 style ease
Part 25 - Saving images to files
Part 26 - Integer-only textBox
Part 27 - Passing save settings
Part 28 - The last exception
Part 29 - Menus offer more
Part 30 - Book, app & source
Couple of CS Snippets
XML processing quickies
File extension extensions
McAfee.Not
cs IntBox
Floating Holidays
Code snippets
Flexible sorts
Converted deserializations
Autodeploy not found
Autodeploy just stops
VB must be killed
Media file attributes
Fastest file searches
Webservicing custom objects
Aspect correct resize
Funky thumbnails
Wide interval timing
VB2005 or bust?
Recurring events
Single instances
Proper proper casing
Simple address formating
Combo filling reminder
Easy gradient forms
Grrrr on interops?
Winform memory usage
Windows service your ISP
Pretty up VS code printing
Remote configs with no BS
GDI+ printing cd cases
Your own flat combo
Where's the splitter?
Full autoemail
ReessppoonncceeWwrriittee
Kill the back cache
Color to hex
Source to web
Recode without recompile
Prop snippet for VS2008
Database tricks

Your own image control

Originally published December 2002 on DoItIn.net 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
        Try
            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
            
        Finally
            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
        Get
            If Not m_ImagePages Is Nothing AndAlso m_ImagePages.Length > 0 Then
                Return m_ImagePages(m_CurrentPageNumber).CurrentZoom

            Else
                '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

                UpdateZoomDisplay()

                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)
        MyBase.New()
        m_CurrentZoom = currentZoom

    End Sub

    Public ReadOnly Property CurrentZoom() As Integer
        Get
            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

   UpdateZoomDisplay()

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)

    Else
...

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
        Try
            If IsNumeric(txtZoomAmount.Text) Then
                SvImageEditor1.ZoomPercent = CInt(txtZoomAmount.Text)

            End If

        Catch ex As Exception
            MsgBox(ex.ToString)

        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


Print  

pagecomment
  Add Comment



Submit Comment
  View Ratings
50.00%0
40.00%0
30.00%0
20.00%0
10.00%0

Number of Comments 0 , Average of Ratings
  View Comments
No comment.


Privacy Statement  |  Terms Of Use
Copyright 2008 by Robert C. Smith