BUG: Enabling Accelerator Keys in Visual Basic ActiveX Controls
ID: Q189779
|
The information in this article applies to:
-
Microsoft Visual Basic Enterprise Edition for Windows, version 5.0
SYMPTOMS
Using an ActiveX control (or UserControl) that is created with Visual Basic
5.0 in an MFC container application, the accelerator keys (or access keys)
defined for the child windows in the ActiveX control do not work as
expected.
CAUSE
Because both Visual Basic and MFC provide different mnemonic handling
implementations, Visual Basic needs to change the UserControl's accelerator
tables in order for the accelerator keys to work properly in an MFC
container. For more information, see the REFERENCES section of this
article.
RESOLUTION
The workaround is to call the EnumChildWindows() API to enumerate all the
child windows of the ActiveX control from the container application. Then,
determine the mnemonic character key of each child window based on the HWND
that is passed to the callback function in the EnumChildWindows()call.
STATUS
Microsoft has confirmed this to be a bug in the Microsoft products listed
at the beginning of this article. We are researching this bug and will post
new information here in the Microsoft Knowledge Base as it becomes
available.
MORE INFORMATION
The steps below show how to set up a callback function for the
EnumChildWindows() API from the MFC container application; determine the
access keycode for the child windows of the Visual Basic ActiveX control;
and determine which control to set the input focus to when an accelerator
key is pressed:
- Place the Visual Basic ActiveX control in an MFC dialog-based
application.
- Add a member variable named m_XControlInfo to the CDialog-derived class
as below:
#include <afxtempl.h>
// Information about child controls in the Visual Basic
// ActiveX control.
typedef struct xcontrol_info
{
HWND hwnd; // handle of child control
char mnemonic_char; // mnemonic character key for the control
}
XCONTROL_INFO;
class CTestVBDlg : public CDialog
{
...
// Array of info for child controls.
CArray<XCONTROL_INFO, XCONTROL_INFO> m_XControlInfo;
...
}
- Enumerate all the child windows of the Visual Basic ActiveX control in
OnInitDialog() of your CDialog-derived class. This callback function
will be called for each child control in the ActiveX control. The HWND
and the mnemonic character keycode of the child control will be stored
in the m_XControlInfo array:
// Callback function for EnumChildWindows() API
BOOL CALLBACK EnumActiveXControlChildProc(HWND hwnd, LPARAM lParam)
{
CArray<XCONTROL_INFO, XCONTROL_INFO>* arr =
(CArray<XCONTROL_INFO, XCONTROL_INFO>*)lParam;
CString str;
CWnd* control = CWnd::FromHandle(hwnd);
control->GetWindowText(str);
int index = str.Find('&'); // look for mnemonic
// character keycode
if (index > -1)
{
XCONTROL_INFO info;
info.hwnd = hwnd;
info.mnemonic_char = str[index+1];
arr->Add(info);
}
return TRUE;
}
BOOL CTestVBDlg ::OnInitDialog()
{
CDialog::OnInitDialog();
...
// Setup a callback function for EnumChildWindows() API to
// enumerate all child windows of the ActiveX control.
HWND hcontrol = GetDlgItem(IDC_VB_CONTROL1)->GetSafeHwnd();
EnumChildWindows(hcontrol, EnumActiveXControlChildProc,
(LPARAM) &m_XControlInfo);
return TRUE;
}
- Modify the OnSysCommand() function of your CDialog-derived class for
determining which child window in your ActiveX control should receive
the input focus when an accelerator key is pressed:
void CTestVBDlg ::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
// If user pressed an accelerator keystroke, loop through the
// array to find out if it is one of the access keys for a
// child control in the Visual Basic ActiveX control.
BOOL trap_accel = nID == SC_KEYMENU;
if (trap_accel)
{
int size = m_XControlInfo.GetSize();
for (int i = 0; i < size; i++)
{
if (toupper(LOWORD(lParam)) ==
toupper(m_XControlInfo[i].mnemonic_char))
{
HWND child = m_XControlInfo[i].hwnd;
::SetFocus(child);
::SendMessage(child, BM_CLICK, 0, 0L); // OPTIONAL!!!
}
}
}
if (!trap_accel)
CDialog::OnSysCommand(nID, lParam);
}
}
Steps to Reproduce Behavior
- Create an ActiveX control with Visual Basic 5.0. The ActiveX control has
two Command buttons as its child windows called "Button &A" and "Button
&B," where A and B are the access keys for the buttons.
- Use the control in a Visual Basic form and note the access keys work as
expected.
- Use the control in an MFC container application and note the access keys
do not work. You will only get a message beep sound when the access key
is pressed.
REFERENCES
For information about defining access keys for a control, please refer to
the Online Help in Visual Basic.
For additional information, please see the following article in the
Microsoft Knowledge Base:
Q145698 PRB: Mnemonic Differences Between MFC 4.0 and Visual Basic 4.0
(c) Microsoft Corporation 1997. All Rights Reserved.
Contributions by Yeong-Kah Tam, Microsoft Corporation
Additional query words:
ocx kbOLE kbActiveX kbCtrl kbVBp500bug kbdsd kbMFC kbDSupport
Keywords : kbGrpVB
Version : WINDOWS:5.0
Platform : WINDOWS
Issue type : kbbug