win32非模态对话框的建立过程

**

1024节日的献礼

**
网上找了很多关于win32非模态对话框的文章,也看了一些培训班的视频,感觉误人者真是不少。这些资料建立一个简单的对话框程序,主程序本身就是一个对话框,,关闭对话框就是关闭主程序,这一切都不是问题。当主程序是一个比较复杂的程序,在运行到某个节点弹出一个非模态对话框的时候(比如登录框、消息提示框),按照他们的代码,关闭对话框,结果连主程序也给关掉了。对于非模态对话框,很多文章资料都说的不清楚。
今天总结一下共享给大家,我在代码中做了精确的注释,方便初学者搞懂问题的关键。
//建立非模态对话框的步骤:
//第一步,建立全局变量:HWND Dlg_Modeless; 弹出对话框(非模态)的窗口句柄
//第二步,在资源文件里建立对话框资源,并且命名、确定ID号、确定风格
//第三步,在菜单栏设立选项,实现点击以后弹出对话框的逻辑
//第四步,弹出对话框的逻辑。在鼠标点击菜单项“非模态对话框”以后,调用函数CreateDialog()创建非模态对话框,
//第五步,调用函数 ShowWindow(Dlg_Modeless, SW_SHOW);显示窗口(下面注释掉了,自己体会效果)
//第六步,定义和声明对话框窗口过程函数(照着程序自带的“关于”对话框去做,复制粘贴,改改名称)
//第七步,非模态对话框的窗口过程函数中使用DestroyWindow(Dlg_Modeless);这个函数来关闭对话框,
//而不是 EndDialog(hDlg, LOWORD(wParam));

废话少说,上代码:
VS2019,VS2017,
工程名称:Dialog_Modeless:
这样的话可复制粘贴代码可以直接运行

// Dialog_Modeless.cpp : 定义应用程序的入口点。
#include "framework.h"
#include "Dialog_Modeless.h"

#define MAX_LOADSTRING 100
// 全局变量:
HINSTANCE hInst;                                // 当前实例
WCHAR szTitle[MAX_LOADSTRING];                  // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING];            // 主窗口类名
//非模态对话框,第一步:
HWND Dlg_Modeless;  //弹出对话框(非模态)的窗口句柄
HWND Dlg_Mode;      //弹出对话框(模态)的窗口句柄
// 此代码模块中包含的函数的前向声明:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

//第六步:
//声明对话框窗口过程函数了:DlgModeLessProc和DlgModeProc
//我很懒,复制“关于”对话框的窗口过程函数About,改改就可以了(转到最后)
//INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
//就是这个:(在上面呢)
// “模态对话框”的消息处理程序。把About换成DlgModeProc
INT_PTR CALLBACK DlgModeProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
// “非模态对话框”的消息处理程序。把About换成DlgModeLessProc
INT_PTR CALLBACK DlgModeLessProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPWSTR  lpCmdLine,_In_ int nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 在此处放置代码。

    // 初始化全局字符串
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_DIALOGMODELESS, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 执行应用程序初始化:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }
    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_DIALOGMODELESS));
    MSG msg;

    // 主消息循环:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        //此处的消息循环必须加上下面的判断内容,
        //否则将来关闭非模态对话框调用函数DestroyWindow(Dlg_Modeless)的时候
        //不仅仅关闭对话框,连同主程序也给关掉了,网上大批的狗屁例子主程序就是一个简单的对话框,这这这当然没有问题了
        //if (Dlg_Modeless == 0 || !IsDialogMessage(Dlg_Modeless, &msg))//如果不是对话框的消息主程序才循环
        //{
        //    TranslateMessage(&msg);
        //    DispatchMessage(&msg);
        //}

        //记住了,下面的if判断里必须有:|| Dlg_Modeless == 0 || !IsDialogMessage(Dlg_Modeless, &msg
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)|| Dlg_Modeless == 0 || !IsDialogMessage(Dlg_Modeless, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int) msg.wParam;
}



//  函数: MyRegisterClass()
//  目标: 注册窗口类。
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_DIALOGMODELESS));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_DIALOGMODELESS);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
    return RegisterClassExW(&wcex);
}

//   函数: InitInstance(HINSTANCE, int)
//   目标: 保存实例句柄并创建主窗口
//   注释:
//        在此函数中,我们在全局变量中保存实例句柄并
//        创建和显示主程序窗口。
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 将实例句柄存储在全局变量中

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
   if (!hWnd)
   {
      return FALSE;
   }
   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);
   return TRUE;
}
//第二步,在资源文件里建立模态和非模态对话框:
//在.rc文件里建立两个对话框“模态对话框”和“非模态对话框”模板,注意:是对话框!对话框!对话框!
//把他们的菜单项ID设为IDD_MODE 和IDD_MODELESS(为了和下面的代码匹配)


