【问题标题】:DrawText with DT_CALCRECT and DT_RIGHT does not work带有 DT_CALCRECT 和 DT_RIGHT 的 DrawText 不起作用
【发布时间】:2023-03-26 16:51:01
【问题描述】:

我在一个默认的 VisualStudio 项目中有以下代码。它将 DT_CALCRECT 传递给 DrawTextW 以计算矩形以绘制一些文本,然后使用该矩形来绘制文本。要自己测试,只需将此代码粘贴到默认 VisualStudio 项目中:

case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    // TODO: Add any drawing code here...

    {
        wchar_t txt[] = L"abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef\r\nabc\r\n123456";
        BOOL useDT_RIGHT = TRUE; // <<< ** SWITCH THIS BETWEEN TRUE AND FALSE **
        wchar_t buf1[100] = {0};
        wchar_t buf2[100] = {0};
        RECT r1 = {0, 0, 192, 1000};
        RECT r2 = {r1.right + 10, r1.top, r1.right + 400, r1.top + 100};
        int ret1, ret2;

        FillRect(hdc, &r1, (HBRUSH)GetStockObject(GRAY_BRUSH));

        ret1 = DrawTextW(hdc, txt, -1, &r1,
            DT_CALCRECT |
            DT_WORDBREAK |
            (useDT_RIGHT == FALSE ? DT_LEFT : DT_RIGHT)
        );
        if(ret1 == 0) MessageBoxW(NULL, L"ret1 == 0", NULL, MB_OK);

        wsprintfW(buf1, L"useDT_RIGHT = %i\r\nDT_CALCRECT returned %i %i %i %i\r\nret1 = %i\r\n", useDT_RIGHT, r1.left, r1.top, r1.right, r1.bottom, ret1);

        ret2 = DrawTextW(hdc, txt, -1, &r1,
            DT_WORDBREAK |
            (useDT_RIGHT == FALSE ? DT_LEFT : DT_RIGHT)
        );
        if(ret2 == 0) MessageBoxW(NULL, L"ret2 == 0", NULL, MB_OK);

        wsprintfW(buf2, L"%sret2 = %i", buf1, ret2);
        DrawTextW(hdc, buf2, -1, &r2, DT_LEFT);
    }

    EndPaint(hWnd, &ps);
    break;

代码中,如果useDT_RIGHT设置为FALSE,文字左对齐,DT_CALCRECT返回正确的矩形,如下图:

http://i64.tinypic.com/2ptw2dk.png

如果useDT_RIGHT设置为TRUE,文本右对齐但DT_CALCRECT返回一个不正确的矩形,如下图:

http://i68.tinypic.com/nwx9co.png

或者更确切地说,它可能返回了一个正确的矩形,而随后实际绘制文本的调用是错误地绘制它,这是不可能的。

DT_CALCRECT 的文档说“如果有多行文本,DrawText 使用 lpRect 参数指向的矩形的宽度并扩展矩形的底部以绑定最后一行文本。如果最大的单词是比矩形更宽,宽度被扩大。如果文本小于矩形宽度,宽度被缩小。"

我期望的是 DrawTextW 返回的矩形将是绘制文本的正确大小(在实际代码中,矩形也用于定位周围的控件,所以只是随意扩展它不会真的帮助)。我还希望文本正确右对齐(即与左对齐文本相反),而不是上面第二个屏幕截图所示的混乱。我的意思是正确右对齐,如写字板的屏幕截图所示:

http: //i63.tinypic.com/qqya1u.png (请删除此链接中的空格以使其正常工作。)

这段代码有什么问题?为什么带有 DT_RIGHT 的 DT_CALCRECT 不能产生预期的结果?或者,如果是,为什么对 DrawTextW 的第二次调用没有正确绘制它?

