Creating a win32 modal window with CreateWindow
Asked Answered
P

6

20

I create a window with CreateWindow() and show it with ShowWindow(). But the parent window on which this was created should be disabled until user returns from this window, i.e. it should simulate modal dialog box.

Peba answered 9/4, 2009 at 15:13 Comment(0)
P
23

Make sure you set the hwndParent in CreateWindow and use EnableWindow(hwndParent, FALSE) to disable the parent after showing the pop up window. Then enable the parent with EnableWindow(hwndParent, TRUE) after the pop up window has been closed.

Penelope answered 10/4, 2009 at 12:14 Comment(2)
You have to EnableWindow(hwndParent, TRUE) before the popup window is closed, see The correct order for disabling and enabling windowsChetchetah
@AlexeyIvanov Link broken, new link is devblogs.microsoft.com/oldnewthing/20040227-00/?p=40463Cherrylchersonese
T
14

Modality, part 1: UI-modality vs code-modality explains how to do this, and why you might not want to.

Telemetry answered 9/4, 2009 at 15:28 Comment(1)
Old New Thing references are great, but too bad it's a link only answer. You might want to rephrase some content from the article in your own words.Parrnell
H
4

You need to consider what it means to be a modal window - basically, the window's parent is disabled. The only automatic way of doing this (that I am aware of) is to call DialogBox() to create a modal dialog box. However, since you want to use CreateWindow(), then all that you need to do is to manually disable the parent window yourself.

Ideally, it would be better to go the dialog box route (since the OS knows exactly what must be done in order to create a modal window), but I suppose this option is there if you must use it.

Haversack answered 9/4, 2009 at 15:19 Comment(0)
R
3

You could also run a "secondary message loop" which keeps the parent window inactive till your work with the "modal" dialog is finished.

Rosco answered 31/5, 2010 at 12:9 Comment(0)
K
3

Okay I just wrestled with this same problem myself. I needed a quick dialog that behaved like it would if I used DialogBox() but I didn't want to create a template for the particular project I was using.

What I discovered is that if you disable the parent window of the dialog you also disable the dialog. And you can't enable that dialog without re-enabling the parent dialog. So that method won't work.

I also discovered that you can't use SetCapture() / ReleaseCapture() because then the child windows of the dialog won't get messages.

I did find a solution that works: Use a local message pump, driven by either PeekMessage() or GetMessage(). Here is the code that worked for me:

  while (!m_bFinished)
  {
     BOOL bEat;

     if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
     {
        if (msg.message == WM_CHAR)
        {
           if (msg.wParam == VK_ESCAPE)
           {
              m_bFinished = TRUE;
              continue;
           }
        }

        bEat = FALSE;

        if (msg.message >= WM_MOUSEFIRST &&
           msg.message <= WM_MOUSELAST)
        {
           RECT rectMe;

           pcMe->GetWindowRect(&rectMe);
           if (!::PtInRect(&rectMe, msg.pt))
              bEat = TRUE;
        }

        if (!bEat)
        {
           ::TranslateMessage(&msg);
           ::DispatchMessage(&msg);
        }
     }
  }

What this effectively does is "eat" any mouse messages that are outside the client area of the window for all messages delivered to that application. It does not prohibit clicking outside the application, just clicking anywhere inside the application that isn't within the client area of the "modal" window. If you add a MessageBeep() when you eat a message you'll get the exact same behavior as a real modal dialog.

m_bFinished is a BOOL member of the class and it gets set if either the OK or Cancel buttons are accessed on the "dialog," and under certain other conditions that are outside the scope of the code snippet here.

Kist answered 15/1, 2018 at 20:10 Comment(0)
V
1

First of all, this isn't too hard to do but it does require a little work. Here is a skin and bones of how you can do it.

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hInstPrev, PSTR cmdline, int nCmdShow)
{
    WNDCLASSEX wc = {}, wc2 = {};
    MSG msg;

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WNDProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;
    wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ALARMICON));
    wc.hIconSm = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ALARMICON));
    wc.hCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_SWORDCURSOR));
    wc.hbrBackground = hBackground;
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "Primitive Era Alarms";

    wc2.cbSize = sizeof(WNDCLASSEX);
    wc2.style = 0;
    wc2.lpfnWndProc = ModalProc;
    wc2.cbClsExtra = 0;
    wc2.cbWndExtra = 0;
    wc2.hInstance = hInst;
    wc2.hbrBackground = hBackground;
    wc2.lpszMenuName = NULL;
    wc2.lpszClassName = "Settings";

    if (!RegisterClassEx(&wc)) return -1;    
    if (!RegisterClassEx(&wc2)) return -1;


    hParent = CreateWindowEx(WS_EX_CLIENTEDGE, "Parent Window", "Parent Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
    hModal = CreateWindowEx(WS_EX_CLIENTEDGE, "Modal Window", "Modal Window", WS_OVERLAPPEDWINDOW, 100, 100, 800, 800, NULL, NULL, hInstance, NULL);

    if (hParent == NULL) return -1;
    if (hModal == NULL) return -1;

    ShowWindow(hParent, nCmdShow);
    ShowWindow(hModal, false);
    UpdateWindow(hParent);
    UpdateWindow(hSettings);

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}

As you can see here, we are creating two separate windows. So you will need two separate procedures, the modal one we will be handling a little different as I will show you now. The WNDProc, we just create a quick way to call the modal window so we use

LRESULT CALLBACK WNDProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static HWND hButton;
    switch (msg)
    {
    case WM_CREATE:
    {
        hButton = CreateWindow("Button", "Modal Window", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 10, 10, 100, 100, hWnd, (HMENU)ID_MODAL, (HINSTANCE)GetWindowLong(hWnd, GWLP_HINSTANCE), NULL);
        break;
    }
    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
            case ID_MODAL:
            {
                ShowWindow(hModal, SW_SHOW);
                break;
            }
        }
        break;
    }
    case WM_CLOSE:
    {
        DestroyWindow(hWnd);
        break;
    }
    case WM_DESTROY:
    {
        PostQuitMessage(0);
        break;
    }
    default:
        return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    return 0;
}

Here is the modal window procedure function:

LRESULT CALLBACK SettingsProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static HWND hOk;
    switch (msg)
    {
        case WM_CREATE:
        {
            hOk = CreateWindow("Button", "Ok", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 10, 10, 100, 100, hWnd, (HMENU)IDOK, (HINSTANCE)GetWindowLong(hWnd, GWLP_HINSTANCE), NULL);
            break;
        }
        case WM_SHOWWINDOW:
        {
            if (wParam == true)
                EnableWindow(hParent, false);
            else
                EnableWindow(hParent, true);
            return DefWindowProc(hWnd, msg, wParam, lParam);
        }
        case WM_CLOSE:
        {
            ShowWindow(hWnd, false);
            break;
        }
        case WM_COMMAND:
        {
            switch (LOWORD(wParam))
            {
                case IDOK:
                {
                    // Put code here to handle processing data
                    ShowWindow(hWnd, false);
                    break;
                }
            }
        }
        default:
            return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    return 0;
}

Hopefully this gives you an idea on how to do it and anyone else that comes in here wanting to know. There are a lot of reasons to do this and basically you are manually creating a Dialog this way. Doing it this way does help you understand how a Dialog Window actually works.

Now you could make it so that it creates and destroys the window each time you open the modal window, that would require some extra work but it is possible. This should give you a general idea on how to do it.

Vintage answered 21/4 at 23:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.