Search code examples
c++mfcscrollbarzooming

How to change dynamically CFormView WIDTH or HEIGHT in MDI MFC C++ windows application?


In 1997, I have used C++ to create an MDI MFC program.

I have created a Class named XFormFiew that extends MFC CFormView class.

In OnInitialUpdate() event method, I have written some codes to modify automatically the zooming of the view.

In the past, the majority of screen resolution are 800x600, but now the resolution is higher. In zooming automatically in XFormView, I avoid to made some modifications in all my views.

The zooming code modify Left and Bottom but also Width and Height of each CWnd contained in active CFormView

The code is the following

void XFormView::OnInitialUpdate()
    {
    CFormView::OnInitialUpdate();

    pLogFont = new LOGFONT;
    CFont* pDialogFont = GetFont();
    pDialogFont->GetObject(sizeof(LOGFONT),pLogFont);
    pLogFont->lfHeight = MulDiv(pLogFont->lfHeight, Config.GetDstH(), Config.GetSrcH());
    pLogFont->lfWeight = FW_NORMAL;

    pFont = new CFont;
    pFont->CreateFontIndirect(pLogFont);
    SetFont(pFont);

    CWnd* pWnd;
    pWnd = GetWindow(GW_CHILD);
    while (pWnd != NULL)
        {
        ZoomWnd(pWnd);
        pWnd = pWnd->GetWindow(GW_HWNDNEXT);
        }

    // TRY to modify WIDTH and HEIGTH of CFormView    
    ZoomWnd(this);
    }        

void XFormView::ZoomWnd(CWnd* pWnd)
    {
    CRect rect;
    pWnd->GetWindowRect(&rect);
    ScreenToClient(&rect);
    rect.left   = (int)((long)rect.left   * Config.GetDstH() / Config.GetSrcH());
    rect.top    = (int)((long)rect.top    * Config.GetDstV() / Config.GetSrcV());
    rect.right  = (int)((long)rect.right  * Config.GetDstH() / Config.GetSrcH());
    rect.bottom = (int)((long)rect.bottom * Config.GetDstV() / Config.GetSrcV());

    pWnd->MoveWindow(&rect);
    pWnd->SetFont(pFont);
    }

The code work well but for some views, the vertical and horizontal scrollbar are missing.

The Window is always loaded MAXIMIZED. If I unmaximize Window, the scrollbars are always missing.

If I reduce WIDTH or HEIGHT the scrollbar is displayed WHEN width or height reaches what has been defined in RC Resource file.

Example:

IDD_OVFORM DIALOGEX 0, 0, 370, 297

where 370 is the width of Form and 297 is height.

If I reduce Child Window width, nothing happens until width is reduced to 370. At this moment an horizontal scrollbar is automatically displayed.

My problem is how to change dynamically (not in RC file) the width and height of IDD_OVFORM to correspond to zoom level so that horizontal and vertical scrollbar are displayed correctly ?

I have already try to change these properties in OnInitialUpdate() method but nothing is working.

If I change Width and Height in RC Resource FILE, the zoom work correctly for a specific zoom level (but not for all zoom level).

On Internet, I find a some solutions about removing Scrollbars but not about zooming and Scrollbars missing using MDI MFC Form.

Is there somebody that has already found a solution to this problem ?

2019-02-12: thanks to Barmak Shemirani for his solution (OPTION 3) that works perfectly.


