DialogEx with Controls: Resizing?

2019-09-04 18:12发布

The aim is to resize the Window of the DialogEx that best fit the configured Systemmetrics screenheight and screendepth on the object machine:

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
    return DialogBoxW(hInstance, MAKEINTRESOURCEW(IDD_MAIN), nullptr, DlgProc);
}

IDD_MAIN is setup as the default in 768p. Let's call it IDD_760Pinstead and use its resource file config as the base to work on.

IDD_768P DIALOGEX 0, 0, 701, 191
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "MahProject"
FONT 8, "MS Sans Serif", 0, 0, 0x0
BEGIN
    LTEXT           "Add",IDC_STATIC,506,10,14,8
    EDITTEXT        IDC_TEXT,528,7,120,14,ES_AUTOHSCROLL
    EDITTEXT        IDC_NUMBER,647,7,21,14,ES_NUMBER
    LTEXT           "times.",IDC_STATIC,671,10,23,8
    LISTBOX         IDC_LIST,7,22,641,148,LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP //principal, main, or chief control on form
    PUSHBUTTON      "&Add",IDC_ADD,650,30,46,14
    PUSHBUTTON      "&Up",IDC_UP,650,47,47,14
    PUSHBUTTON      "&Down",IDC_DOWN,650,63,47,14
    PUSHBUTTON      "&Sideways",IDC_CREATE,650,80,47,14
    PUSHBUTTON      "&UpsideDown",IDC_REMOVE,650,97,47,14
    PUSHBUTTON      "&Less",IDC_CLEAR,650,114,47,14
    PUSHBUTTON      "&More",IDC_LOGON,650,131,47,14
    PUSHBUTTON      "&NoMore",IDC_NOLOGON,650,148,47,14
    LTEXT           "Great",IDC_STATIC_ONE,530,180,70,8
    CTEXT           "-",IDC_SHOWCOUNT,600,180,25,8
    LTEXT           "Fantastic",IDC_STATIC_TWO,625,180,30,8
END

These controls can be separately recalibrated with CreateWindow according to this post but that's a lot of code.
Is there a way of subclassing the form to use extra resource file presets based on the above for IDD_1080P, IDD_2160P, IDD_4320P& beyond? Where in the code would we place the GetSystemMetrics(SM_CXSCREEN) and GetSystemMetrics(SM_CYSCREEN) functions?

2条回答
家丑人穷心不美
2楼-- · 2019-09-04 18:52

In the "old" days there were avid discussions about the use of static data in resource files as opposed to the then "limited" processing power. Doesn't appear to be such an issue these days, however in this case the processing for huge DPIs might be an issue.
I believe Barmak's answer is the way to go, but added this as an expedient, but cheaper and less precise alternative.
Unfortunately we cannot use the preprocessor for the different presets, so the idea is to enumerate lpTemplate values for LPCDLGTEMPLATE just after the entry point:

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
Create_Temporary_hwnd_here
...
lpTemplate = DoSystemParametersInfoStuff(hwnd);
...
Destroy__Temporary_hwnd
...
return DialogBoxW(hInstance, MAKEINTRESOURCEW(lpTemplate), nullptr, DlgProc);
}

DoSystemParametersInfostuff returns the appropriate strings IDD_1080P, IDD_2160P etc. Provided the template structure and associated data for each template in the resource file is not changed there shouldn't be a problem.
Edit1: Checking for primary/secondary display might be a little awkward if calling the function MonitorFromWindow before we get a handle for our DlgProc window using the alternative method to Barmak's. Well it appears that it is not possible to ever return to the wWinMain routine once having left it, so the next option is to create another ephemeral "cheaper" hwnd from wWinMain to get the appropriate templates.
Edit 2: The form templates for the different resolutions are all set up in a spreadsheet downloadable here thanks to AOO.

查看更多
爷的心禁止访问
3楼-- · 2019-09-04 18:56

You can resize the dialog in WM_INITDIALOG

GetSystemMetrics(SM_CXSCREEN)/GetSystemMetrics(SM_CYSCREEN) gives the full screen width/height. This would try to overlap the toolbar. You probably don't want that.

SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcDesktop, NULL); will get the rectangle for desktop, or you can just show maximized window

BOOL CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
    if (msg == WM_INITDIALOG)
    {
        ShowWindow(hwnd, SW_MAXIMIZE);
        return 0;
    }
    ...
    return FALSE;
}

You should change the dialog style to the following:

STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_MAXIMIZEBOX | WS_POPUP | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME

Note, you will have to move/resize all the child controls. When moving child controls, not that you have to its position in screen coordinates, then convert to client coordinate's of parent window.

Example

#include <Windows.h>
#include "resource.h"

#define rcwidth(rc) (rc.right - rc.left)
#define rcheight(rc) (rc.bottom - rc.top)

void move_resize(HWND child, int dx, int dy, int dw, int dh)
{
    if (!child) return;
    if (!IsWindow(child)) return;
    if (!GetParent(child)) return;

    //find child window's coordinates relative to top-left of parent:

    RECT rc;
    GetWindowRect(child, &rc);
    //rc is now child control's rectangle in screen coordinates

    POINT pt = { 0 };
    ScreenToClient(GetParent(child), &pt);
    OffsetRect(&rc, pt.x, pt.y);
    //rc is now child control's rectangle relative to parent window

    //prevent negative size
    if ((rcwidth(rc) + dw) < 0) dw = -rcwidth(rc);
    if ((rcheight(rc) + dh) < 0) dh = -rcheight(rc);

    MoveWindow(child, rc.left + dx, rc.top + dy, rcwidth(rc) + dw, rcheight(rc), TRUE);
}

BOOL CALLBACK DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static RECT save_rect;

    switch (msg)
    {
    case WM_INITDIALOG:
        GetClientRect(hwnd, &save_rect);
        ShowWindow(hwnd, SW_MAXIMIZE);
        break;

    case WM_SIZE:
        if (lParam)
        {
            int cx = LOWORD(lParam);
            int cy = HIWORD(lParam);
            int dx = cx - save_rect.right;
            int dy = cy - save_rect.bottom;

            //change x/y position of OK/Cancel button
            move_resize(GetDlgItem(hwnd, IDCANCEL), dx, dy, 0, 0);
            move_resize(GetDlgItem(hwnd, IDOK), dx, dy, 0, 0);

            //change width/height of listbox:
            move_resize(GetDlgItem(hwnd, IDC_LIST1), 0, 0, dx, dy);

            GetClientRect(hwnd, &save_rect);
        }
        break;

    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK)
            EndDialog(hwnd, wParam);

        if (LOWORD(wParam) == IDCANCEL)
            EndDialog(hwnd, wParam);
        break;

    }

    return FALSE;
}

int WINAPI wWinMain(HINSTANCE hinst, HINSTANCE, LPTSTR, int)
{
    DialogBox(hinst, MAKEINTRESOURCE(IDD_DIALOG1), 0, DlgProc);
    return 0;
}
查看更多
登录 后发表回答