Applications: How to create a custom dialog box for Windows Mobile 6 (native)

Ashraf, on the MSDN forum, asks,

Is there a way to make a default choice for the messagebox that happens after a period of time if the user doesn’t choose (Clicked ) Yes or No buttons.

To elaborate, the requirement is to show a message box to the user with certain options to select, and if the user does not respond within a predefined time limit (say 8 seconds) then the message box must dismiss itself and select a default option. Now such a functionality is not available with the MessageBox() api, you will have to write your own custom dialog box.

Surely, creating a dialog box is quite a simple task using the DialogBox() api, and we have been creating full screen dialog boxes all the while. So how will this custom message box be any different? It’s not much different from a regular dialog box except for a few changes in its properties. First, it has a title bar but no buttons on the title bar (no ‘x’ or ‘ok’ button on the title bar), it doesn’t occupy full screen and it contains the controls that you put into it, thus justifying the title ‘custom’.

So in this post we create a custom dialog box with two buttons, ‘Black’ and ‘White’. The user is given 8 seconds to select one of those colours, if the user doesn’t make a selection in 8 seconds, the default option ‘Black’ is selected. Before going into the implementation here is a video of how the dialog box works;

Custom dialog box

To start off, add a new dialog resource into your application, size it appropriately and add whatever controls you need to the dialog. In my case, I added two static text labels and two buttons, as below;

CustomDialog_rsrc

Now we need to write up the window procedure for this dialog, here is the complete function;

BOOL CALLBACK CustomDialogProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
    int wmID, wmEvent;
    PAINTSTRUCT ps;
    HDC hdc;

    static int timeCount = 0;

    switch(uMessage)
    {
        case WM_INITDIALOG:
            {
                SHINITDLGINFO shidi;

                memset(&shidi, 0, sizeof(shidi));

                shidi.dwMask = SHIDIM_FLAGS;
                //shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIPDOWN | SHIDIF_SIZEDLGFULLSCREEN | SHIDIF_EMPTYMENU;
                shidi.dwFlags = SHIDIF_SIPDOWN | SHIDIF_EMPTYMENU;
                shidi.hDlg = hDlg;
                SHInitDialog(&shidi);

                SHDoneButton(hDlg, SHDB_HIDE);

                timeCount = 0;

                SetWindowText(GetDlgItem(hDlg, IDC_STATIC_TIME_REMAINING), L"Time remaining: 8 second(s)");
                SetTimer(hDlg, MY_TIMER, 1000, NULL);

            }
            return TRUE;

        case WM_COMMAND:
            {
                wmID = LOWORD(wParam);
                wmEvent = HIWORD(wParam);

                switch(wmID)
                {
                    case IDC_BUTTON_BLACK:

                        KillTimer(hDlg, MY_TIMER);
                        EndDialog(hDlg, IDC_BUTTON_BLACK);
                        break;
                    case IDC_BUTTON_WHITE:
                        KillTimer(hDlg, MY_TIMER);
                        EndDialog(hDlg, IDC_BUTTON_WHITE);
                        break;
                }
            }
            break;

        case WM_TIMER:
            {
                if (wParam == MY_TIMER)
                {
                    WCHAR wszText[128];
                    memset(&wszText, 0, sizeof(wszText));

                    timeCount++;

                    //8 seconds are over, dismiss the dialog, select def value
                    if (timeCount >= 8)
                    {
                        KillTimer(hDlg, MY_TIMER);
                        EndDialog(hDlg, IDC_BUTTON_BLACK_DEF);
                    }

                    wsprintf(wszText, L"Time remaining: %d second(s)", 8-timeCount);

                    SetWindowText(GetDlgItem(hDlg, IDC_STATIC_TIME_REMAINING), wszText);
                    UpdateWindow(GetDlgItem(hDlg, IDC_STATIC_TIME_REMAINING));
                }

            }
            break;
        case WM_PAINT:
            {
                hdc = BeginPaint(hDlg, &ps);

                EndPaint(hDlg, &ps);
            }
            break;

    }

    return FALSE;
}

The MSDN documentation mentions that you need to specify the flag WS_NONAVDONEBUTTON, but I got an error saying that the value could not be found, so we can ignore this for now. Next up, while calling SHInitDialog() for your custom dialog, make sure that you don’t specify SHDIF_DONEBUTTON in the dwFlags member of the SHINITDIALOG structure, this flag makes the ‘ok’ button appear on the dialog title bar. Finally, we need to call SHDoneButton() with SHDB_HIDE flag to, well, hide the Done button. The ‘Done’ button is the same as the ‘ok’ button, so this step might seem redundant, and the dialog works fine without calling SHDoneButton() too, but it’s better to stick with the documentation (; So you can see that we have followed all these steps above, under WM_INITDIALOG. We also setup a few things like a variable to keep track of the time, and setting off a one second timer.

Every time the timer fires, we receive a WM_TIMER message. We then update the static label displaying the amount of time left to the user. If 8 seconds go by without the user selecting any option, we kill the timer and end the dialog with IDC_BUTTON_BLACK_DEF. This is just a #define’d integer value, make sure it’s unique. You’ll see why this is important. If the user makes a selection, either Black or White, we kill the timer and end the dialog with the corresponding selection the user made, that is, either IDC_BUTTON_BLACK or IDC_BUTTON_WHITE.

Ok, so now our custom dialog is ready to be used. I invoke the custom dialog from a menu entry in the main window as below,

case IDM_MENU_CUSTOMDLG:
    {
        int ret = DialogBox(g_hInst, MAKEINTRESOURCE(IDD_CUSTOM_DIALOG), hWnd, CustomDialogProc);

        switch(ret)
        {
            case IDC_BUTTON_BLACK_DEF:
                SetWindowText(g_hStaticSelection, L"You Selected: Black (default)");
                break;
            case IDC_BUTTON_BLACK:
                SetWindowText(g_hStaticSelection, L"You Selected: Black");
                break;

            case IDC_BUTTON_WHITE:
                SetWindowText(g_hStaticSelection, L"You Selected: White");
                break;
        }

        UpdateWindow(g_hStaticSelection);
    }
    break;

So you see why ending the dialog with the corresponding value was important, that’s what the DialogBox() api returns with. And in the main window I update a static text label to show which option was selected.

I cranked this out in about an hour, and unfortunately don’t have time for a managed C# version. That will have to be another post, if I manage to get it working that is (;

Leave a Reply

Your email address will not be published. Required fields are marked *