nop
nop

Reputation: 6331

WinAPI GUI: Is Status Bar possible with dialog box?

I'm using dialog box-based Win API C++ GUI. Just read the docs and there is CreateStatusWindow API which is used to create the status bar, but I don't know how it can be fit into mine, because I'm using CreateDialogW.

CreateStatusWindow is obsolete, CreateWindow is recommended.

Note This function is obsolete. Use CreateWindow instead

#include <Windows.h>

#include "resource.h"

// Enable Visual Styles
#pragma comment(linker, "\"/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

// Set Windows GUI Subsystem
#pragma comment(linker, "/SUBSYSTEM:WINDOWS")

// Required for LV_COLUMN and ListView_x
#include <CommCtrl.h>

INT_PTR CALLBACK DialogProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_INITDIALOG:
    {
        // Load icon
        const auto icon = static_cast<HICON>(LoadImageW(GetModuleHandleW(nullptr), MAKEINTRESOURCEW(IDI_ICON1), IMAGE_ICON, 64, 64, 0));
        SendMessageW(hWnd, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(icon));

        // Load menu
        const HMENU menu = LoadMenuW(GetModuleHandleW(nullptr), MAKEINTRESOURCEW(IDR_MENU1));
        SetMenu(hWnd, menu);
            
        // Add ListView columns
        LVCOLUMNW col{};
        col.mask = LVCF_TEXT | LVCF_WIDTH | LVIF_IMAGE;
        col.cx = 60;
        wchar_t c0_txt[] = L"Title";
        col.pszText = c0_txt;
        ListView_InsertColumn(GetDlgItem(hWnd, IDC_LIST1), 0, &col);
            
        return TRUE;
    }
    case WM_NCDESTROY:
        PostQuitMessage(0);
        return FALSE;
    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
        case IDOK:
        case IDCANCEL:
            DestroyWindow(hWnd);
            break;
        default:
            break;
        }
        return FALSE; // we didn't handle that particular `msg` completely, therefore we should return FALSE. It can also depend on `wParam` and `lParam`.
    }
    default:
        return FALSE;
    }
}

