在所有者绘制控件中更改行高
在所有者绘制的列表视图控件中更改行高
目录
历史
2001-10-10 插入示例 "multiline.zip",来自 Danielle。
2001-09-27 创建文章
前言
本文是 Codeguru 上文章 "更改拥有者绘制控件中的行高" 的一个近乎改编。在尽量保留原有内容的同时,我尽可能地对其进行了增强。我还包含了对文章评论的总结。
引言
当您更改 列表视图 控件的字体时,控件或其父窗口没有机会重新指定行高。没有 WM_MEASUREITEM
消息会被发送到控件的父窗口。最终结果是,当更改控件的字体时,行高将不再有效。
这里有一个解决方法,可以强制生成 WM_MEASUREITEM
消息
分步指南
以下步骤中的代码使用了 MFC 及其类 CListCtrl。
步骤 1:为 WM_SETFONT
添加处理程序
WM_MEASUREITEM
消息在控件创建时发送。幸运的是,每当控件调整大小时都会发送此消息。由于我们希望在字体更改时调整行高,那么在 WM_SETFONT
处理程序中执行此操作再合适不过了。在 OnSetFont()
中,我们欺骗 Windows,使其认为控件的窗口大小已更改。
我们通过向控件发送 WM_WINDOWPOSCHANGED
消息来实现这一点,然后该消息由控件的默认窗口过程处理。请注意,我们没有指定 SWP_NOSIZE
标志,并且我们将 WINDOWPOS
结构中的宽度和高度字段设置为等于现有尺寸。
在头文件中
//{{AFX_MSG(CMyListCtrl) : : //}}AFX_MSG afx_msg LRESULT OnSetFont(WPARAM wParam, LPARAM); afx_msg void MeasureItem ( LPMEASUREITEMSTRUCT lpMeasureItemStruct ); DECLARE_MESSAGE_MAP()
在 CPP 文件中
BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl) //{{AFX_MSG_MAP(CMyListCtrl) : : //}}AFX_MSG_MAP ON_MESSAGE(WM_SETFONT, OnSetFont) ON_WM_MEASUREITEM_REFLECT() END_MESSAGE_MAP() LRESULT CMyListCtrl::OnSetFont(WPARAM wParam, LPARAM) { LRESULT res = Default(); CRect rc; GetWindowRect( &rc ); WINDOWPOS wp; wp.hwnd = m_hWnd; wp.cx = rc.Width(); wp.cy = rc.Height(); wp.flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER; SendMessage( WM_WINDOWPOSCHANGED, 0, (LPARAM)&wp ); return res; }
步骤 2:为 WM_MEASUREITEM
添加处理程序
由于我们希望我们的 CListCtrl
派生类是模块化的,因此我们将在该类中处理 WM_MEASUREITEM
消息。但是,此消息发送到父窗口,因此我们使用消息反射。同样,类向导的帮助不大。我们必须手动将条目添加到消息映射中并更新头文件。有关此内容,请参见第一步中的代码片段。
void CMyListCtrl::MeasureItem( LPMEASUREITEMSTRUCT lpMeasureItemStruct ) { LOGFONT lf; GetFont()->GetLogFont( &lf ); if ( lf.lfHeight < 0 ) lpMeasureItemStruct->itemHeight = -lf.lfHeight; else lpMeasureItemStruct->itemHeight = lf.lfHeight; }
结论
到目前为止,这些步骤都改编自原始文章。接下来是原始文章中最有趣的评论的摘要。评论包括对先前步骤的修复和更正。由于我希望保留文章的原样,因此我决定*不*在上面的代码片段中包含建议的修复。是否使用它们由您决定。另一个原因是,我不知道所有建议的修复是否正确。请在此处留下评论,以便我删除/调整任何错误。
读者评论
MeasureItem()
例程的正确实现
问题/解决方案:
在 MeasureItem()
例程中为 CListCtrl
派生对象设置行高时,不应使用 LOGFONT
结构体的 lfHeight
成员。此值是逻辑磅值,不是物理像素值。对于不同的屏幕分辨率和更大的字体值,行高仍然会不正确。以下代码显示了一个实现 MeasureItem()
例程的示例,该例程应适用于所有这些情况,并且产生的行高与默认实现完全相同。
void CMyListCtrl::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) { TEXTMETRIC tm; HDC hDC = ::GetDC(NULL); CFont* pFont = GetFont(); HFONT hFontOld = (HFONT)SelectObject(hDC, pFont->GetSafeHandle()); GetTextMetrics(hDC, &tm); lpMeasureItemStruct->itemHeight = tm.tmHeight + tm.tmExternalLeading + 1; SelectObject(hDC, hFontOld); ::ReleaseDC(NULL, hDC); }
(Paul Geringer, 1999-04-06 )
我没有收到 WM_SETFONT
或 WM_MEASUREITEM
消息
问题:
我没有收到 WM_SETFONT
或 WM_MEASUREITEM
消息。
(Damian Williams, 1999-10-18)
解决方案
- 将列表控件的样式修改为“拥有者绘制固定”(
LVS_OWNERDRAWFIXED
)。 - 重载
CListCtrlEx::DrawItem()
。 WM_MEASUREITEM
消息将被发送到列表控件的父窗口。
(Chung Hyun, Lee, 2000-05-25)
解决方案 2:
类向导不会显示 =WM_MEASUREITEM
反射消息。如果您使用类向导获取 WM_MEASUREITEMS
,它只能由列表视图控件以外的类处理,即父窗口。这是不令人满意的。要获取反射消息,您必须在 .CPP 文件中的消息映射中添加以下内容
ON_WM_MEASUREITEM_REFLECT()
然后,您必须在头文件消息映射中添加以下内容
afx_msg void MeasureItem( LPMEASUREITEMSTRUCT lpMeasureItemStruct );
然后,在 .CPP 文件中添加以下函数
void CMyListCtrl::MeasureItem ( LPMEASUREITEMSTRUCT lpMeasureItemStruct ) { // this should happen when the control is created. AfxMessageBox("You just received a WM_MEASUREITEM message"); }
(Dominic Gamble, 2000-01-13)
解决方案 3:
首先,您必须使用 LVS_OWNERDRAWFIXED
样式创建列表视图控件。然后,使用 CWnd::MoveWindow()
或 CWnd::SetWindowPos()
函数更改控件的大小。
这两种情况都适用:手工创建的控件(通过调用 CListCtrl::Create()
)和模板创建的控件(使用资源编辑器)。
(Gon (Jung-gon, Kim), 2000-08-09)
更改行高而不使用 LVS_OWNERDRAWFIXED
问题/解决方案
您可以通过将图像列表图标的大小设置为所需大小来轻松更改行高(并通过绘制非实际大小的图标来欺骗用户)。例如:
imagelist.Create( 24, 24, ILC_COLOR4, 10, 10 ); m_cList.SetImageList( &imageList, LVSIL_SMALL );
如果您的图标大小为 16x16,则将行高增加 50%。这看起来有点笨拙,但它很容易奏效。
(Allan Gallano, 2000-11-17)
另请参阅/参考文献
- 更改拥有者绘制控件中的行高, Zafir Anjum, 1998-08-06, www.codeguru.com。
- 选择高亮显示整行, Uwe Keim, 2001-09-26, www.codeproject.com。
- 使用自定义绘制在列表控件中进行的整洁操作, Michael Dunn, 1999-11-30, www.codeproject.com。