 |
How Did He Do That?
(This page is for programmers)
Occasionally I get asked how I implemented some interesting program feature in code, since they
may want to do something similar. Very often, the seed idea for what I do comes from other similar
programs, programming newsgroups, tech e-mails, reference books, or non-related software I've written before.
Since I've so often been helped by others, I've added this web page for those programmers who may
want to implement some of the things they find useful in PicViewPlus.
All the code that follows is in Microsoft Visual Basic .NET. It should be easily convertable to
C#.
The Windows XP "Look" of Controls
By default, applications built with the .NET architecture have the standard, if somewhat
drab appearing, set of controls used in years past. In contrast, Microsoft written .NET
applications for Windows XP often sport an "XP Themes" look to them. Unfortunately, there is no easy
way to get this new look in your controls when using Microsoft's Visual Studio environment.
But with a little effort, there is a way to do it. John Robbins discusses it in
his posting on his website.
Unfortunately, it omits some key information. Karl Moore admits he spent quite a while
trying to get this feature to work. See his article also.
Karl give a whole series of steps. Unfortunately, his directions are over-complicated and
some steps simply don't make sense.
So, after reviewing both writeups, and trying it myself, this is what you have to do.
- Karl is right on when he says the first step is, where possible, to set the FlatStyle
property of your controls to System. This is important. (Strangely, John omits this step)
- Both Karl and John agree you need a manifest file. It is an XML file and should
be placed in your application's Bin directory, where your Exe file resides. But both Karl
and John give different and incorrect naming advice. If your program name is MyProg, your
Exe would be MyProg.Exe, and the manifest file should be named MyProg.Exe.manifest.
In design mode, your controls will look no different from before. They will only have the new
XP look when you run your program.
Use the following text in your manifest file, replacing MyProg and MyProg Description
with the name and description of your application. It doesn't seem to make any difference
what the version is.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="X86"
name="MyProg"
type="win32"
/>
<description>MyProg Description</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>
Selecting a Rectangular Area
The ability to zoom into an arbitrary rectangular area is really handy, especially when you
can dynamically show this rectangle as the user creates it with the mouse. The first step is
to create a MouseDown event for the picture box control, as this event defines
a vertex of the selection rectangle:
Private Sub picSlide_MouseDown(ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) Handles picSlide.MouseDown
'Get the MouseDown click coordinates and save them in public variables for use
'during subsequent MouseMove events (where we draw a selection rectangle)
MouseDownX = e.X
MouseDownY = e.Y
'Initialize for rectangular area generation
MouseOldX = MouseDownX
MouseOldY = MouseDownY
MouseDownViewRectX = ViewRectX
MouseDownViewRectY = ViewRectY
'Get the button that was pressed along with the mouse click.
Dim ShiftCtrlAlt As Short = System.Windows.Forms.Control.ModifierKeys \ &H10000
'ShiftCtrlAlt is set as follows:
' Shift-
' CtrlAlt Keyboard key pressed simultaneously
' ------- -----------------------------------
' 1 Shift
' 2 Ctrl
' 3 Shift and Ctrl
' 4 Alt
' 5 Shift and Alt
' 6 Ctrl and Alt
' 7 Shift and Ctrl and Alt
'Set the mouse mode up here
MouseDownShiftCtrlAlt = ShiftCtrlAlt
End Sub
The MouseMove event code does all the work for the visual display. The key point to keep in
mind here is that all draws to the screen are done in complementary move. This has two benefits.
One is that redrawing a line simply restores the original graphics under the line. The other is
that no matter what color the underlying graphics, the selection rectangle line will always be
visible, since its color will be "opposite" to that of the underlying color.
Private Sub picSlide_MouseMove(ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) Handles picSlide.MouseMove
'Get the mouse-click coordinates
Dim MouseX As Integer = e.X
Dim MouseY As Integer = e.Y
'-----------------------------------------------------------------------
'Now check to see if the user is dragging the mouse to make a zoom frame
'-----------------------------------------------------------------------
'Get the mouse button that the user clicked.
Dim MouseButton As Short = e.Button \ &H100000
'MouseButton is set as follows:
' Mouse
' Button Key pressed
' ------ -------------------
' 1 Left mouse button was clicked/is pressed
' 2 Right mouse button was clicked
' 4 Wheel/center button was clicked
'MouseDownShiftCtrlAlt is Public
If MouseDownShiftCtrlAlt = 0 Then
If MouseButton = 1 Then
'From the microsoft.public.dotnet.framework.drawing newsgroup,
' re: ControlPaint.DrawReversibleFrame
Dim ZoomFrame As Rectangle
'Draw the frame that is associated with the old mouse position,
' effectively erasing it, but only if there is a frame already there
If (MouseDownX <> MouseOldX) Or (MouseDownY <> MouseOldY) Then
ZoomFrame = picSlide.RectangleToScreen(New Rectangle(MouseOldX, MouseOldY, _
MouseDownX - MouseOldX, MouseDownY - MouseOldY))
ControlPaint.DrawReversibleFrame(ZoomFrame, Me.BackColor, FrameStyle.Thick)
End If
'Don't draw a selection rectangle unless its bigger than a minimum size
If (Math.Abs(MouseX - MouseDownX) > 10) Or (Math.Abs(MouseY - MouseDownY) > 10) Then
'Draw the frame that is associated with the new mouse position
ZoomFrame = picSlide.RectangleToScreen(New Rectangle(MouseX, MouseY, _
MouseDownX - MouseX, MouseDownY - MouseY))
ControlPaint.DrawReversibleFrame(ZoomFrame, Me.BackColor, FrameStyle.Thick)
'Update the old mouse position
MouseOldX = MouseX
MouseOldY = MouseY
'Note that if we drew a regular rectangle here, we could only see it if we did
' a picSlide.Refresh(), but doing that using ControlPaint.DrawReversibleFrame
' tries to erase our frame
Else
'Initialize to no rectangle drawn
MouseOldX = MouseDownX
MouseOldY = MouseDownY
End If
End If
End If
End Sub
Finally, the MouseUp event erases the selection rectangle, gets the selection rectangle
coordinates, and takes the appropriate graphics action.
Private Sub picSlide_MouseUp(ByVal sender As Object, _
ByVal e As System.Windows.Forms.MouseEventArgs) Handles picSlide.MouseUp
'Pan and/or Zoom into/outof the image
If MouseDownShiftCtrlAlt = 0 Then
'If there is a selection frame, erase it by redrawing it
' (since the drawing is done in "invert" mode)
Dim ZoomFrame As Rectangle
'Draw the frame that is associated with the old mouse position, effectively erasing it,
'but only if there is a frame already there
If (MouseDownX <> MouseOldX) Or (MouseDownY <> MouseOldY) Then
ZoomFrame = picSlide.RectangleToScreen(New Rectangle(MouseOldX, MouseOldY, _
MouseDownX - MouseOldX, MouseDownY - MouseOldY))
ControlPaint.DrawReversibleFrame(ZoomFrame, Me.BackColor, FrameStyle.Thick)
End If
End If
'Get the mouse-click (mouse up) coordinates
Dim MouseX As Integer = e.X
Dim MouseY As Integer = e.Y
'Get the mouse button that the user clicked.
Dim MouseButton As Short = e.Button \ &H100000
'MouseButton is set as follows:
' Mouse
' Button Key pressed
' ------ -------------------
' 1 Left mouse button was clicked
' 2 Right mouse button was clicked
' 4 Wheel/center button was clicked
'Check for the Left mouse button
If MouseButton = 1
' (MouseX,MouseY) and (MouseDownX,MouseDownY) define the two corners
'of the selection rectangle. i.e., get the size of the zoom rectangle
Dim ZoomRectangleSizeX As Integer = Math.Abs(MouseX - MouseDownX)
Dim ZoomRectangleSizeY As Integer = Math.Abs(MouseY - MouseDownY)
'Using the zoom rectangle, take some graphics action here
End If
End Sub
|