smithvoice.com
 Y'herd thisun? 

“It's better to be unofficial till you get the facts”
-Lt. Columbo



Your own image control and App part 3

TaggedCoding, Imaging

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.


 

3) Feature 1: Loading an image

Although it's tempting to just dive in and write routines, we're going to set up housekeeping first; It's easier now than later. Switch to codeview of the svImageEditor UC.

Loading a file of course means disk IO so to cut down on the dots in the code add an "Imports System.IO" to the top of the class file.

Next, under the "Windows Form Designer generated code" region, create three new custom Regions called "Declarations" (using VB's traditional name for the non-method code area), "Public Methods" and "Private Methods".

Regioning is sometimes taken to the Quicken-Addiction extreme and gets to the point of diminishing returns, but in my experience it does come in handy to create sub-regions; I've found that going two levels deep is usually more than enough. In the "Declarations" region create a sub-region named "Private Variables".

While we are going to be using a picturebox for image displays, there will be times after rotating/zooming when our users will want to revert to the original so we could get trapped in a corner if we just dump images into the picturebox and manipulate them through the pbDisplay.Image property. Click inside the Private Variables sub-region and add an image variable named m_OriginalImage, setting the object variable initially to nothing.

Now click inside the Public Methods region and add the "LoadImage" routines, two overloaded versions to accept either an Image object or a file path string. You could make these subs but we prefer to make such methods Functions and have them return a boolean value. This way, even if the user-dev doesn't do any exception handling and doesn't check the return they can't say we didn't give them ample opportunity.

The version that accepts a file path simply uses IO to verify the existence of the file and tries to read the file's image data using the shared Image.FromFile(). If that works then the object is passed to the other version of LoadImage. This version clones the image object and puts the copy in the picturebox for display. You want to use Cloning instead of just setting a reference to the original Image instance because we want m_OriginalImage to be a distinct and pristine object.

Because setting the picturebox display from a clone of the saved image will probably be used in a number of places, we'll put the functionality in its own method. Click in the Private Methods region and add a sub called "ResetImage" to do the work.

Your UC code should now look like this:

 

#Region "Declarations"
  
#Region "Private Variables"
Private m_OriginalImage As Image = Nothing
  
#End Region
  
#End Region
  
#Region "Public Methods"
  
Public Function LoadImage(ByVal img As Image) As Boolean
 Try
 m_OriginalImage = DirectCast(img.Clone, Image)
 ResetImage()
 Return True
  
 Catch ex As Exception
 'todo:  test to figure errors
 '       and remove catch-all
 Throw ex
  
 End Try
 
End Function
  
Public Function LoadImage(ByVal fileName As String) As Boolean
If File.Exists(fileName) Then
 Try
 Return LoadImage(Image.FromFile(fileName))
  
 Catch ex As Exception
 'todo:  test to figure errors
 '       and remove catch-all
 Throw ex
  
 End Try
  
Else
 Throw New FileNotFoundException
  
End If
  
End Function
  
#End Region
  
#Region "Private Methods"
  
Private Sub ResetImage()
If Not m_OriginalImage Is Nothing Then
 pbDisplay.Image = DirectCast(m_OriginalImage.Clone, Image)
  
End If
  
End Sub
  
#End Region

 

Drag and Drop in a custom control?

Windows is all about drag & drop so shouldn't we add it?

Drag & drop loading is simple boilerplate code so it's very tempting but I'd say that usually you don't want to embed it into a control. If the dev wants to allow dragdrop on the control or on the entire form they can, using their own code they only need to pass the dropped filename to the LoadImage function. If you put the same code into your control then two loads will take place and two ImageLoaded events will fire. In the worst case a race condition could mess up their app and at least the dev and end user could get confused.

There's another reason embedding DragDrop isn't advised for controls: Exception handling. The events are being dynamically worked inside of the UserControl so the dev-user can't set a Try...Catch for them. That means when an invalid file is dropped and the LoadImage hits an exception it goes nowhere useful. It's a common quandary, typically the routes taken are: (1) Check the file during the DragEnter and if it's not valid then turn off the drag cues and don't accept the drop; (2) Define and raise a new custom event that the dev-user can code a messagebox with; (3) Eat the exception. In my experience the first option is a wasteful performance hit and the second sounds nice but dev-users usually won't bother trapping the event.

Since in most cases a DragDrop will be applied to an entire form, avoid the temptation and don't handle it in your control.

Rebuild and Save the solution.

Now add the unit-test code to testHarness. Bring up the Form1 designer and add a button, name it "butLoadImage" and give it the text value "Load". Double click on the button to call up its click event stub and add the code to let the user pick an image file and pass the path string to the UC's LoadImage function:

 

Private Sub butLoad_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles butLoad.Click
  
Dim fo As New OpenFileDialog
  
With fo
 .Filter = "Images|*.jpg;*.gif;*.bmp;*.png;*.tif; *.tiff"
  
 If .ShowDialog = DialogResult.OK Then
 Try
 SvImageEditor1.LoadImage(.FileName)
  
 Catch ex As Exception
 MsgBox(ex.ToString)
  
 End Try
  
 End If
  
End With
  
If Not fo Is Nothing Then
 fo = Nothing
  
End If
  
End Sub

 

F5 to run testHarness and give the code a try. Because you gave the svImageEditor1 instance Anchors on all sides you can resize the form and see that the AutoScroll property kicks in and draws scrollbars when the image is bigger than the control's area.

Unit-testing isn't just for proving your code works whenyou use it, it's for banging on the way a real dev-user might, with attention to how they'll use it when they're in a hurry. Use form level DragDrop code. Try different variations of the testHarness code, checking the return value and not, trying all the different image types, forcing a path string that doesn't point to a file and whatever else you can think of. Make sure you try loading an invalid existing file; you can do this by renaming a non-image file with an image extension to see what happens with the current code.

If the dev-user put in even the most basic exception handling, trapping for any and all exceptions and raising the ex.message to the end user then you'd be okay with your catch-all code, but there are really two problems; (1) When you leave a catch-all in your code the complier adds a significant amount of lines to cover all of the possible runtime bases, it doesn't add much to the assembly's file size but it does add overhead as tests are run against every generic condition including ones that might have no possible relation to your functionality; (2) How does "Out of Memory" help the end user figure out what went wrong? They may be stupidly trying to open a Word doc but not telling them enough information to let them see their error is just asking for them to say that you screwed up and reporting a "bug".

We'll take care of the issue with a custom exception that will let both the dev-user and end user get right to the root of the problem.

Next: Custom Exceptions

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 smithvoice.com