解决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仍然不显示省略号的问题的主要内容,如果未能解决你的问题,请参考以下文章

Win GDI DrawText 渲染错误

为啥使用 DT_MODIFYSTRING 选项将副本传递给 DrawText 函数时,原始 CString 会被覆盖?

MFC DrawText,垂直,DT_CALCRECT with lf_escapement = 900

带有 DT_CALCRECT 的 DrawText - 有没有办法在不修改宽度的情况下计算矩形的高度(使用大字符串)?

delphi DrawText 的用法

Win32 C++ 中的 DrawText 会增加内存使用量