【问题讨论】:

  • 是否有可能是右对齐文本的每一行末尾都有空格,这就是矩形宽度偏移的原因?
  • 我不确定你所说的偏移是什么意思。在这两种情况下都是相同的文本。在这两种情况下运行的是相同的代码。两个屏幕截图之间的唯一区别是一个使用 DT_RIGHT,另一个使用 DT_LEFT。然而 DrawText 正在绘制右对齐的错位。
  • 不,这不是同一个文本。第一个屏幕截图中左对齐的有一个额外的 123456 文本。我不确定我是否了解您的实际问题。您能解释一下,因为从屏幕截图中看不出来吗?
  • 你也可以试试 DT_EDITCONTROL 看看是否有什么不同。
  • 我刚试过 DT_EDITCONTROL 但没有任何区别。它相同的文本。在第二个屏幕截图中,DrawText 无法绘制 123456。如果将代码粘贴到默认的 VS 项目中,您可以自己查看。我的实际问题如上所述。 DrawText 在传递该文本 DT_CALCRECT 和 DT_LEFT 时返回一个矩形,该矩形可用于第二次调用 DrawText 以实际绘制文本。如果你做同样的事情,只将 DT_LEFT 更改为 DT_RIGHT,矩形要么太小,要么第二次调用 DrawText 绘制的文本被破坏。

标签: winapi drawtext


【解决方案1】:

似乎这种行为要么是错误,要么是设计使然。也许 DT_WORDBREAK 会删除空格,这就是为什么它在使用 DT_RIGHT 时会产生更窄的矩形。无论如何,当使用 DT_CALCRECT 与 DT_LEFT 或 DT_RIGHT 时,这是一种使 DrawText 行为相同的方法,您可以测试此代码(检查以 FIX 开头的注释):

case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);
    // TODO: Add any drawing code here...

    {
        wchar_t txt[] = L"abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef abcdef\r\nabc\r\n123456";
        BOOL useDT_RIGHT = FALSE; // TRUE; // <<< ** SWITCH THIS BETWEEN TRUE AND FALSE **
        wchar_t buf1[100] = { 0 };
        wchar_t buf2[100] = { 0 };
        RECT r1 = { 0, 0, 192, 1000 };
        RECT r2 = { r1.right + 10, r1.top, r1.right + 400, r1.top + 100 };
        int ret1, ret2;

        FillRect(hdc, &r1, (HBRUSH)GetStockObject(GRAY_BRUSH));

        ret1 = DrawTextW(hdc, txt, -1, &r1,
            DT_CALCRECT |
            DT_WORDBREAK | 
            (useDT_RIGHT == FALSE ? DT_LEFT : DT_RIGHT) 
            );
        if (ret1 == 0) MessageBoxW(NULL, L"ret1 == 0", NULL, MB_OK);

        // FIX: The following two lines make DrawText with DT_CALCRECT behave the same way for DT_LEFT and DT_RIGHT
        r1.right = 192;
        r1.bottom = ret1;

        wsprintfW(buf1, L"useDT_RIGHT = %i\r\nDT_CALCRECT returned %i %i %i %i\r\nret1 = %i\r\n", useDT_RIGHT, r1.left, r1.top, r1.right, r1.bottom, ret1);

        ret2 = DrawTextW(hdc, txt, -1, &r1,
            DT_WORDBREAK |
            (useDT_RIGHT == FALSE ? DT_LEFT : DT_RIGHT)
            );
        if (ret2 == 0) MessageBoxW(NULL, L"ret2 == 0", NULL, MB_OK);

        wsprintfW(buf2, L"%sret2 = %i", buf1, ret2);
        DrawTextW(hdc, buf2, -1, &r2, DT_LEFT);
    }

    EndPaint(hWnd, &ps);
    break;

【讨论】:

  • 其实第二行“r1.bottom = ret1;”是多余的,因为 r1.bottom 无论如何都等于 ret1,但为了清楚起见,我会留下它。这个想法是在调用第二个 DrawText 渲染文本之前将矩形的宽度重置为原始值。
  • 这是正确的答案,因为您可以从 DT_CALCRECT (示例中为 0 0 184 96)获得 DrawText 正在绘制的矩形的实际大小,然后 DrawText 正确地绘制文本右对齐在那个矩形中。必须在第二次调用 DrawText 之前将 r1.right 设置回 192,再加上第一次调用返回正确的矩形,这意味着错误的是正确对齐文本的实际绘制。
  • 这一定是我在核心功能中看到的第一个错误,它似乎在互联网上完全没有任何参考资料指出它(否则我没有使用正确的搜索词)。这个错误必须有几十年的历史。也许我是第一个尝试使用 DrawText 右对齐文本的人!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-11
  • 2012-04-23
  • 1970-01-01
  • 1970-01-01
  • 2011-12-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多