smithvoice.com
 Y'herd thisun? 

“Some critics of manned space flight on Capital Hill are said to be using their influence to hold down the amount of research on this subject [of Human-affecting radiation in Space]; this could be an indirect way of slowing the pace of flights to the moon and Mars.”

from Space Commerce, Materials Processing by Secretary of the Air Force, Dr. John L. McLucas

Your own image control and App part 1

TaggedCoding, VB, 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.


 

1) The spec

Let's imagine we're in an end user planning session, at the point where all of the company gossip is over and the brown dribbles have dried down the sides of the cold coffee cups. The five real minutes of work in that hour and a half meeting boiled down to the following functional spec:

  1. We need to have a program that lets us load various image files including JPEGs, BMPs, PNGs, GIFs and "Faxes" ("faxes" are always thrown in when we're not listening closely)
  2. We need to be able to rotate the images.
  3. We need to be able to zoom in and out of the images.
  4. We need to be able to select parts of an image and save just the selection as a new image ("crop").
  5. We need to save the rotated/cropped image out to a new image file with various file type options.
  6. We need it done two weeks ago or the company will go under and we'll have to move to India to beg for coins from the only people left in the world doing real tech work.

 

If you'd been down this road with VB6 or earlier then there's a very good chance you ended up having to explain to your company that VB couldn't do the do with complete stability in the time allotted. Saving to different image formats and dealing with multipage Fax tiffs were simply out of reach. And, knowing it wasn't a reflection on you but feeling it was anyway, you surrendered to recommending the purchase of a stupidly expensive 3rd party widget library that made you feel like a hack scripter, never really gave exactly what the users wanted and was made as a one-size-fits-all solution complete with its own mini-GUI that always screamed "I don't belong in this application!"

Some readers will say that they did get hit most of the spec using VB, thanks to the excellent information in the VB3 and VB4 "Power Toolkit" books by Richard Mansfield and Evangelos Petroutsos and with Rod Stephens' "Visual Basic Graphics Programming" and "Advanced Visual Basic Graphics Programming". I loved those books too, I got a lot out of them and out of respect they will always have a home on my bookshelf, but the gutsy parts that offered real useable speed were done with what few graphics API calls VBClassic could work with so calling the result a stable and complete "Visual Basic solution" is a stretch. Gotta face it, "DeviceContext", "GlobalAlloc" and "RTLMoveMemory" are not exactly found in the same Keyword list as "MsgBox", "ElseIf" and "Dim".

But things have changed for Visual Basic. Now, thanks to the latest VB runtime (which we so generously let other managed languages share) a whole lot of real imaging work is possible without having to spaghetti our OOP with the illogical and the arcane. For once Microsoft adding a little plus sign to a technology name didn't mean another step deeper into nerddom.

GDI+ through System.Drawing is one of the best reasons I can think of for a VB6 person to move up to the latest version of Visual Basic, and if you've never had the pleasure then this article is for you. We're going to hit that spec and then some, using a mix of GDI+ and down-home VB RADness.

It's for images, so what's it look like?

Before we fire up the IDE, let's give our heads a visible goal. We know what the deliverable is supposed to do, but for an app that is all about visuals and user interaction it helps to have a vision of what we'll be putting in the hands of those users.

Going back to the spec, "We need to load images..." pretty much says "Pictureboxes" and "...save just the selection as a new image" implies to the experienced developer that the users will be happiest if they can see the original image and the results of their crops and rotations at the same time rather than just seeing the source image and having to save the crops to files that need to be opened for verification.

Something along the lines of this should fit the bill...

(If you like, you can give the final app and control a try to see where we're going. Get the tiny zip here.)

We've got pictureboxes acting as viewports on the right for the source and the left for the result. If the images are zoomed in on far enough so that the picture is bigger than the viewport area then scrollbars should automatically appear allowing the user to move around the rest of the image and the scrollbars should disappear if the image is smaller than the picturebox area.

So the user can get a good look at either version the viewports should be separated by a moveable splitter (again the scrollbars need to adjust for the size).

Above both pictureboxes are a row of buttons for rotations and zooms. The source viewport needs a FileOpen button and the result viewport needs a Save button.

'Sounds easy enough. 'Also sounds like something that could be reused.

The architecture

Here's where a developer's experience kicks in. The users asked for a "program" which is their way of saying that they need a monolithic one-off tool, they only know that they have a current need and it's not their job to worry over the lines you have to type or to see beyond their department at all the other users in the organization. These particular folks however aren't your only customers so you have to look wider and consider if any new project could possibly have uses elsewhere.

Not all solutions have far-reaching application, in fact most don't. You'll always learn something new from any project that you can apply to others later, such as more efficient database connectivity or more robust error logging logic, but that's more likely evolution of style not real code re-use. This imaging spec, though, does "have legs" and because it's so visual (and will probably look fun) it's going to catch the eyes of your other customers who will find a way to need it even if they don't work with graphics.

So a monolithic hard-coded exe might not be the best route and a drop-in component would get you farther in the long run. But where does the component stop and the container begin?

Should your widget include the toolbar buttons? We'd say no because of experience with 3rd party controls that annoyingly force their UIs on us. When LeadTools 12 was released all apps were grey with hard edges but shortly thereafter many corporate screens started getting color to make them look at least from the same decade as MSOffice, we'd update our GUIs but LeadTools popups remained the way they were and instantly it was obvious that something didn't fit. Users like and trust things that fit, and you can reduce many "false bug" reports if you can minimize the number of places where a user says "that looks strange".

Another reason to not do your own buttons is that while it's nice to think you can cover every option conceivable, you just can't do it. A few years back toolbars were boxy 3d, last year they were flat rectangles with dynamic pastel hover shading, this year they're rounded capsules with gradients and next year maybe they'll be irregularly shaped puffy icons. Today and for this customer it makes sense to put the buttons across the top of the viewports, but your next user might prefer them across the bottom or vertically on the sides or even to be exposed in translucent floating toolboxes. You can try to code now for every option, letting your downstream user-dev set a location by an enum and add buttons by an image collection, but you're never going to get it right for everyone and all those options are just more code to test so keep it simple and fully extendable by just coding the methods and raising the events and letting later devs mess with the GUIs.

That brings us to the viewports. It would seem that both of them do the same thing just with different image data. That means we really only need to code one and pop instances into containers as many times as needed. It's a valid point that having a source and destination viewport separated by a splitter wouldn't always be needed or wanted; maybe sometimes each view would be on a different tab or maybe a program would have to zoom and rotate but not crop and save. If we code the viewport as a distinct control we can always wrap yet another control around it and offer a full viewport/splitter/viewport widget, but the viewport itself should be self-contained to give it more chance of simple re-use.

One last question before we open the IDE: inherit or contain? Our viewports are going to allow a number of powerful options but they will always be displaying images. As such, perhaps we could inherit from a vanilla picturebox, letting the dev-user use our component in any place where a normal picturebox would apply and use our extended features if the case warranted. With VB.Net real Inheritance is incredibly easy to do but in this case it's not the best plan. Remember that we're going to offer zooming and if the zoomed image is bigger than the visible area of the control we'll want scrollbars to appear; A picturebox won't give us that feature without lots of tedious hack coding... but a UserControl will. Like a form, if you set the UserControl AutoScroll property to true and put visual children on it and they extend beyond the client area then scrollbars will be drawn for you no fuss no muss. So our viewport code will harken back to the age of Visual Basic 5.0 and contain a picturebox for image displays.

Now we're ready to start VB.

Next: Setting up the workspace

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