Introduction
EnableGroupboxControls is a function I have used in several projects,
where I wanted to enable/disable all the controls within a groupbox.
There are several other solutions to this problem here on CodeProject - I
have included a list
here.
None of these, however, had everything I was looking for:
|
Able to be used in MFC or non-MFC project - since I spend a lot of time
maintaining older Windows applications, I wanted a very lightweight solution.
|
|
Able to be used without instantiating class - this was important,
because I necessarily have to minimize the impact on existing code.
|
|
Able to be used without recoding or redefining existing control
variables - again, for same reasons as previous point.
|
|
Able to accommodate nested groupboxes - many of the dialogs
in apps I maintain are non-trivial, because the apps themselves are
highly technical. Nesting groupboxes is one way of coping
with complex user interfaces.
|
EnableGroupboxControls API
Here is the
EnableGroupboxControls function:
int EnableGroupboxControls(HWND hWnd, BOOL bEnable)
{
int rc = 0;
if (::IsWindow(hWnd))
{
TCHAR szClassName[MAX_PATH];
szClassName[0] = _T('\0');
::GetClassName(hWnd, szClassName, sizeof(szClassName)/sizeof(TCHAR)-2);
LONG lStyle = ::GetWindowLong(hWnd, GWL_STYLE);
if ((_tcsicmp(szClassName, _T("Button")) == 0) &&
((lStyle & BS_GROUPBOX) == BS_GROUPBOX))
{
RECT rectGroupbox;
::GetWindowRect(hWnd, &rectGroupbox);
HWND hWndChild = 0;
HWND hWndParent = ::GetParent(hWnd);
if (IsWindow(hWndParent))
hWndChild = ::GetWindow(hWndParent, GW_CHILD);
while (hWndChild)
{
RECT rectChild;
::GetWindowRect(hWndChild, &rectChild);
if ((rectChild.left >= rectGroupbox.left) &&
(rectChild.right <= rectGroupbox.right) &&
(rectChild.top >= rectGroupbox.top) &&
(rectChild.bottom <= rectGroupbox.bottom))
{
::EnableWindow(hWndChild, bEnable);
rc++;
}
hWndChild = ::GetWindow(hWndChild, GW_HWNDNEXT);
}
if (rc && IsWindow(hWndParent))
{
::InvalidateRect(hWndParent, NULL, FALSE);
}
}
}
return rc;
}
EnableGroupboxControls Demo
The
EnableGroupboxControls demo app shows how controls inside a groupbox
may be enabled/disabled:
When the checkbox for groupbox 1 is unchecked, all the controls inside groupbox 1
and groupbox 2 are disabled:
Here is the code that manages these nested groupboxes:
void CEnableGroupboxControlsTestDlg::OnCheck1()
{
UpdateData(TRUE);
EnableGroupboxControls(::GetDlgItem(m_hWnd, IDC_GROUPBOX_1), m_bCheck1);
OnCheck2();
}
void CEnableGroupboxControlsTestDlg::OnCheck2()
{
UpdateData(TRUE);
EnableGroupboxControls(::GetDlgItem(m_hWnd, IDC_GROUPBOX_2), m_bCheck1 && m_bCheck2);
}
Note that there is no linkage between the checkboxes and the groupboxes
except what you see in the above code. The two groupboxes are created in the
dialog template with a field of spaces (instead of text). Then checkboxes are
created and overlaid on the groupbox, so that the visual appearance is that
of a groupbox "controlled" by a checkbox. Here is dialog template
for demo app, with the two checkbox/combobox pairs highlighted:
Overlaying the groupbox header with a checkbox will work only if you
know the trick: the groupbox must precede the checkbox in tab order.
Tab order is simply the order in which controls appear in the dialog template.
If the groupbox came after the checkbox, it would overlay the checkbox,
and the checkbox would not be visible to the user. This works this way
because the order of controls in the dialog template is also the order
in which the controls are created and displayed at run time.
You can set the tab order inside Visual Studio, but with overlapping controls
it is not completely straightforward. Here is what the dialog template looks
like in VS2005, with tab order labels made visible by
menu command
Format |
Tab Order:
The two groupbox headers that are outlined in red are the ones that have been
overlaid by checkboxes. Since the tab order labels for 1 and 2, and 8 and 9,
are displayed over each other, it is difficult to set the tab order in
VS IDE. However, as usual, there is a trick: temporarily move the checkboxes
above or below the groupbox, and then edit the tab order. When you are
finished, move checkboxes back.
Of course, it is not necessary to have checkbox in groupbox header,
in order to make use of EnableGroupboxControls. The button labelled
Disable Groupbox 3 is an example of how to do this.
How to use
Step 1 - Add Files
To integrate EnableGroupboxControls into your app, you first need to
add following files to your project:
- EnableGroupboxControls.cpp
- EnableGroupboxControls.h
The .cpp file should be set to Not using precompiled header
in Visual Studio. Otherwise, you will get error
fatal error C1010: unexpected end of file while looking for precompiled header directive
Step 2 - Add Header File to Your Source Module
In the module where you want to use
EnableGroupboxControls,
include header file
EnableGroupboxControls.h .
Step 3 - Add Code
When user clicks a checkbox (or makes some kind of selection).
add code like I have shown above, to call
EnableGroupboxControls
function.
References
Here are some other articles that discuss managing controls
within a groupbox:
Revision History
Version 1.0 - 2008 April 9
Usage
This software is released into the public domain. You are free to use it
in any way you like, except that you may not sell this source code. If you
modify it or extend it, please to consider posting new code here for everyone
to share. This software is provided "as is" with no expressed or implied
warranty. I accept no liability for any damage or loss of business that
this software may cause.