1
votes

I have a CDialog based MFC application. I save the window's current position when the program ends. When the program starts, I want to restore the previous position. I'm currently trying to do this in OnInitDialog(), however, the program asserts when I call SetWindowPos() from within OnInitDialog(). My call to SetWindowPos() is similar to:

SetWindowPos(&CWnd::wndTop, 10, 10, 500, 500, SWP_NOREDRAW | SWP_NOZORDER);

The assertion relates to a null m_hWnd handle.

Is this the correct place to re-position a dialog-based application's window?

Any ideas on why I'm asserting?

2
It is valid to call SetWindowPos() from OnInitDialog(), as the window handle already exists. Need to see minimal reproducible example for further assitance.zett42
As I remember, MFC saves window positions (and restores them) for you already. Has that changed?IInspectable
@IInspectable Not in a CDialog based application.Andrew Truckle

2 Answers

1
votes

In the process of providing more information to my initial question, I discovered that calling SetWindowPos() passed messages to toolbars within the dialog that hadn't been created, yet. I moved the SetWindowPos() to the end of OnInitiDialog() and it worked.

Thanks for the encouragement, @zett42.

1
votes

This is how I do it:

#include "stdafx.h"
#include "resource.h"
#include "ResizingDialog.h"

IMPLEMENT_DYNAMIC(CResizingDialog, CDialogEx)

CResizingDialog::CResizingDialog(const CString& strWindowID, UINT nIDTemplate, CWnd* pParent /* nullptr */, bool bOnlyStorePosition /* false */)
    : CDialogEx(nIDTemplate, pParent)
    , m_strWindowID(strWindowID)
    , m_bOnlyStorePosition(bOnlyStorePosition)
    , m_bDoNotShowResizeIcon(false)
{
    m_rcInit.SetRect(0, 0, 0, 0);
}


CResizingDialog::~CResizingDialog()
= default;

void CResizingDialog::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}


BEGIN_MESSAGE_MAP(CResizingDialog, CDialogEx)
    ON_WM_GETMINMAXINFO()
    ON_WM_DESTROY()
    ON_WM_PAINT()
    ON_WM_NCHITTEST()
    ON_WM_SIZE()
END_MESSAGE_MAP()


BOOL CResizingDialog::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // Save Initial window size to m_rcInit
    GetWindowRect(&m_rcInit);

    //if (!m_bOnlyStorePosition && !m_bDoNotShowResizeIcon)
        //InitialiseResizeIcon(m_bmpResize, m_lblResize, this);

    RestoreWindowPosition(m_strWindowID, this, true);

    return TRUE;  // return TRUE unless you set the focus to a control
                  // EXCEPTION: OCX Property Pages should return FALSE
}


void CResizingDialog::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
    // Set the minimum window size to initial size.
    lpMMI->ptMinTrackSize.x = m_rcInit.Width();
    lpMMI->ptMinTrackSize.y = m_rcInit.Height();

    CDialogEx::OnGetMinMaxInfo(lpMMI);
}


void CResizingDialog::RestoreWindowPosition(CString strWindow, CWnd* pWindow, bool bOverrideState)
{
    int     max_x, max_y;
    RECT    rtWindow;

    if (pWindow == nullptr)
        return;

    // Only restore if there is a previously saved position
    if ((rtWindow.top = AfxGetApp()->GetProfileInt(strWindow, _T("Top"), -1)) != -1 &&
        (rtWindow.left = AfxGetApp()->GetProfileInt(strWindow, _T("Left"), -1)) != -1 &&
        (rtWindow.bottom = AfxGetApp()->GetProfileInt(strWindow, _T("Bottom"), -1)) != -1 &&
        (rtWindow.right = AfxGetApp()->GetProfileInt(strWindow, _T("Right"), -1)))
    {
        max_x = rtWindow.right - rtWindow.left;
        max_y = rtWindow.bottom - rtWindow.top;

        // Get a handle to the monitor
        HMONITOR hMonitor = ::MonitorFromPoint(
            CPoint(rtWindow.left, rtWindow.top), MONITOR_DEFAULTTONEAREST);

        // Get the monitor info
        MONITORINFO monInfo;

        monInfo.cbSize = sizeof(MONITORINFO);
        if (::GetMonitorInfo(hMonitor, &monInfo) == 0)
            AfxMessageBox(_T("GetMonitorInfo failed"));
        else
        {
            // Adjust for work area
            rtWindow.left += monInfo.rcWork.left - monInfo.rcMonitor.left;
            rtWindow.top += monInfo.rcWork.top - monInfo.rcMonitor.top;

            // Ensure top left point is on screen
            if (CRect(monInfo.rcWork).PtInRect(CPoint(rtWindow.left, rtWindow.top)) == FALSE)
            {
                rtWindow.left = monInfo.rcWork.left;
                rtWindow.top = monInfo.rcWork.top;
            }

            rtWindow.right = rtWindow.left + max_x;
            rtWindow.bottom = rtWindow.top + max_y;

            // Restore window size
            pWindow->MoveWindow(&rtWindow, FALSE);
        }

        if (bOverrideState)
        {
            // Let us override by restoring the window state
            int iState = AfxGetApp()->GetProfileInt(strWindow, _T("ShowCmd"), SW_SHOWNORMAL);
            pWindow->ShowWindow(iState);
        }
    }
}

