?
Solved

Automatic resizing controls in a MFC CFormView

Posted on 2007-07-24
21
Medium Priority
?
4,461 Views
Last Modified: 2013-12-03
I'm following a tutorial for MFC/C++ that regards automatic resizing of elements in a formview. Essentially, I want to have a CFormView dialog with elements that occupy the entire screen, no matter what the desktop resolution is, and elements that move around as the window is resized. I've found a tutorial that covers this:

http://www.codeproject.com/docking/AutoResizingControls.asp

Everything is working, except when I resize the window. That is to say that no matter what my desktop resolution is, my form fully fills it. However, when I resize the window, the elements don't move around to accommodate the new size of the window. I suspect this is due to this (from the tutorial):

"The BPControlAnchorMap-mechanism was originally designed for dialogs where the dialog or parent-window and its controls, both have constant dimensions. A problem arises when you use the mechanism with CFormView. A CFormView resizes itself to fit into the area of the parent-window in which it is contained. Since this process happens before you can call InitAnchors(), the control-map will be empty and the controls will not be resized correctly.

A workaround for this is to use the flag ANIF_CALCSIZE when calling the InitAnchors-function. If you call InitAnchors(ANIF_CALCSIZE), then the InitAnchors-function will try to find the original size of the parent-window (your CFormView) by taking the bottom-right-most coordinate of all controls which are in the control-map."

I've used this ANIF_CALCSIZE argument, and everything works as described.

Here is my OnSize overloading function:

void CLustreView::OnSize(UINT nType, int cx, int cy)
{
      CFormView::OnSize(nType, cx, cy);
      CRect rcWnd;
      GetWindowRect(&rcWnd);
      HandleAnchors(NULL);
      Invalidate(false);
}

I haven't forgotten to #include <atltypes.h>, the code compiles, but the elements still won't accommodate the size of the window.

Please advise.
0
Comment
Question by:sternocera
  • 10
  • 5
  • 4
  • +1
20 Comments
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 19555062
I made a sample formview with some controls and resized it. The positions and shape of the controls actually do not change when resizing. It's only scrollbars that appear when making it smaller than the size defined in the resources.

I think the behavior is ok. You can't shrink controls properly as the font and image may not be shrinked with the same scale but in steps only. Hence text is either too small or would be cut or couldn't be read.

Does your view has a different behavior?

Regards, Alex
0
 

Author Comment

by:sternocera
ID: 19555098
Alex,

Yes, my view behaves differently. I don't want the actually size of the elements to change - I want their positions to change. This would mean that the screen would be a lot more crowded on a lower resolution display, and more sparse on a higher resolution display. The position of the elements scales, not the elements themselves.

Anyone who's ever coded HTML will know what I mean by this.

Thanks,
Sternocera
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 19555124
>>>> If you call InitAnchors(ANIF_CALCSIZE), then the
>>>> InitAnchors-function will try to find the original size
>>>> of the parent-window (your CFormView)
If I interpret that text correctly the new calculation is not based on the new size but on the size defined in the dialog resource. If I am right  the ANIF_CALCSIZE is more suitable to get called in the OnInitialUpdate and wouldn't change anything if called in OnSize.

Regards, Alex
0
NFR key for Veeam Agent for Linux

Veeam is happy to provide a free NFR license for one year.  It allows for the non‑production use and valid for five workstations and two servers. Veeam Agent for Linux is a simple backup tool for your Linux installations, both on‑premises and in the public cloud.

 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 19555249
>>>> Anyone who's ever coded HTML will know what I mean by this.
Yes, that was my bad ;-)

>>>> Yes, my view behaves differently.
Hmmm, sizing on different resolutions is ok. I've done that recently. We kept the relative positions same and resized controls and images as well. For the latter we needed classified possible resolutions and loaded images per class. That was cause automatically resized pictures were rather bad especially if they have text inside. Another problem was fonts. We classified font sizes as well and changed to a true type font (to get more variance). Each possible resolution got appropriate fonts associated so that the texts were not clipped but have maximum size within their box.

Resizing a form window dynamically by the user is a completely different thing especially if you don't resize the controls. Then, your form must be dynamically scaled what really gives poor results for any text outside of controls (or in static controls), for images and for lines of boxes and frames.

