Introduction
This set of classes is a set of item separators to be used in a dialog box.
They look like what is used in the options of Office 2000/XP for instance with
additional features. The set include four controls:
CSeparator
: simple control that just draws a bevelled line
CStaticSeparator
: static text with a bevelled line on its right
CCheckBoxSeparator
: checkbox with a bevelled line on its right
that can automatically enable/disable a set of controls
CSectionSeparator
: static text with a bevelled line on its
right that can automatically hide/show a set of controls.
In the above picture the controls appear in the following order:
CStaticSeparator
, CCheckBoxSeparator
,
CSectionSeparator
and CSeparator
. In the second
screenshot the CCheckBoxSeparator
has been clicked (enabling the
About... button) and the CSectionSeparator
has been collapsed.
Usage
To use these controls it's really simple: simply draw them in the dialog
editor and assign a value to them using the class that you want. Do not forget
to size your controls properly: the line will extend to the right edge of the
control.
In the dialog editor use a static for CSeparator
,
CStaticSeparator
and CSectionSeparator
. Use a checkbox
for CCheckBox
Separator
.
CSeparator
and CStaticSeparator
do not require any
more steps. CCheckBoxSeparator
and CSectionSeparator
needs to be setup to know on which controls they should perform their
action.
The simplest method is to use
SetNextSectionId(int id)
which is available in both classes. Pass as the parameter the ID of the control
that defines the begining of the next section. All the controls between the
separator itself and the ID passed will be under control of the separator. If
your separator is the last section of the dialog then pass -1 and all the
controls under the separator will be affected.
This has to be done in the InitDialog
of your dialog. For the
CCheckBoxSeparator
do not forget to call
UpdateControlledDlgItems()
so that the items will be properly
disabled/enabled depending on the state of the checkbox. For
CSectionSeparator
you can call Collapse()
on it if you
want it to appear initially collapsed.
The implementation is pretty simple so I won't talk long about it except for
two points. Here they are.
The drawing
A short word on how the bevelled line is drawn. Thanks to David Y. Zhao and
his XP Visual Style
support files [^], we are able to draw a bevelled line supporting the XP
theme under Windows XP. We just open the theme data and tell the theme data to
draw itself in our DC. If the application is not themed then we use standard CDC
calls to draw the line and the bevel.
As MSDN states, a good control should handle WM_PRINT
and
WM_PRINTCLIENT
properly. So that's I did and OnPaint
just calls the Print handler. Moreover we need this to be properly
implemented for a flicker free drawing during animation (see below).
The (un)collapsing animation
The animation done when a CSectionSeparator
is (un)collapsed was
probably the most tricky part. I wanted a smooth flicker free animation and it
was more difficult than I thought.
First thing was to use
BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos
to avoid
moving each control one after the other. Using these functions (available in the
Win32 API) we group the controls we want to move and Windows handles the
operation all at once in the EndDeferWindowPos
. As we resize the
window during the animation, we tell the EndDeferWindowPos
not to
redraw the controls and we tell them to redraw the entire window when we resize
it.
To remove flickering, I used the double buffer trick: tell all the controls
to draw themselves in a memory DC (using WM_PRINT
message) and then
BitBlt
the memory DC into the dialog's DC. I wanted to remove the
repaint flag on the parent when resizing it as I was redrawing the entire window
but when I did that then the underlying windows were NOT updated anymore. So I
had to keep it. The dialog is kind of drawn twice but it seems not to be an
issue.
Finally as we want a constant time animation we just time it properly. If we
are ahead of time the we just wait a bit (with a PumpMessage
like
loop to avoid the application to freeze and to let the underlying windows
repaint themselves). If we are late then we advance the frame counter in order
to skip the frames we missed.
For timing I use the performance counters for a precise measuring. More info
on performance counters can be found here [^].
Settings
You can tweak the animation by changing the constants defined at the top of
SectionSeparator.cpp. You can change the number of frames per second
and the length of the animation. Use 0 to run it as fast as it can. Use -1 to
use no animation and have an instant collapse/uncollapse effect.
Conclusion
I think I covered everything I wanted. Hope you will like those separators
and use them in you next app! Sorry I do not have Visual Studio 6.0
installed anymore, so I can't provide a dsw/dsp for the demo project. Still the
release binary is included so you can still take a look.
History
- Updated Dec 16 2003 - Added support for XP themed property pages