int WINAPI WinMain(
    _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPSTR lpCmdLine,
    _In_ int nShowCmd
)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    HWND hWnd = CreateDialogParamW(hInstance, MAKEINTRESOURCEW(IDD_MAIN), nullptr, &DialogProc, 0);

    if (!hWnd)
    {
        MessageBoxW(nullptr, L"Dialog Creation Failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    ShowWindow(hWnd, nShowCmd);
    UpdateWindow(hWnd);

    MSG msg;
    while (GetMessageW(&msg, nullptr, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }

    return msg.wParam;
}

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// English (United States) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""\0"
END

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    IDD_MAIN, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 448
        TOPMARGIN, 7
        BOTTOMMARGIN, 213
    END
END
#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_MAIN DIALOGEX 0, 0, 455, 220
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Test"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
    CONTROL         "",IDC_LIST1,"SysListView32",LVS_REPORT | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,21,25,396,160
END


/////////////////////////////////////////////////////////////////////////////
//
// AFX_DIALOG_LAYOUT
//

IDD_MAIN AFX_DIALOG_LAYOUT
BEGIN
    0
END


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

IDR_MENU1 MENU
BEGIN
    POPUP "Control Panel"
    BEGIN
        MENUITEM "Exit",                        ID_CONTROLPANEL_EXIT
    END
END


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICON1               ICON                    "Hopstarter-Sleek-Xp-Basic-Folder.ico"

#endif    // English (United States) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED


Edit (Fixed the chinese characters):

I was able to add it on WM_INITDIALOG, but unfortunately it doesn't resize along with the window's resizing and its encoding is chinese.

enter image description here

// Add Status Bar
RECT client_rect{};
GetClientRect(hWnd, &client_rect);

HWND status = CreateWindowExW(0, STATUSCLASSNAMEW, nullptr, WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0,
    client_rect.bottom - client_rect.top - 20,
    client_rect.right - client_rect.left, 20, hWnd, nullptr, hInstance,
    nullptr);

int status_parts[2] = { client_rect.right - client_rect.left - 170,
                client_rect.right - client_rect.left };
SendMessageW(status, SB_SETPARTS, 2, reinterpret_cast<LPARAM>(&status_parts));
SendMessageW(status, SB_SETTEXT, 0, reinterpret_cast<LPARAM>(L"Test"));
SendMessageW(status, WM_SIZE, 0, 0);

Upvotes: 0

Views: 727

Answers (3)

IInspectable
IInspectable

Reputation: 51413

When creating a dialog from a resource template the system sends a WM_INITDIALOG message to the dialog procedure:

Sent to the dialog box procedure immediately before a dialog box is displayed. Dialog box procedures typically use this message to initialize controls and carry out any other initialization tasks that affect the appearance of the dialog box.

If a dialog needs a dynamically created Status Bar control, that's where it should be created. Since

the window procedure for the status bar automatically sets the initial size and position of the window, ignoring the values specified in the CreateWindowEx function

you can pass arbitrary values for x, y, width, and height:

case WM_INITDIALOG:
    CreateWindowExW(0, STATUSCLASSNAMEW, nullptr, WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP,
        0, 0, 0, 0, hWnd,
        reinterpret_cast<HMENU>(IDC_STATUS_BAR), GetModuleHandleW(nullptr), nullptr);
    return TRUE;

For this to compile you will have to create a symbolic constant IDC_STATUS_BAR to act as a control identifier (used later when resizing). One way is to add it to the resource.h file, e.g.:

#define IDC_STATUS_BAR 101

That creates a properly positioned and sized status bar control. Instructions on how to keep it in the desired relative position when its parent is resized are documented as well:

The window procedure [for the status bar] automatically adjusts the size of the status bar whenever it receives a WM_SIZE message. Typically, when the size of the parent window changes, the parent sends a WM_SIZE message to the status bar.

Again, the status bar control ignores the width and height, so sending a WM_SIZE message with arbitrary values is fine. Add the following to the dialog procedure:

case WM_SIZE:
    SendMessageW(GetDlgItem(hWnd, IDC_STATUS_BAR), WM_SIZE, 0, 0);
    return FALSE;

and the status bar control will automatically follow its parent's client area.

Upvotes: 2

Vlad Feinstein
Vlad Feinstein

Reputation: 11311

For the last two questions:

From the fact that your WinMain accepts LPSTR (and not LPWSTR) - I assume that you are NOT building for UNICODE. Then SB_SETTEXT requires an ANSI character string, while you are passing wide string here:

SendMessageW(status, SB_SETTEXT, 0, reinterpret_cast<LPARAM>(L"Test"));

You should either pass a narrow string:

SendMessageW(status, SB_SETTEXT, 0, reinterpret_cast<LPARAM>("Test"));

or use SB_SETTEXTW:

SendMessageW(status, SB_SETTEXTW, 0, reinterpret_cast<LPARAM>(L"Test"));

And for the position of your status bar - you create it at a fixed position within the client window:

HWND status = CreateWindowExW(0, STATUSCLASSNAMEW, nullptr, WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0,
  client_rect.bottom - client_rect.top - 20,
  client_rect.right - client_rect.left, 20, hWnd, nullptr, hInstance,
  nullptr);

When you resize your window - the status bаr stays at the same client coordinates. You should process the WM_SIZE message and move the status bar accordingly.

Upvotes: 2

Vlad Feinstein
Vlad Feinstein

Reputation: 11311

You should only use dialog-based app if you need only the features provided by the dialog box.

As soon as you start adding status bar, menu bar, accelerators, serialization, etc. - you would be better off using a regular windows app.

Upvotes: -1

Related Questions