mfcvisual-studio-2017bordercdialog

Resize cursor is showing on the border of a fixed dialog frame


It is a bit difficult to provide you with a minimal working example here but I am going to try and explain this issue that I have only just noticed.

The Context

So, I have a regular CDialogEx derived class, defined like this:

class CChristianLifeMinistryStudentsDlg : public CDialogEx

I have set it up so that the borders will not resize:

Settings

The main application (also CDialogEx based) has a fixed window. That behaves correct.

What Happens

When this dialog is displayed I have noticed this when you hover the mouse over the dialog borders:

Frame cursor

I don't understand why this is happening.

Cursor Management

In the "editor" that spawns this popup window I do have some cursor management like this:

BOOL CChristianLifeMinistryEditorDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
    if (CPersistentWaitCursor::WaitCursorShown())
    {
        RestoreWaitCursor();
        return TRUE;
    }

    return CDialogEx::OnSetCursor(pWnd, nHitTest, message);
}

But, I have tried temporarily to invoke this popup from my main application dialog which does not have an cursor management and the result is still the same.

Spy Results

As requested I have just used Spy to examine the window styles:

Spy Results

As anticipated we suddenly have WS_THICKFRAME set, when it was not in the resource editor!

So

In my RC file the dialog has the DS_MODALFRAME flag set but at runtime it ends up having the WS_THICKFRAME set. As far as I am aware I never make these changes for these affected dialog objects.

Update

I have found out the following:

BOOL CChristianLifeMinistryStudentsDlg::OnInitDialog()
{
    LONG_PTR lStyle = GetWindowLongPtr(GetSafeHwnd(), GWL_STYLE);
    if (lStyle & WS_THICKFRAME)
        AfxMessageBox(_T("Thick"));
    else if (lStyle & DS_MODALFRAME)
        AfxMessageBox(_T("Modal"));

    CDialogEx::OnInitDialog();

If I put the check code before the CDialogEx::OnInitDialog(); call the style is set as DS_MODALFRAME. But if I put the same check code after the CDialogEx::OnInitDialog(); call it is then changed to WS_THICKFRAME. Why?

OK

So, the CDialogEx::OnInitDialog method calls CWnd::LoadDynamicLayoutResource(LPCTSTR lpszResourceName). This in turn calls CWnd::InitDynamicLayout(). And in that method it does this:

if (!bIsChild && (pDialog != NULL || pPropSheet != NULL))
{
    CRect rect;
    GetClientRect(&rect);

    ModifyStyle(DS_MODALFRAME, WS_POPUP | WS_THICKFRAME);
    ::AdjustWindowRectEx(&rect, GetStyle(), ::IsMenu(GetMenu()->GetSafeHmenu()), GetExStyle());

    SetWindowPos(NULL, 0, 0, rect.Width(), rect.Height(), SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
}

There we go. So it is because I am using CDialogEx as my base class. Is this a bug in MFC?

Clarification

The "Editor" (parent window of the popup that owns the button) does use dynamic layout functonality:

Editor

But in this instance the popup does not need to. But it is because my popup is derived from CDialogEx that this is happening.

The plot thickens

So this is the MFC code that is always called with CDialog::OnInitDialog:

BOOL CWnd::LoadDynamicLayoutResource(LPCTSTR lpszResourceName)
{
    if (GetSafeHwnd() == NULL || !::IsWindow(GetSafeHwnd()) || lpszResourceName == NULL)
    {
        return FALSE;
    }

    // find resource handle
    DWORD dwSize = 0;
    LPVOID lpResource = NULL;
    HGLOBAL hResource = NULL;
    if (lpszResourceName != NULL)
    {
        HINSTANCE hInst = AfxFindResourceHandle(lpszResourceName, RT_DIALOG_LAYOUT);
        HRSRC hDlgLayout = ::FindResource(hInst, lpszResourceName, RT_DIALOG_LAYOUT);
        if (hDlgLayout != NULL)
        {
            // load it
            dwSize = SizeofResource(hInst, hDlgLayout);
            hResource = LoadResource(hInst, hDlgLayout);
            if (hResource == NULL)
                return FALSE;
            // lock it
            lpResource = LockResource(hResource);
            ASSERT(lpResource != NULL);
        }
    }

    // Use lpResource
    BOOL bResult = CMFCDynamicLayout::LoadResource(this, lpResource, dwSize);

    // cleanup
    if (lpResource != NULL && hResource != NULL)
    {
        UnlockResource(hResource);
        FreeResource(hResource);
    }

    if (bResult)
    {
        InitDynamicLayout();
    }

    return bResult;
}

For some reason this call BOOL bResult = CMFCDynamicLayout::LoadResource(this, lpResource, dwSize); is return TRUE. As a result the dialog eventually calls InitDynamicLayout. In my other dialogs that are popups this does not happen. Instead, bResult ends up as FALSE and thus the frame is not resized.

So why does it think it worked?


Solution

  • Worked it out. I don't remember doing this but for some reason some of my controls on the dialog had dynamic properties set. For example:

    Dynamic

    I had to set all of these properties back to None. Then it behaved.


    You can easily tell if a given dialog resource has any dynamic properties by opening your resource file in a text editor. For example:

    IDD_DIALOG_OUR_CHRISTIAN_LIFE_AND_MINISTRY_MATERIAL AFX_DIALOG_LAYOUT
    BEGIN
        0,
        0, 0, 0, 0,
        0, 0, 0, 0,
        0, 0, 0, 0,
        0, 0, 0, 0,
        0, 0, 0, 0,
        0, 0, 10, 0,
        0, 0, 0, 0,
        50, 0, 0, 0,
        50, 0, 0, 0,
        0, 0, 0, 0,
        0, 0, 10, 0,
        0, 0, 0, 0,
        50, 0, 0, 0,
        50, 0, 0, 0,
        0, 0, 0, 0,
        0, 0, 10, 0,
        0, 0, 0, 0,
        50, 0, 0, 0,
        50, 0, 0, 0,
        0, 0, 0, 0,
        0, 0, 0, 0
    END
    

    If something like the above is present then your dialog will be deemed as having a dynamic layout, and thus the settings for the dialog are modified:

    ModifyStyle(DS_MODALFRAME, WS_POPUP | WS_THICKFRAME);
    

    The resource will look like this when it has no dynamic control properties:

    IDD_DIALOG_OUR_CHRISTIAN_LIFE_AND_MINISTRY_MATERIAL AFX_DIALOG_LAYOUT
    BEGIN
        0
    END
    

    I chose to manually reset each control via the IDE. However, I guess you could modify the text file manually.


    As to why I had controls with dynamic properties in the first place, well, I can't tell you. I might have been fiddling in the past with the dialog and not realised the side effect to the border frame. Or, possibly, I may have copied controls from one resource on to another and it carried the dyanmic values.

    The interesing side note is that whilst the MFC code set the border as thick, it did not change it sufficiently to enable dialog resizing. But that is another matter!

    At least we now know the cause of the issue and how to easily identify the dialogs in the resource that have dynamic layouts.