让非模态对话框处理TAB按键事件的三种方法

让非模态对话框处理TAB按键事件的三种方法

作者:Oscar Cai
时间:2005-9-17 20:17

对话框(Dialog)分为模态对话框和非模态对话框(Modeless Dialog)。非模态对话框不能处理TAB键、快捷键等按键事件,也就是说用户不能在非模态对话框中通过按TAB键切换各控件之间的焦点(Focus)。但这也不是没有解决办法的。

方法一

在MSDN中,让非模态对话框处理TAB按键事件的经典代码如下:

HINSTANCE hinst;
HWND hwndMain;
HWND hwndDlgModeless = NULL;


int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpszCmdLine, int nCmdShow)
{
    MSG msg;
    BOOL bRet;
    WNDCLASS wc;
    HACCEL haccel;
    UNREFERENCED_PARAMETER(lpszCmdLine);

    // Register the window class for the main window.

    if (!hPrevInstance)
    {
        wc.style = 0;
        wc.lpfnWndProc = (WNDPROC) WndProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = hInstance;
        wc.hIcon = LoadIcon((HINSTANCE) NULL, IDI_APPLICATION);
        wc.hCursor = LoadCursor((HINSTANCE) NULL, IDC_ARROW);
        wc.hbrBackground = GetStockObject(WHITE_BRUSH);
        wc.lpszMenuName =  "MainMenu";
        wc.lpszClassName = "MainWndClass";

        if (!RegisterClass(&wc))
            return FALSE;
    }

    hinst = hInstance;  // save instance handle

    // Create the main window.

    hwndMain = CreateWindow("MainWndClass", "Sample",
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
        CW_USEDEFAULT, CW_USEDEFAULT, (HWND) NULL,
        (HMENU) NULL, hinst, (LPVOID) NULL);

    // If the main window cannot be created, terminate
    // the application.

    if (!hwndMain)
        return FALSE;

    // Show the window and paint its contents.

    ShowWindow(hwndMain, nCmdShow);
    UpdateWindow(hwndMain);

    // Start the message loop.

    while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
    {
        if (bRet == -1)
        {
            // handle the error and possibly exit
        }
        else
        {
            if (hwndDlgModeless == (HWND) NULL ||
                    !IsDialogMessage(hwndDlgModeless, &msg) &&
                    !TranslateAccelerator(hwndMain, haccel, &msg))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    }

    // Return the exit code to the system.

    return msg.wParam;
}

其主要解决方案是让非模态对话框hwndDlgModeless的父窗口hwndMain,在其主消息循环中通过调用IsDialogMessage函数来通知hwndDlgModeless TAB按键事件。可是,如果hwndMain没有明确写出的主消息循环,由该怎么办呢?

方法二

譬如,当hwndMain指向的是一个模态对话框时,其WinMain函数通常实现如下:

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <string.h>
/* --- The following code comes from e:/lccwin32/lib/wizard/dlgbased.tpl. */
/*<---------------------------------------------------------------------->*/

/*
   Template for a dialog based application. The main procedure for this
   template is the DialogFunc below. Modify it to suit your needs.
*/
/* prototype for the dialog box function. */
static BOOL CALLBACK DialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
/*
   Win main just registers a class of the same type that the dialog class, and
   then calls DialogBox. Then it exits. The return value is the return value of
   the dialog procedure.
*/

int APIENTRY WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASS wc;
    INITCOMMONCONTROLSEX cc;

    memset(&wc,0,sizeof(wc));
    wc.lpfnWndProc = DefDlgProc;
    wc.cbWndExtra = DLGWINDOWEXTRA;
    wc.hInstance = hinst;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    wc.lpszClassName = "MainWndClass";
    RegisterClass(&wc);
    memset(&cc,0,sizeof(cc));
    cc.dwSize = sizeof(cc);
    cc.dwICC = 0xffffffff;
    InitCommonControlsEx(&cc);

    return DialogBox(hinst, MAKEINTRESOURCE(IDD_MAINDIALOG), NULL, (DLGPROC) DialogFunc);

}

/*
   You should add your initialization code here. This function will be called
   when the dialog box receives the WM_INITDIALOG message.
*/
static int InitializeApp(HWND hDlg,WPARAM wParam, LPARAM lParam)
{
    return 1;
}

/*
   This is the main function for the dialog. It handles all messages. Do what your
   application needs to do here.
*/
static BOOL CALLBACK DialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {
    /* This message means the dialog is started but not yet visible.
       Do All initializations here
    */
    case WM_INITDIALOG:
        InitializeApp(hwndDlg,wParam,lParam);
        return TRUE;
    /* By default, IDOK means close this dialog returning 1, IDCANCEL means
       close this dialog returning zero
    */
    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:
            EndDialog(hwndDlg,1);
            return 1;
        case IDCANCEL:
            EndDialog(hwndDlg,0);
            return 1;
        }
        break;
    /* By default, WM_CLOSE is equivalent to CANCEL */
    case WM_CLOSE:
        EndDialog(hwndDlg,0);
        return TRUE;

    }
    return FALSE;
}

