解决win10系统中调用DrawText时传入DT_ENDELLIPSIS仍然不显示省略号的问题
Posted dvlinker
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了解决win10系统中调用DrawText时传入DT_ENDELLIPSIS仍然不显示省略号的问题相关的知识,希望对你有一定的参考价值。
经测试发现,我们PC程序在win10系统中运行时,个别窗口中显示文字的控件,在文字显示不下时并没有显示“...”省略号,但在win7系统是会显示省略号的。
我们PC程序的UI界面库使用的是duilib,控件文字的绘制调用的是系统API函数DrawText,已经给出问题的控件设置了DT_END_ELLIPSIS,但在win10系统中文字显示不下时还是没有显示省略号,在win7系统中则会显示省略号,这个有点奇怪,不知道这是不是win10系统的bug?
后来我们在CodeProject上搜到了一篇可以参考的文章:
Implementing the DT_END_ELLIPSIS flag on the Pocket PChttps://www.codeproject.com/articles/7079/implementing-the-dt-end-ellipsis-flag-on-the-pocke参照文章中的方法,修改了duilib开源库中负责绘制控件文字的接口CRenderEngine::DrawText的实现,修改后的代码如下:
void CRenderEngine::DrawText( HDC hDC, CPaintManagerUI* pManager, RECT& rc, LPCTSTR pstrText, DWORD dwTextColor, int iFont, UINT uStyle )
ASSERT(::GetObjectType(hDC) == OBJ_DC || ::GetObjectType(hDC) == OBJ_MEMDC);
if (pstrText == NULL || pManager == NULL) return;
::SetBkMode(hDC, TRANSPARENT);
::SetTextColor(hDC, RGB(GetBValue(dwTextColor), GetGValue(dwTextColor), GetRValue(dwTextColor)));
HFONT hOldFont = (HFONT)::SelectObject(hDC, pManager->GetFont(iFont));
// 检查一下待绘制的字符串中是否有换行符,如果有换行符,则不能使用下面处理省略号的算法
bool bHasCarriageReturn = false;
CStdString strTmp = pstrText;
if ( strTmp.Find( _T("\\n") ) != -1 )
bHasCarriageReturn = true;
else if ( strTmp.Find( _T("\\r\\n") ) != -1 )
bHasCarriageReturn = true;
if ( (uStyle & DT_WORDBREAK) || bHasCarriageReturn )
::DrawText(hDC, pstrText, -1, &rc, uStyle | DT_NOPREFIX );
else if( uStyle & (DT_END_ELLIPSIS | DT_MODIFYSTRING) )
// 为了解决endellipsis的省略号显示不全问题:
// 1)有些情况下省略号的三个点显示不全;
// 2)win7下省略号显示正常,win10下不显示省略号
// 现做如下处理,参考
// https://www.codeproject.com/articles/7079/implementing-the-dt-end-ellipsis-flag-on-the-pocke
// 注意:下面的代码只能处理单行文字显示时的结尾省略号的问题
int nCount = 0;
int nWidth = rc.right - rc.left;
SIZE szStr = 0, 0 ;
LPTSTR lpStr = NULL;
// 计算需绘制的字符串字符个数
nCount = _tcslen(pstrText);
if( nCount == 0 || nCount == -1 )
// 0是没有字符需要绘制,直接返回,-1是计算字符串长度出错
::SelectObject(hDC, hOldFont);
return;
// 复制一份文字内容
lpStr = new TCHAR[nCount+1];
ZeroMemory( lpStr , (nCount+1)*sizeof(TCHAR) );
_tcscpy_s( lpStr, nCount+1, pstrText );
lpStr[nCount] = _T('\\0');
// 检查如果字符串长度大于绘制区域宽度
GetTextExtentPoint32( hDC, lpStr, nCount, &szStr );
if( szStr.cx > nWidth && nWidth > 0 )
// 估算出绘制区域能够显示的字符数量
// 估算公式:nWidth/nEstimate = szStr.cx/nCount
// 该公式假定字符串中的字符宽度是相等的,实际上数字或字母与中文的宽度是不同的,此处
// 只是粗略的估计
int nEstimate = (nCount * nWidth) / szStr.cx + 1;
if(nEstimate < nCount)
nCount = nEstimate;
const DWORD dwEllipsisUnicode = 0x2026;
// 0x2026对应省略号三个点的Unicode编码
lpStr[nCount-1] = (TCHAR)dwEllipsisUnicode;
GetTextExtentPoint32( hDC, lpStr, nCount, &szStr );
// 当延伸的内容大于绘制区域的宽度,移除这个字符
while(szStr.cx > nWidth && nCount > 1)
lpStr[--nCount] = 0; // 移除字符
lpStr[nCount-1] = (TCHAR)dwEllipsisUnicode; // 用...代替
GetTextExtentPoint32(hDC, lpStr, nCount, &szStr);
::DrawText(hDC, lpStr, nCount, &rc, uStyle | DT_NOPREFIX );
delete[] lpStr;
lpStr = NULL;
else
::DrawText(hDC, pstrText, -1, &rc, uStyle | DT_NOPREFIX );
else
::DrawText(hDC, pstrText, -1, &rc, uStyle | DT_NOPREFIX );
::SelectObject(hDC, hOldFont);
解决此问题的思路是:先计算出当前要绘制的文字长度,如果大于当前控件的宽度则逐个去除末尾的一个字符,然后通过代码人为地加上“...”省略号,然后计算出包含省略号的字符串的长度是否还大于控件的宽度,如果还大于控件的宽度,则继续取出末尾的一个字符,直到能显示的下包含“...”省略号的字符串为止!
以上是关于解决win10系统中调用DrawText时传入DT_ENDELLIPSIS仍然不显示省略号的问题的主要内容,如果未能解决你的问题,请参考以下文章
为啥使用 DT_MODIFYSTRING 选项将副本传递给 DrawText 函数时,原始 CString 会被覆盖?
MFC DrawText,垂直,DT_CALCRECT with lf_escapement = 900