//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//  目标: 处理主窗口的消息。
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择:
            //第三步,在资源文件的菜单里建立“模态对话框”和“非模态对话框”两个菜单项,注意是菜单项!菜单项!菜单项!
            //在.rc文件里建立两个菜单项“模态对话框”和“非模态对话框”,把他们的菜单项ID设为IDM_MODE 和IDM_MODELESS
            switch (wmId)
           {
           case IDM_MODE: //在菜单项中点击了“模态对话框”选项
           {
               //第四步(1)
               //我比较懒:把程序自带的“关于”对话框的创建函数复制过来,改改
               //DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
               //改成这个样子: 
               //其中IDD_MODE是我们第二步在资源文件中建立的对话框的ID,DlgModeProc是处理这个对话框的窗口过程函数
               DialogBox(hInst, MAKEINTRESOURCE(IDD_MODE), hWnd, DlgModeProc);
               //接下来该定义对话框窗口过程函数了:DlgModeProc
           }
           break;
           case IDM_MODELESS: //在菜单项中点击了“模态对话框”选项
           {
               //第四步(2)
               //我比较懒:使用程序自带的“关于”对话框的创建函数:
              //DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
              //复制过来,改改,改成这个样子: 
              //其中IDD_MODELESS是我们第二步在资源文件中建立的对话框的ID,DlgModeLessProc是处理这个对话框的窗口过程函数
              Dlg_Modeless=CreateDialog(hInst, MAKEINTRESOURCE(IDD_MODELESS), hWnd, DlgModeLessProc);
              //全局变量:弹出对话框(非模态)的窗口句柄Dlg_Modeless,现在有了用武之地:
              //ShowWindow(Dlg_Modeless, SW_SHOW);
              //上面这句注释掉的代码也可以放在非模态对话框的窗口过程函数DlgModeLessProc里case WM_INITDIALOG:消息下面这样写:
        //ShowWindow(hDlg, SW_SHOW);
              //接下来该第五步定义对话框窗口过程函数了:DlgModeLessProc和DlgModeProc
              //我很懒,复制“关于”对话框的窗口过程函数About,改改就可以了(转到最后)
           }
           break;
           case IDM_ABOUT:  //IDD_MODELESS  IDD_MODE
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
           }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;
    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}
//第五步:
//定义对话框窗口过程函数了:DlgModeLessProc和DlgModeProc
//我很懒,复制“关于”对话框的窗口过程函数About,改改就可以了(转到最后)
//就是这个:(在上面呢)
//INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)

// “模态对话框”的消息处理程序。把About换成DlgModeProc
INT_PTR CALLBACK DlgModeProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:
        return (INT_PTR)TRUE;
    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
            EndDialog(hDlg, LOWORD(wParam));
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}
// “非模态对话框”的消息处理程序。把About换成DlgModeLessProc
INT_PTR CALLBACK DlgModeLessProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    UNREFERENCED_PARAMETER(lParam);
    switch (message)
    {
    case WM_INITDIALOG:

    //因为:我们在主窗口的消息循环里加了非模态对话框的判断:
    //if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)|| Dlg_Modeless == 0 || !IsDialogMessage(Dlg_Modeless, &msg))
    //所以,本程序写不写下面这句代码都没有问题,无关紧要
    ShowWindow(hDlg, SW_SHOW);
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
        {
           //因为61-66行代码的缘故,所以下面的做法行得通,
           //否则关闭对话框,将会连主程序关闭,这就是网上很多资料耍流氓的地方:
           //主程序本身仅仅是一个什么都不干的简单对话框,关闭非模态对话框就是关闭主程序,看不出有任何问题
            //不能用    EndDialog(Dlg_Modeless, LOWORD(wParam));函数来关闭
            //也不能用  EndDialog(hDlg, LOWORD(wParam));
            //要用这个函数来关闭
           // DestroyWindow(Dlg_Modeless);
            //也可以这样写:(自己思考为什么)
            DestroyWindow(hDlg);
            return (INT_PTR)TRUE;
        }
        break;
    }
    return (INT_PTR)FALSE;
}

//第六步,回到最前面,把这两个函数声明一下:DlgModeLessProc和DlgModeProc

//第七步:编译成功
//发现“模态对话框”以可像“关于”对话框一样正常显示,而非模态对话框不能正常建立和显示
//因为这两个函数不一样(很关键!很关键!很关键!)
//建立非模态对话框:CreateDialog(hInst, MAKEINTRESOURCE(IDD_MODELESS), hWnd, DlgModeLessProc);
//建立模态对话框:  DialogBox(hInst, MAKEINTRESOURCE(IDD_MODE), hWnd, DlgModeProc);
//CreateDialog这个函数后面需要
//把155行代码注释打开:(在第四步(2))
//ShowWindow(Dlg_Modeless, SW_SHOW);

//第八步,现在基本正常了,
    //打开非模态对话框之后还能打开模态对话框
    //但是打开模态对话框却无法进行其他操作了
//第九步,CreateDialog建立的对话框,(代码241--251行)
    //要用   DestroyWindow(Dlg_Modeless);函数来关闭
    //不能用 EndDialog(Dlg_Modeless, LOWORD(wParam));函数来关闭



  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值