DT_CALCRECT 和 DT_RIGHT 的 DrawText 不起作用

DrawText with DT_CALCRECT and DT_RIGHT does not work

我在默认的 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_CALCRECTreturns正确的矩形,如下图:

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

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

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

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

DT_CALCRECT 的文档说 "If there are multiple lines of text, DrawText uses the width of the rectangle pointed to by the lpRect parameter and extends the base of the rectangle to bound the last line of text. If the largest word is wider than the rectangle, the width is expanded. If the text is less than the width of the rectangle, the width is reduced."

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

http: //i63.tinypic.com/qqya1u.png (请从 link 中删除 space 以使其正常工作。)

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

看来此行为是错误或设计使然。也许 DT_WORDBREAK 删除了空格,这就是为什么它在使用 DT_RIGHT 时会产生更窄的矩形。无论如何,这里有一种方法可以使 DrawText 在 DT_CALCRECT 与 DT_LEFT 或 DT_RIGHT 一起使用时以相同的方式运行,您可以测试此代码(检查以 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;