|

  |
Source Code Note
|
  |
  |
 |
This OCX is a binary compatible component which works with all other samples.
If you compile your own copy of this OCX yourself please make sure you change
the name. See disclaimer and license for more details.
|
  |
Overview
It allows you to use any form as a drop-down window, and optionally to drag
the form off and float it over the form. The form behaves just like the drop-down
windows in Office, albeit you cannot currently dock them (yet!) The code includes a VB
titlebar modifier class which ensures that any owned forms with the
WS_EX_TOOLWINDOW style
(i.e. VB WindowStyles 4 and 5) shown in a VB app do not take focus away
from the owner form's titlebar - and the form also stays active in the
TaskBar too. This functionality is completely
generic and works regardless of how many taskbar forms you have in your app.
Please Release Me
The preview of this control was launched back in August, with the aim of a release a month later
Unfortunately, the preview version hit a snag. When you showed a drop-down window,
the tasklist switched so it looked like your form was inactive. It doesn't sound like much,
does it? Surely you could fix that with a couple of quick API calls? Well, that just didn't
happen. The problem with this sort of thing is that whilst VB performs incorrectly, the
implementation is hidden and there is no way of working out what it is doing. And the
implementation relies on certain messages being passed correctly, so although it was possible
to solve parts of the problem (keep the form's titlebar in focus or keep the task bar icon
highlighted), many of the solutions had consequences on VB's ability to track the focus
control or form. These consequences vary from the tedious (focus always switches to
an MDI child form, or have to click twice on a control to make it operate) to the
dramatic (blue screen of death, Kernel32.exe faults, white GPF screens, deadcafe... I've had
every single one now!)
The Question, or the Answer?
Anyway, seven months later, I spotted a Q&A article by Karl Peterson
in VBPJ, where he writes an "Ask the VB Pro"
column. The question in question was how to set your application's icon so it always shows
correctly in the Alt-Tab list. The interesting thing about the article was it described how
VB creates a hidden window (with the class name ThunderMain) which is the
ultimate owner of all other VB windows in a VB application.
Hmmm... So would that affect the TaskBar, then?
You can find the window if you fire up Spy++ and point it at a top-level VB form
and then look for the parent window, or, if you're in VB, you can get the window handle using the
GetWindow API call:
hWndThunderMain = GetWindow(Me.hWnd, GW_OWNER)
It took some playing to work out that you can prevent
the Taskbar icon from losing its highlight by making the ThunderMain
window invisible when you are about to show an owned window. A bit more
playing revealed that the perfect behaviour was achieved if you instead just
stripped the WS_VISIBLE style bit at the appropriate time, the
appropriate time being when the ThunderMain window gets a
WM_WINDOWPOSCHANGING message, because it is about to modify the ZOrder
position and when the window returned by the GetActiveWindow
API call is owned by another VB window rather than ThunderMain.
Cool! Three or four versions later, and finally here is the code: a full, generic
implementation of the MS Office integration of its main
window with Win32, which works on Win95,98,NT and 2000.
Pregnant - by the Boy I was Babysitting
Since VB operates by only setting up one ThunderMain window per VB application, this results
in a tricky coding problem when it comes to add the subclass. If every window
just adds a subclass to the ThunderMain window, then you end up processing the
same message multiple times when a window is shown. This works fine when you
have one window in the application - but of course, that isn't a problem anyway!
When you have two windows, you get into problems, particularly when the windows
have contradictory intentions about what to do with the taskbar icon -
and they start "fighting" each other, causing your VB app to go into
a flickering loop which can only be stopped by extreme use of Task Manager :)
The solution to this problem is to ensure that regardless of the source object,
only one subclass is installed, and its processing works without reference to
the object which installed the subclass (otherwise you need to fire the same
message to multiple objects, and they could return conflicting results).
Whilst
the Subclassing and Timer Assistant component
available from this site solves most subclassing issues, it doesn't solve this one,
so some customised subclassing code was needed to ensure the object behaves in
this manner. You can find this code in the module mTitleBarMod.bas within
the Drop-Down form control. Note that if any other component within your
application needed to subclass the hidden VB window, you could run into problems
with this subclassing approach: it would no longer be guaranteed to receive
messages in the same order and hence the focus processing could be lost.
The Drop-Down Form Package
This object is compiled as an ActiveX control with two constituent controls:
-
vbalTitleBarModifier
This component is invisible at runtime and installs the ThunderMain
subclasser discussed above to ensure the titlebar activation is correct. This
component should be added to all main forms in your application.
In use, this component has only one method; Attach. You pass in the
hWnd of the form you are trying to show as a parameter and the control
then does all the rest of the work.
-
vbalDropDownClient
This component acts as a replacement titlebar on windows with VB WindowStyle
4 or 5 and the Caption property set to blank and ControlBox set to False.
It provides the automatic hide functionality when the form is set to drop-down and
also enables the form to be "dragged-off" and floated on the screen. It
also provides methods to restrict the size of the window on screen and can
be used to auto-hide floating toolwindows when the user switches to anther
application similar to the behaviour of Word and Excel.
This component provides a number of useful events to your dropdown window:
-
AppActivate - Raised whenever the application is activated or inactivated.
You can hide/show the tool window here to emulate the behaviour of Office.
-
DeactivateForm - Raised whenever focus switches from the ToolWindow
to another form in the application. If the window is in the drop-down state,
then the control post a WM_CLOSE message to the form, which has the
same effect as if the user chose the System Menu close option.
-
CaptionResize - Raised when the control has resized the caption, for
example, when the form switches between the drop-down and floating states. This
event enables you to position the controls on the form to the appropriate point.
-
Moving - Raised whenever the form is moved. You can control the
position of the form by modifying the left, top, width and height parameters of
this event.
-
RightClick - Raised whenever the user right clicks on the control.
-
Sizing - Raised whenever the form is sized. You can control the
position and size of the form by modifying the left,top,width and height
parameters of this event.
The control's behaviour can be customised using the properties and methods:
-
AllowResize
This property enables you to create a form which
behaves like the Fixed ToolWindow but with the more pleasant 3D border
provided by a Sizable ToolWindow.
-
AllowTearOff
Determines whether you can tear-off the control
when it is in the drop-down state.
-
Caption
Gets/sets the caption of the Tool Window.
-
MoveOnFormMouseDown
If set to True, enables mouse clicks on the
form to start the form moving as if it was a click on the titlebar.
-
ShowState
Gets/sets the current state of the Tool Window - hidden,
shown in the drop-down position or shown floating.
For more information on the techniques used to detect sizing and activation
changes in these controls, check out these articles:
Showing Drop-Down Forms from Modal Forms
One problem with this technique is that you cannot use it directly from a form shown with
modally in VB, otherwise you get the error message "Can't show non-modal form when modal
form is displayed". There is a workaround for this problem by which you can emulate
showing the form modally. Check out the article Emulating
Modal Forms. This technique has the additional advantage in that it allows you to
create VB projects which have an Internet Explorer style New Window option, but showing a
modal form in one window does not block execution in the other.
|
  |