计算富文本控件的最小尺寸






4.56/5 (3投票s)
1999 年 12 月 2 日
3分钟阅读

92971

3
本文解释了一种计算富文本编辑控件完全显示其所有内容所需的最小尺寸的技术。富文本编辑控件已经具有所谓的“无底”行为,但正如我们将看到的,它并没有解决计算最佳“宽度”的问题。
您的应用程序可以根据需要调整富文本编辑控件 (CRichEditControl) 的大小,以便该控件始终与其内容具有相同高度。 这就是所谓的“无底”行为。 CRichEditControl 通过在其内容的高度发生变化时向其父窗口发送 EN_REQUESTRESIZE 通知来支持它。
在处理 EN_REQUESTRESIZE 通知时,父窗口应将控件的大小调整为指定的 REQRESIZE 结构中的尺寸。 当然,父窗口还应该移动控件附近的任何图形元素,以便为控件的高度变化腾出空间。
为了激活 EN_REQUESTRESIZE 通知,应用程序必须设置控件事件掩码的 ENM_REQUESTRESIZE 事件标志。 应用程序还可以通过调用控件的 RequestResize 成员函数来“强制”控件发送 EN_REQUESTRESIZE 通知。 例如,以下代码就是这样做的
// 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( );
在 EN_REQUESTRESIZE 处理程序中,父窗口可以使用 CWnd::SetWindowPos 或 CWnd::MoveWindow 来调整控件的大小。
这种机制运作良好,但有一个细节。 它仅适用于调整控件的“高度”,而不是“宽度”。 关键是控件总是可以通过断字换行来解决宽度问题,从而再次将其变成“高度”问题。 然而,这是自动的,你无能为力来阻止这种行为。 您可以更改右边距 (CRichEditCtrl::SetRect),但您无法询问控件宽度的最佳值是多少。
现在,假设您有非常短的单行段落,您真的希望它们不被中断地显示。 然而,您希望控件不浪费水平真实状态,并且尽可能窄,与更宽的文本行一样窄 - 没有一个像素更宽(例如,对于具有此类要求的 UI,请查看我的 TCX 消息框 类)。
嗯,使用 CRichEditCtrl 的正常无底行为,你无法得到它。 如果你使用太窄的宽度,控件会断开文本行。 如果您使用太宽的宽度,UI 可能会看起来浪费空间。
我在 TCX 消息框 类中使用的技术是“二分查找”最佳宽度。 基本上,我首先将控件的大小调整为我可以在 UI 中承受的最大宽度(在 TCX 消息框 中,这意味着屏幕宽度的一半),然后我强制一个 EN_REQUESTRESIZE 通知并获取所需的高度。 这是控件显示其内容所需的最小高度。
我现在要做的就是找到仍然保持该高度的最小宽度。 如果我设置的宽度太小,控件会断开行并需要更大的高度。 由于该关系是线性的,我可以使用二分查找算法来优化搜索。
这是代码。
// 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 );
以及父窗口的 EN_REQUESTRESIZE 通知处理程序成员函数
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; }
最终注释
请注意,在 TCX 消息框 类中,我在实际显示窗口之前进行所有这些计算和调整大小。 否则,当应用程序遍历二分查找循环时,它会疯狂闪烁。 因此,如果需要重新计算 富文本编辑控件 在已经可见时的大小,则必须首先冻结父窗口重绘,或者应将 富文本编辑控件 移动到可见区域之外,进行所有计算,完成后,再将其返回到可见区域。