Click here to Skip to main content
15,903,362 members
Articles / Desktop Programming / MFC
Article

Calculating a Rich Edit Control Minimum Size

Rate me:
Please Sign up or sign in to vote.
4.56/5 (3 votes)
1 Dec 19993 min read 91.9K   22   5

This explains a technique to calculate the minimum size that's required by a Rich Edit Control to fully display all its contents. The Rich Edit Control already has what's called a "bottomless" behavior, but as we'll see, it's doesn't solve the problem of calculating the optimal "width".

Your application can resize a Rich Edit Control (CRichEditControl) as needed, so that the control is always the same height as its contents. This is what's called a "bottomless" behavior. The CRichEditControl supports it by sending its parent window a EN_REQUESTRESIZE notification whenever the height of its contents changes.

When processing the EN_REQUESTRESIZE notification the parent window should resize the control to the dimensions in the specified REQRESIZE structure. Of course the parent window should also move any graphical element near the control to open room for the control's change in height.

In order to activate the EN_REQUESTRESIZE notification, the application must set the ENM_REQUESTRESIZE event flag of the control's event mask. The application can also "force" the control to send a EN_REQUESTRESIZE notification by calling the control's RequestResize member function. For example, the following code does that:

// m_Ctrl is a CRichEditCtrl object

// Set the ENM_REQUESTRESIZE event flag
m_Ctrl .SetEventMask( ENM_REQUESTRESIZE );

// Force the control to issue a EN_REQUESTRESIZE notification
m_edCtrl.RequestResize( );

In the EN_REQUESTRESIZE handler, the parent window can use CWnd::SetWindowPos or CWnd::MoveWindow to resize the control.

This mechanism works very well, but for one detail. It works only to adjust the "height" of the control, not its "width". The point is that the control can always solve problems of width by word breaking lines, thus turning it again in a "height" problem. Yet, this is automatic and there's nothing you can do to prevent this behavior. You can change the right margin (CRichEditCtrl::SetRect), but you can't ask the control what's the optimal value for the width.

Now, suppose you have very short single line paragraphs that you really want to display unbroken. Yet, you would like the control to not waste horizontal real state, and to be as narrow as the wider text line - no one single pixel wider (for an example of a UI that has such requirements, take a look in my TCX Message Box class).

Well, with the CRichEditCtrl's normal bottomless behavior you don't get it. If you use a too narrow width, the control will break the text lines. And if you use a too wide width, the UI might look wasting space.

The technique I used in the TCX Message Box class is a "binary search" for the best width. Basically I start by sizing the control to the largest width that I can afford in the UI (in the TCX Message Box that means 1/2 the of the screen width), then I force a EN_REQUESTRESIZE notification and take the required height. This is the minimum height the control needs to show its contents.

All I have to do now is to find the minimum width that still keeps that height. If I set a too small width, the control will break the lines and require a larger height. Since the relation is linear, I can optimize the search with a binary search algorithm.

Here're the code.

// Calculating the CRichEditCtrl m_Ctrl minimum size

m_Ctrl.SetEventMask( ENM_REQUESTRESIZE );

// m_dimRtf is a CSize object that stores m_Ctrl required size

m_dimRtf.cx = 0;
m_dimRtf.cy = 0;

// Performing the binary search for the best dimension

int cxFirst = 0;
int cxLast = ::GetSystemMetrics( SM_CXFULLSCREEN ) / 2;
int cyMin = 0;

cxLast *= 2;

do
{
   // Taking a guess
   int cx = ( cxFirst + cxLast ) / 2;

   // Testing this guess
   CRect rc( 0, 0, cx, 1 );
   m_Ctrl.MoveWindow( rc );
   m_Ctrl.RequestResize();

   // If it's the first time, take the result anyway.
   // This is the minimum height the control needs
   if( cyMin == 0 )
      cyMin = m_dimRtf.cy;

   // Iterating
   if( m_dimRtf.cy > cyMin )
   {
      // If the control required a larger height, then
      // it's too narrow.
      cxFirst = cx + 1;
   }
   else
   {
      // If the control didn't required a larger height,
      // then it's too wide.
      cxLast = cx - 1;
   }
}
while( cxFirst < cxLast );

// Giving it a few pixels extra width for safety
m_dimRtf.cx += 2;

// Moving the control
m_Ctrl.MoveWindow( xMsg, cyTop, m_dimRtf.cx, m_dimRtf.cy );

And, the parent's window EN_REQUESTRESIZE notification handler member function:

void CParenWindow::OnRequestResize( NMHDR* pNMHDR, LRESULT* pResult )
{
   _ASSERT( pNMHDR->code == EN_REQUESTRESIZE );

   // Storing the requested sized to be used in the binary search

   REQRESIZE* prr = (REQRESIZE*)pNMHDR;
   m_dimRtf.cx = prr->rc.right - prr->rc.left;
   m_dimRtf.cy = prr->rc.bottom - prr->rc.top;

   *pResult = NULL;
}

Final Notes

Do note that, in the TCX Message Box class, I do all this calculation and resizing before I actually display the window. Otherwise, it will flick crazily as the application traverses the binary search loop. Therefore, if you need to recalculate the Rich Edit Control size when it's already visible, you must first freeze the parent window redraw, or you should move the Rich Edit Control to outside of the visible area, do all the calculation and, when it's done, you get it back to the visible area.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralIt is possible to get the width Pin
domehead15-Jul-08 1:56
domehead15-Jul-08 1:56 
GeneralRe: It is possible to get the width Pin
Acidtech8-Mar-10 17:41
Acidtech8-Mar-10 17:41 
GeneralI run your rcxmessagebox sample, the size is not correct Pin
code_discuss25-Feb-08 14:23
code_discuss25-Feb-08 14:23 
All dialogs are very narrow(too narrow to see the messages!) and very high(higher than the screen), both in XP and in Vista.
GeneralGets wrong size sometimes: Solution Pin
Paul Vickery4-Mar-05 0:22
professionalPaul Vickery4-Mar-05 0:22 
GeneralFIX for Windows 2000 Pin
Harold Harkema26-Feb-00 4:57
sussHarold Harkema26-Feb-00 4:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.