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 specLet'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: - 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)
- We need to be able to rotate the images.
- We need to be able to zoom in and out of the
images.
- We need to be able to select parts of an image and save
just the selection as a new image ("crop").
- We need to save the rotated/cropped image out to a new
image file with various file type options.
- 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 ziphere.) 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 architectureHere'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: |