Regards, Alex
0
 

Author Comment

by:sternocera
ID: 19555674
I don't want to resize images or fonts. I'm happy to have fonts & images consistently the same size, I'd just like them to keep their *relative* position.

Pretty standard stuff.

Any idea why my overloaded OnSize function doesn't work?
0
 

Author Comment

by:sternocera
ID: 19556742
Here is the OnSize function:

void CLustreView::OnSize(UINT nType, int cx, int cy)
{
      CFormView::OnSize(nType, cx, cy);
      CRect rcWnd;
      GetWindowRect(&rcWnd);
      HandleAnchors(NULL);
      Invalidate(false);
}

I should mention that I call the base class's OnSize function, and since my baseclass is a CFormView that is what's called.

This differs from the tutorial, where as in the tutorial CDialog's base class is called.
0
 

Author Comment

by:sternocera
ID: 19556760
*I should have said that the CDialog base class is called. Whoops.
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 19557044
From help file about OnSize

nType
Specifies the type of resizing requested. This parameter can be one of the following values:

SIZE_MAXIMIZED   Window has been maximized.

SIZE_MINIMIZED   Window has been minimized.

SIZE_RESTORED   Window has been resized, but neither SIZE_MINIMIZED nor SIZE_MAXIMIZED applies.

SIZE_MAXHIDE   Message is sent to all pop-up windows when some other window is maximized.

SIZE_MAXSHOW   Message is sent to all pop-up windows when some other window has been restored to its former size.

cx
Specifies the new width of the client area.

cy
Specifies the new height of the client area.


so you are already given the actual size of the client area that you want to fill.


ps.  GetWindowRect gets the *window* rectangle - not necessarily the size of the client area.
pps. You don't even do anything with the window rect after you get it !
0
 

Author Comment

by:sternocera
ID: 19557076
Sorry. I tried setting HandAnchors to NULL before, but changed it back. Now, the function is like this:

void CLustreView::OnSize(UINT nType, int cx, int cy)
{
      CFormView::OnSize(nType, cx, cy);
      CRect rcWnd;
      GetWindowRect(&rcWnd);
      HandleAnchors(&rcWnd);
      Invalidate(false);
}
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 19557107
repeat:  ps.  GetWindowRect gets the *window* rectangle - not necessarily the size of the client area.

Showing what is in HandleAnchors might be useful.
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 19557149
consider this.
Client rectangle - 0,0,300,300
Window rectangle - 500,500,820,820

How will you code behave?
0
 

Author Comment

by:sternocera
ID: 19557215
This is HandleAnchors:
                             
 void HandleAnchors(RECT *pRect);
                             
#define BEGIN_ANCHOR_MAP(theclass) void theclass::HandleAnchors(RECT *pRect) { \
                                m_bpfxAnchorMap.HandleAnchors(pRect); \
                                }; \