资源文件中,IDD_MAINDIALOG定义的对话框只有标题栏、默认的OK(IDOK)键和Cancel(IDCANCEL)键。对话框的主消息循环在系统API DialogBox中,我们无法动其分毫。于是,只有修改WinMain函数,还得添加三个全局变量:

HINSTANCE hinst;
HWND hwndMain;
HWND hwndDlgModeless = NULL;

int APIENTRY WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASS wc;
    INITCOMMONCONTROLSEX cc;
    MSG msg;
    int res;

    memset(&wc,0,sizeof(wc));
    wc.lpfnWndProc = DefDlgProc;
    wc.cbWndExtra = DLGWINDOWEXTRA;
    wc.hInstance = hinst;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    wc.lpszClassName = "MainWndClass";
    RegisterClass(&wc);
    memset(&cc,0,sizeof(cc));
    cc.dwSize = sizeof(cc);
    cc.dwICC = 0xffffffff;
    InitCommonControlsEx(&cc);

    hInst = hinst;

    hMainDlg = CreateDialog(hinst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC) DialogFunc);

    while( (res = (int)GetMessage( &msg, NULL, 0, 0 )) != 0)
    {
        if (res == -1)
        {
            // handle the error and possibly exit
        }
        else
        {
            if(hMainDlg == (HWND) NULL ||
                    !IsDialogMessage(hMainDlg, &msg) ||
                    hModelessDlg == (HWND) NULL ||
                    !IsDialogMessage(hModelessDlg, &msg))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    }

    if(hModelessDlg != NULL) {
        DestroyWindow(hModelessDlg);
        hModelessDlg = NULL;
    }

    if(hMainDlg != NULL) {
        DestroyWindow(hMainDlg);
        hMainDlg = NULL;
    }

    return msg.wParam;
}

因为将父窗口hwndMain创建为非模态对话框,所以在其主消息循环处除了调用IsDialogMessage函数处理非模态对话框hwndDlgModeless的消息外,还要调用该函数处理hwndMain的消息。另外要特别注意的是,调用DestroyWindow函数销毁对话框hModelessDlghwndMain,只能在退出主消息循环之后进行,千万不能放到对话框过程函数DialogFunc中对消息WM_CLOSEWM_QUIT的处理模块中,否则会引起整个进程陷在主消息循环里,无法退出。在处理消息WM_CLOSE时,只需调用函数PostQuitMessage即可。消息WM_QUIT就不用处理了。以下是让非模态对话框处理TAB按键事件全部代码(除去注释):

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <string.h>


static HWND hModelessDlg = NULL;
static HWND hMainDlg = NULL;
static HINSTANCE hInst = NULL;

#define ErrMsg(s)   MessageBox(NULL, (s), "Caution", MB_OK)

static BOOL CALLBACK DialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);

int APIENTRY WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASS wc;
    INITCOMMONCONTROLSEX cc;
    MSG msg;
    int res;

    memset(&wc,0,sizeof(wc));
    wc.lpfnWndProc = DefDlgProc;
    wc.cbWndExtra = DLGWINDOWEXTRA;
    wc.hInstance = hinst;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
    wc.lpszClassName = "MainWndClass";
    RegisterClass(&wc);
    memset(&cc,0,sizeof(cc));
    cc.dwSize = sizeof(cc);
    cc.dwICC = 0xffffffff;
    InitCommonControlsEx(&cc);

    hInst = hinst;

    hMainDlg = CreateDialog(hinst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC) DialogFunc);

    while( (res = (int)GetMessage( &msg, NULL, 0, 0 )) != 0)
    {
        if (res == -1)
        {
            // handle the error and possibly exit
        }
        else
        {
            if(hMainDlg == (HWND) NULL ||
                    !IsDialogMessage(hMainDlg, &msg) ||
                    hModelessDlg == (HWND) NULL ||
                    !IsDialogMessage(hModelessDlg, &msg))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
    }

    if(hModelessDlg != NULL) {
        DestroyWindow(hModelessDlg);
        hModelessDlg = NULL;
    }

    if(hMainDlg != NULL) {
        DestroyWindow(hMainDlg);
        hMainDlg = NULL;
    }

    return msg.wParam;
}

static int InitializeApp(HWND hDlg,WPARAM wParam, LPARAM lParam)
{
    hMainDlg = hDlg;
    hModelessDlg = CreateDialog(hInst, MAKEINTRESOURCE(DLG_STEP1), hDlg, NULL);
    return 1;
}

static BOOL CALLBACK DialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    int i;

    switch (msg) {

    case WM_INITDIALOG:
        InitializeApp(hwndDlg,wParam,lParam);
        return TRUE;

    case WM_COMMAND:
        switch (LOWORD(wParam)) {
        case IDOK:
        case IDCANCEL:
            PostQuitMessage(0);
            break;
        }
        break;

    case WM_CLOSE:
        PostQuitMessage(0);
        break;
    }

    return FALSE;
}

方法三

还可以用函数FindResourceLoadResourceLockResourceUnlockResourceFreeResource来加载和释放对话框资源,用函数CreateDialogIndirect创建非模态父对话框hwndMain。要使用这种方法的话,大部分代码都相同或类似,但查找、加载、锁定、释放对话框资源这些代码实现起来颇为麻烦,还不如直接调用函数CreateDialog来得方便、简洁。这里就不详细说明了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值