This explains a technique to calculate the minimum size that's required by aRich Edit Control to fully display all its contents. TheRich Edit Controlalready has what's called a "bottomless" behavior, but as we'll see, it's doesn'tsolve the problem of calculating the optimal "width".
Your application can resize aRich Edit Control (CRichEditControl) asneeded, so that the control is always the sameheight as its contents. Thisis what's called a "bottomless" behavior. TheCRichEditControl supports it bysending its parent window aEN_REQUESTRESIZE notification whenever the height ofits contents changes.
When processing theEN_REQUESTRESIZE notification the parent window should resizethe control to the dimensions in the specifiedREQRESIZE structure. Of course theparent window should also move any graphical element near the control to open room for thecontrol's change in height.
In order to activate theEN_REQUESTRESIZE notification, the application must settheENM_REQUESTRESIZE event flag of the control's event mask. The application canalso "force" the control to send aEN_REQUESTRESIZE notification by calling thecontrol'sRequestResize 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 theEN_REQUESTRESIZE handler, the parent window can useCWnd::SetWindowPosorCWnd::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 problemsof width by word breaking lines, thus turning it again in a "height" problem. Yet, thisis automatic and there's nothing you can do to prevent this behavior. You can changethe right margin (CRichEditCtrl::SetRect), but you can't ask the control what'sthe optimal value for the width.
Now, suppose you have very short single line paragraphs that you really want to displayunbroken. Yet, you would like the control to not waste horizontal real state, and to beas narrow as the wider text line - no one single pixel wider (for an example of a UI thathas such requirements, take a look in myTCX Message Box class).
Well, with theCRichEditCtrl'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 atoo wide width, the UI might look wasting space.
The technique I used in theTCX Message Box classis a "binary search" for the best width. Basically I start by sizing the control to thelargest width that I can afford in the UI (in theTCX Message Boxthat means 1/2 the of the screen width), then I force aEN_REQUESTRESIZE notificationand take the required height. This is the minimum height the control needs to showits contents.
All I have to do now is to find the minimum width that still keeps that height. If Iset 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 dimensionint cxFirst =0;int cxLast = ::GetSystemMetrics( SM_CXFULLSCREEN ) /2;int cyMin =0; cxLast *=2;do {// Taking a guessint 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 needsif( cyMin ==0 ) cyMin = m_dimRtf.cy;// Iteratingif( 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 windowEN_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 theTCX Message Box class, I doall this calculation and resizing before I actually display the window. Otherwise, it will flickcrazily as the application traverses the binary search loop. Therefore, if you need to recalculatetheRich Edit Control size when it's already visible, you must first freeze the parentwindow redraw, or you should move theRich 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.