Here is the non-class member HandleAnchors that is called in turn (it's a bit of a monolithic function):
  void HandleAnchors(RECT *pRectParent) {
 
    int             iCtrl = 0;
    TCtrlEntry      *pCtrl = NULL;
    FSIZE           szCtrl;
    BOOL            bChanged = FALSE;
    RECT            rcParent;
    HDWP            hDeferPos = NULL;
    WINDOWPLACEMENT wpl;
   
    if (pRectParent==NULL) {
   
      ::GetWindowRect(m_hWndParent, &rcParent);
      PreProcess(&rcParent);
   
    } else PreProcess(pRectParent);
   
    // handle the visibility of the sizing-grip if we have one
    if (m_hWndSizeGrip!=NULL) {
   
      wpl.length = sizeof(wpl);
      ::GetWindowPlacement(m_hWndParent, &wpl);
      if ((wpl.showCmd==SW_MAXIMIZE) && (::IsWindowVisible(m_hWndSizeGrip))) {
        ::ShowWindow(m_hWndSizeGrip, SW_HIDE);
      } else if (!::IsWindowVisible(m_hWndSizeGrip)) {
        ::ShowWindow(m_hWndSizeGrip, SW_SHOW);
      };
   
    };
   
    // handle child-controls
    hDeferPos = BeginDeferWindowPos(m_nCtrlCount);
 
    for (iCtrl=0; iCtrl < m_nCtrlCount; iCtrl++) {
   
      pCtrl = &m_Ctrl[iCtrl];
      if (pCtrl->hWnd==NULL) continue;
     
      // get the size of the control
      szCtrl.cx = pCtrl->rect.right-pCtrl->rect.left;
      szCtrl.cy = pCtrl->rect.bottom-pCtrl->rect.top;
     
      // we´ve nothing changed until now
      bChanged = FALSE;
     
      // handle docking
      if ((pCtrl->nFlags & ANF_DOCK_ALL)==ANF_DOCK_ALL) {
     
        SetFRect(&pCtrl->rect, 0, 0, m_rcClient.right, m_rcClient.bottom);
        bChanged = TRUE;
     
      } else if (pCtrl->nFlags & ANF_DOCK_TOP) {
     
        SetFRect(&pCtrl->rect, 0.0, 0.0, (double)m_rcClient.right, szCtrl.cy);
        bChanged = TRUE;
     
      } else if (pCtrl->nFlags & ANF_DOCK_BOTTOM) {
     
        SetFRect(&pCtrl->rect, 0, m_rcClient.bottom-szCtrl.cy, m_rcClient.right, m_rcClient.bottom);
        bChanged = TRUE;
     
      } else if (pCtrl->nFlags & ANF_DOCK_LEFT) {
     
        SetFRect(&pCtrl->rect, 0, 0, szCtrl.cx, m_rcClient.bottom);
        bChanged = TRUE;
     
      } else if (pCtrl->nFlags & ANF_DOCK_RIGHT) {
     
        SetFRect(&pCtrl->rect, m_rcClient.right-szCtrl.cx, 0, m_rcClient.right, m_rcClient.bottom);
        bChanged = TRUE;
     
      } else if (pCtrl->nFlags & ANF_DOCK_LEFT_EX) {
     
        SetFRect(&pCtrl->rect, 0, pCtrl->rect.top, szCtrl.cx, pCtrl->rect.bottom);
        bChanged = TRUE;
     
      } else if (pCtrl->nFlags & ANF_DOCK_RIGHT_EX) {
     
        SetFRect(&pCtrl->rect, pCtrl->rect.left, pCtrl->rect.top, m_rcClient.right, pCtrl->rect.bottom);
        bChanged = TRUE;
     
      } else if (pCtrl->nFlags & ANF_DOCK_TOP_EX) {
     
        SetFRect(&pCtrl->rect, pCtrl->rect.left, 0, pCtrl->rect.right, pCtrl->rect.bottom);
        bChanged = TRUE;
     
      } else if (pCtrl->nFlags & ANF_DOCK_BOTTOM_EX) {
     
        SetFRect(&pCtrl->rect, pCtrl->rect.left, pCtrl->rect.top, pCtrl->rect.right, m_rcClient.bottom);
        bChanged = TRUE;
     
        };
     
      // handle anchoring
      if ((m_uiSizedBorders & ANF_LEFTRIGHT) && (m_szDelta.cx!=0) && (!bChanged)) {
     
        switch(pCtrl->nFlags & ANF_LEFTRIGHT) {
       
          case ANF_LEFT         : // nothing to do here, control moves automatically
                                  // with the left-border of the window (client-rect)
                                  break;
         
          case ANF_RIGHT        : pCtrl->rect.left+=m_szDelta.cx;
                                  pCtrl->rect.right=(pCtrl->rect.left+szCtrl.cx);
                                  bChanged = TRUE;
                                  break;
         
          case ANF_LEFTRIGHT    : pCtrl->rect.right+=m_szDelta.cx;
                                  bChanged = TRUE;
                                  break;
         
          default               : pCtrl->rect.left+=((double)m_szDelta.cx/2.0);
                                  pCtrl->rect.right=(pCtrl->rect.left+szCtrl.cx);
                                  bChanged = TRUE;
                                  break;
       
        };
     
      };
     
      if ((m_uiSizedBorders & ANF_TOPBOTTOM) && (m_szDelta.cy!=0)) {
     
        switch(pCtrl->nFlags & ANF_TOPBOTTOM) {
       
            case ANF_TOP        : // nothing to do here, control moves automatically
                                  // with the top of the window (client-rect);
                                  break;  
           
            case ANF_BOTTOM     : pCtrl->rect.top+=m_szDelta.cy;
                                  pCtrl->rect.bottom=(pCtrl->rect.top+szCtrl.cy);
                                  bChanged = TRUE;
                                  break;
                                 
            case ANF_TOPBOTTOM  : pCtrl->rect.bottom+=m_szDelta.cy;
                                  bChanged = TRUE;
                                  break;

            default             : pCtrl->rect.top+=((double)m_szDelta.cy/2.0);
                                  pCtrl->rect.bottom=(pCtrl->rect.top+szCtrl.cy);
                                  bChanged = TRUE;
                                  break;
        };
     
      };

      // now reposition the control, if its size/position has changed        
      if (bChanged!=FALSE) {

        szCtrl.cx = pCtrl->rect.right-pCtrl->rect.left;
        szCtrl.cy = pCtrl->rect.bottom-pCtrl->rect.top;
        ::DeferWindowPos(hDeferPos, pCtrl->hWnd, NULL, (int)pCtrl->rect.left, (int)pCtrl->rect.top, (int)szCtrl.cx, (int)szCtrl.cy, SWP_NOZORDER | SWP_NOACTIVATE);

      };  
   
    };
   
    ::EndDeferWindowPos(hDeferPos);

    PostProcess();
   
  };
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 19557441
I'd advise you to study that function and look up what each function it calls is doing.  A lot of the code isn't very suitable for use in a view.


(I don't like explaining every last detail - it is better in the long run to work out oneself what the code does.  You learn more.)
0
 

Author Comment

by:sternocera
ID: 19563613
Andy,

That is reasonable. I didn't actually expect you to go through all that code and get back to me with the answer....You're not my employee.

The only reason I pasted the function was because you asked me to.

I'll look into it,

Thanks
0
 
LVL 45

Expert Comment

by:AndyAinscow
ID: 19563712
From my side I asked you to post the function so that the experts here can look at it.  Maybe it was just a few lines of code with an obvious problem.


ps.  If you took offence then that was not my intention at all.  I've developed a nasty cold and am not at my best at present.
0
 

Author Comment

by:sternocera
ID: 19563723
Andy,

No, not at all. You've been very reasonable, and very helpful. I havn't taken offence. What I said wasn't sarcasm or anything,

Thanks
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 19564068
>>>> I asked you to post the function so that the experts here can look at it.  
I looked at the above function and I would like to know the flags the (anchor) controls were created with. As far as I see the only changes of positions is made if the 'm_rcClient' rectangle had changed prior to calling that function. That seems to be a class member representing the client window of the formview in your case, which I don't know whether it was set correctly (or at all) in case of an update. If not, that could be a reasonable explanation why resizing failed.

Note, the m_rcClient was used for positioning for flags ANF_DOCK_ALL, ANF_DOCK_LEFT, ANF_DOCK_TOP, ANF_DOCK_RIGHT and ANF_DOCK_BOTTOM but not for the flags with suffix _EX.

>>>> A lot of the code isn't very suitable for use in a view.
It doesn't seem to me that the function makes things which were not senseful in a view - beside you would decline buttons in a view at all ...

The only thing what isn't considered is that the view form was adapted to the frame hence was different than it would have been in a dialog which gets its initial size from resource only. You could change that by adopting the frame to the view prior to calling ResizeParentToFit. But never forget to set the m_rcClient member after each change.

Regards, Alex


0
 

Author Comment

by:sternocera
ID: 19564154
I did a simple test.

I amended this to my OnSize function:
AfxMessageBox ("You have resized your window");

I never see this message box. The function is never called.

Your thoughts?
0
 

Author Comment

by:sternocera
ID: 19564387
Guys,

I forgot to include ON_WM_SIZE() in my message map. That was all. I once again find myself feeling stupid and grateful,

Regards
0
 

Accepted Solution

by:
ee_auto earned 0 total points
ID: 25353778
Question PAQ'd, 500 points refunded, and stored in the solution database.
0

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
Ready to get certified? Check out some courses that help you prepare for third-party exams.
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…

840 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question