Automatic resizing controls in a MFC CFormView

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.
sternoceraAsked:
Who is Participating?
 
ee_autoCommented:
Question PAQ'd, 500 points refunded, and stored in the solution database.
0
 
itsmeandnobodyelseCommented:
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
 
sternoceraAuthor Commented:
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
Cloud Class® Course: Ruby Fundamentals

This course will introduce you to Ruby, as well as teach you about classes, methods, variables, data structures, loops, enumerable methods, and finishing touches.

 
itsmeandnobodyelseCommented:
>>>> 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
 
itsmeandnobodyelseCommented:
>>>> 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
 
sternoceraAuthor Commented:
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
 
sternoceraAuthor Commented:
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
 
sternoceraAuthor Commented:
*I should have said that the CDialog base class is called. Whoops.
0
 
AndyAinscowFreelance programmer / ConsultantCommented:
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
 
sternoceraAuthor Commented:
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
 
AndyAinscowFreelance programmer / ConsultantCommented:
repeat:  ps.  GetWindowRect gets the *window* rectangle - not necessarily the size of the client area.

Showing what is in HandleAnchors might be useful.
0
 
AndyAinscowFreelance programmer / ConsultantCommented:
consider this.
Client rectangle - 0,0,300,300
Window rectangle - 500,500,820,820

How will you code behave?
0
 
sternoceraAuthor Commented:
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
 
AndyAinscowFreelance programmer / ConsultantCommented:
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
 
sternoceraAuthor Commented:
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
 
AndyAinscowFreelance programmer / ConsultantCommented:
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
 
sternoceraAuthor Commented:
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
 
itsmeandnobodyelseCommented:
>>>> 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
 
sternoceraAuthor Commented:
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
 
sternoceraAuthor Commented:
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
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.