void CResizingDialog::SaveWindowPosition(CString strWindow, CWnd* pWindow)
{
    WINDOWPLACEMENT wp;

    if (pWindow == nullptr)
        return;

    pWindow->GetWindowPlacement(&wp);

    // Commit to registry
    AfxGetApp()->WriteProfileInt(strWindow, _T("Top"), wp.rcNormalPosition.top);
    AfxGetApp()->WriteProfileInt(strWindow, _T("Left"), wp.rcNormalPosition.left);
    AfxGetApp()->WriteProfileInt(strWindow, _T("Bottom"), wp.rcNormalPosition.bottom);
    AfxGetApp()->WriteProfileInt(strWindow, _T("Right"), wp.rcNormalPosition.right);
    AfxGetApp()->WriteProfileInt(strWindow, _T("ShowCmd"), wp.showCmd);
}


void CResizingDialog::InitialiseResizeIcon(CBitmap& rBmpResize, CStatic& rLblResize, CWnd* pDialog)
{
    CRect rcIcon, rcClient;

    if (pDialog != nullptr)
    {
        rBmpResize.LoadOEMBitmap(OBM_SIZE);
        rLblResize.Create(nullptr, WS_CHILD | WS_VISIBLE | SS_BITMAP,
            CRect(0, 0, 16, 16), pDialog, IDC_STATIC_RESIZE);
        rLblResize.SetBitmap(rBmpResize);
        //theApp.UpdateBitmapBackground(rLblResize.GetBitmap(), true, GetSysColor(COLOR_ACTIVECAPTION));

        pDialog->GetClientRect(rcClient);
        rLblResize.GetClientRect(rcIcon);
        rLblResize.SetWindowPos(&CWnd::wndTop,
            rcClient.right - rcIcon.Width(),
            rcClient.bottom - rcIcon.Height(), 0, 0, SWP_NOSIZE);

        CMFCDynamicLayout *pDynamicLayout = pDialog->GetDynamicLayout();
        if (pDynamicLayout != nullptr)
        {
            CMFCDynamicLayout::MoveSettings moveSettings = CMFCDynamicLayout::MoveHorizontalAndVertical(100, 100);
            CMFCDynamicLayout::SizeSettings sizeSettings = CMFCDynamicLayout::SizeNone();

            pDynamicLayout->AddItem(rLblResize.GetSafeHwnd(), moveSettings, sizeSettings);
        }
    }
}

void CResizingDialog::OnDestroy()
{
    CDialogEx::OnDestroy();

    SaveWindowPosition(m_strWindowID, this);
}


void CResizingDialog::DoNotShowResizeIcon()
{
    m_bDoNotShowResizeIcon = true;
}


void CResizingDialog::OnPaint()
{
    CPaintDC dc(this); // device context for painting
                       // TODO: Add your message handler code here
                       // Do not call CDialogEx::OnPaint() for painting messages

    if (!m_bOnlyStorePosition && !m_bDoNotShowResizeIcon)
    {
        CRect rc;
        GetClientRect(&rc);
        rc.left = rc.right - ::GetSystemMetrics(SM_CXHSCROLL);
        rc.top = rc.bottom - ::GetSystemMetrics(SM_CYVSCROLL);
        HTHEME ht = OpenThemeData(GetSafeHwnd(), L"STATUS");
        if (ht)
        {
            DrawThemeBackground(ht, dc, SP_GRIPPER, 0, &rc, nullptr);
            CloseThemeData(ht);
        }
        else
        {
            dc.DrawFrameControl(rc, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
        }
    }
}


LRESULT CResizingDialog::OnNcHitTest(CPoint point)
{
    CRect rc;

    GetWindowRect(rc);
    rc.left = rc.right - ::GetSystemMetrics(SM_CXHSCROLL);
    rc.top = rc.bottom - ::GetSystemMetrics(SM_CYVSCROLL);
    if (rc.PtInRect(point))
        return HTBOTTOMRIGHT;

    return CDialogEx::OnNcHitTest(point);
}


void CResizingDialog::OnSize(UINT nType, int cx, int cy)
{
    CDialogEx::OnSize(nType, cx, cy);

    Invalidate(TRUE);
}

I don't know how you are storing the window position but my code factors in for multiple monitor configurations.