Timers and Animation

theForger's Win32 API教程第二版(简体中文)

主页

基础
  1. 开始学习
  2. 一个简单的窗口
  3. 处理消息
  4. 理解消息循环
  5. 使用资源
  6. 菜单和图标
  7. 对话框
  8. 非模态对话框
  9. 标准控件
  10. 对话框常见问题
创建一个简单应用
  1. 在运行时创建控件
  2. 文件与常用对话框
  3. 工具栏与状态栏
  4. 多文档界面
图形设备接口
  1. 位图,设备上下文
  2. 透明位图
  3. 定时器与动画
  4. 文本,字体与顏色
工具与文档
  1. 参考
  2. 免费的Visual C++(最新更新)
附表
  1. 常见错误的解決方法
  2. API vs. MFC
  3. 关于资源文件的说明

无模态对话框

[images/dlg_two.gif]

范例:dlg_two

  现在我们来看看CreateDialog(),它是DialogBox()的姐妹函数.区別在于DialogBox()拥有自己的消息循环并且直到对话框关闭才返回,CreateDialog()则更加像CreateWindowEx()创建的一个窗口,立即返回并向你的消息循环发送消息,就像是你的主窗口发的消息样.这就是所谓的无模态,而DialogBox()创建的是模态对话框.

  你可以像上个例子样来创建对话框,你也需要设工具窗口的扩展风格来给你的标题栏一个典型的小些的工具栏.我创建的资源如下:

IDD_TOOLBAR DIALOGEX 0, 0, 98, 52
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION
EXSTYLE WS_EX_TOOLWINDOW
CAPTION "My Dialog Toolbar"
FONT 8, "MS Sans Serif"
BEGIN
    PUSHBUTTON      "&Press This Button",IDC_PRESS,7,7,84,14
    PUSHBUTTON      "&Or This One",IDC_OTHER,7,31,84,14
END

  你可以看到资源编辑器把DIALOG換成了DIALOGEX表明我们要为我们的对话框设置扩展风格.

  接下来我们想在我们的程序运行的时候创建对话框,我们想要我们的对话框可视,所以我们在WM_CREATE的消息处理中创建它.我们也需要声明一个全局变量来保持从CreateDialog()返回的窗口句柄以便在后面使用它.DialogBox()不向我们返回句柄因为DialogBox()在窗口销毀的时候才返回.

HWND g_hToolbar = NULL;
    case WM_CREATE:
        g_hToolbar = CreateDialog(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_TOOLBAR),
            hwnd, ToolDlgProc);
        if(g_hToolbar != NULL)
        {
            ShowWindow(g_hToolbar, SW_SHOW);
        }
        else
        {
            MessageBox(hwnd, "CreateDialog returned NULL", "Warning!",  
                MB_OK | MB_ICONINFORMATION);
        }
    break;

  我们要检查返回值,这什么时候总是一个好的习惯,如果是正确的(不为NULL)我们就用ShowWindow()来显示这个窗口,要是用DiaglogBox(),这一步就是不必要的,因为系统为我们调用了ShowWindow().

  现在我需要为我们的工具栏写一个对话过程.

BOOL CALLBACK ToolDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
    switch(Message)
    {
        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case IDC_PRESS:
                    MessageBox(hwnd, "Hi!", "This is a message", 
                        MB_OK | MB_ICONEXCLAMATION);
                break;
                case IDC_OTHER:
                    MessageBox(hwnd, "Bye!", "This is also a message", 
                        MB_OK | MB_ICONEXCLAMATION);
                break;
            }
        break;
        default:
            return FALSE;
    }
    return TRUE;
}

  大多数适用于DialogBox()的消息处理规则也适用于CreateDialog(),不调用DefWindowProc(),对不处理的消息返回FALSE,处理的返回TRUE.

  有个攺变就是我们不为无模态窗口调用EndDialog(),我们可以像对常规的窗口一样调用DestroyWindow().我们的例子中主窗口销毀的时候对话框才销毀.在主窗口的WndProc()中这样写...

    case WM_DESTROY:
        DestroyWindow(g_hToolbar);
        PostQuitMessage(0);
    break;

  还有一点,我们希望在任何我们希望的时候显示或隠藏我们的工具栏,所以我在菜单上加上了两个命令来做这件事情,并这样处理:

    case WM_COMMAND:
        switch(LOWORD(wParam))
        {   
            case ID_DIALOG_SHOW:
                ShowWindow(g_hToolbar, SW_SHOW);
            break;
            case ID_DIALOG_HIDE:
                ShowWindow(g_hToolbar, SW_HIDE);
            break;
            //... other command handlers
        }
    break;

  你现在应该可以用资源编辑器或手工的方法来创建你自己的菜单,如果还有问题(一般是这样)可以参看一个本教程提供的例子dlg_two.

  现在你运行你的程序的时候,你就应该可以同时访问你的对话框和主窗口.

  如果你在此时运行你的程序并试图在两个按钮间切換的话,就会注意到不行,按Alt+P和Alt+O来激活按钮都不行.为什么? 因为DialogBox()有自己的消息循环并默认地处理这些事件,CreateDialog()卻沒有.但是我们可以通过在我们的消息循环中调用可以为我们做默认处理的IsDialogMessage()来自己处理它们.

    while(GetMessage(&Msg, NULL, 0, 0))
    {
        if(!IsDialogMessage(g_hToolbar, &Msg))
        {
            TranslateMessage(&Msg);
            DispatchMessage(&Msg);
        }
    }

  这里我们首先将消息传给IsDialogMessage(),如果消息是为我们的工具栏的(由我们传进的窗口的句柄来指示),系统就作默认的处理并返回TRUE.这种情況下消息已经被处理了所以我们不需要再调用TranslateMessage()或DispatchMessage()了.如果消息是为另外一个窗口的我们就照常处理.

  值得提出来的是IsDialogMessage()也可以用于不是对话框的窗口来给它们一些像对话框的功能.记住,对话框就是一个窗口,并且大多数(如果不是全部的话)对话框的API可以工作于任何窗口.

  关于无模态的对话框讲得夠多了!一个问题就是如果你有多个工具栏...怎么办?一种解決方案就是定义一个列表(或是一个数组,一个标準模板库的std::list,或类似的东西)并在你的消息循环里循环把每个句柄传给IsDialogMessage() 直至找到那个正确的,如果沒有找到,就作常规的处理.这是一个泛型编程问题,不是一个Win32问题,就留为读者的练习吧.

Copyright © 1998-2008, Brook Miles (forgey). All rights reserved.