Solution

  • Call SetScrollSizes to show the scrollbars at specific sizes:

    CRect rect;
    GetClientRect(rect);
    
    //this multiplication is to make sure the scrollbar is visible
    //remove it in actual code.
    rect.right *= 2;
    rect.bottom *= 2;
    
    SetScrollSizes(MM_TEXT, rect.Size());
    

    But this is not the right method in general.

    Option 1:

    You can just go to resource editor, select the dialog, select dialog's properties, and change the dialog's default font size. Increase the font size to 9 or higher, this will automatically make the dialog and its controls bigger, as well as using larger font.

    Option 2:

    In dialog's properties, you will also see a section called "Dynamic Layout". This lets you zoom the controls on resize, or move them up/down and left/right.

    Option 3:

    Change the font in dialog template during run time. To do so, you must override CFormView::Create, which in turn calls CreateDlg. These function names have to be declared exactly as below.

    Note that the font in dialog template can be changed only once, before the dialog is load.

    Example for Visual Studio 2017 (this code may not be compatible with older MFC versions)

    BOOL XFormView::CreateDlg(LPCTSTR lpszTemplateName, CWnd* pParentWnd)
    {
        CDialogTemplate dlt;
        if(dlt.Load(lpszTemplateName)))
        {
            // set your own font
            dlt.SetFont(L"Arial", 20);
    
            HINSTANCE hInst = AfxFindResourceHandle(lpszTemplateName, RT_DIALOG);
    
            LPCDLGTEMPLATE dlgtemplate = (LPCDLGTEMPLATE)GlobalLock(dlt.m_hTemplate);
    
            // create a modeless dialog
            BOOL bSuccess = CreateDlgIndirect(dlgtemplate, pParentWnd, hInst);
    
            GlobalUnlock(dlt.m_hTemplate);
            return bSuccess;
        }
    
        return CFormView::CreateDlg(lpszTemplateName, pParentWnd);
    }
    
    BOOL XFormView::Create
        ( LPCTSTR lpszClassName
        , LPCTSTR lpszWindowName
        , DWORD dwRequestedStyle
        , const RECT& rect
        , CWnd* pParentWnd
        , UINT nID
        , CCreateContext* pContext
        );
    {
        ASSERT(pParentWnd != NULL);
        ASSERT(m_lpszTemplateName != NULL);
    
        m_pCreateContext = pContext;    // save state for later OnCreate
    
        // call PreCreateWindow to get prefered extended style
        CREATESTRUCT cs; 
        memset(&cs, 0, sizeof(CREATESTRUCT));
        if(dwRequestedStyle == 0)
            dwRequestedStyle = AFX_WS_DEFAULT_VIEW;
        cs.style = dwRequestedStyle;
        if(!PreCreateWindow(cs))
            return FALSE;
    
        // create a modeless dialog
        if(!CreateDlg(m_lpszTemplateName, pParentWnd))
            return FALSE;
    
        m_pCreateContext = NULL;
    
        ModifyStyle(WS_BORDER | WS_CAPTION, cs.style & (WS_BORDER | WS_CAPTION));
        ModifyStyleEx(WS_EX_CLIENTEDGE, cs.dwExStyle & WS_EX_CLIENTEDGE);
    
        SetDlgCtrlID(nID);
    
        CRect rectTemplate;
        GetWindowRect(rectTemplate);
        SetScrollSizes(MM_TEXT, rectTemplate.Size());
    
        // initialize controls etc
        if(!ExecuteDlgInit(m_lpszTemplateName))
            return FALSE;
    
        // force the size requested
        SetWindowPos(NULL, rect.left, rect.top,
            rect.right - rect.left, rect.bottom - rect.top,
            SWP_NOZORDER | SWP_NOACTIVATE);
    
        // make visible if requested
        if(dwRequestedStyle & WS_VISIBLE)
            ShowWindow(SW_NORMAL);
    
        return TRUE;
    }
    

    and class definition in XFormView.h MUST contains following lines

    protected:
    
        BOOL Create
            ( LPCTSTR lpszClassName
            , LPCTSTR lpszWindowName
            , DWORD dwRequestedStyle
            , const RECT& rect
            , CWnd* pParentWnd
            , UINT nID
            , CCreateContext* pContext
            );
    
        BOOL CreateDlg(LPCTSTR lpszTemplateName, CWnd* pParentWnd);
    
        afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    

    The OnCreate declaration is absolutely necessary.

    Without this declaration, nothing happens !!!

    OnCreate is only declared in include file but is not defined in CPP file.