Edit Control 或 MessageBox() 中的表格字符串格式

Tabular string formatting in Edit Control or MessageBox()

我需要在只读多行编辑控件中以 table 格式显示一些数据。由于编辑控件的字体不是所有文本的宽度,我不能使用这种格式 "%-20s",所以我选择使用 \t 格式(见下面的代码)。但这并不能完全帮助我,因为它显示在图像中。



我尝试使用 GetTextExtentPoint32() API 但找不到 \t 的确切宽度。那么,如何正确对齐文本?

CString szMsg;
szMsg.Format(_T("%s\t%s\t%s\r\n\r\n%s\t%s\t%s\r\n%s\t%s\t%s\r\n%s\t%s\t%s"),
    _T("ITEM"), _T("VALUE"), _T("STATUS"),
    _T("XXXXXXXX"), _T("1.0001"), _T("PASSED"),
    _T("YYYYYYYYYYYYYYYY"), _T("-0.0001"), _T("FAILED"),
    _T("ZZZ"), _T("0.0101"), _T("PASSED")
    );
this->GetDlgItem(IDC_EDIT1)->SetWindowText(szMsg);

注意:
1。字符串将在 运行 时间内生成,因此它可以是任意长度。
2。我不想使用 ListCtrl 或 ListView,因为我应该允许用户执行 copy/paste 结果。

我认为您在这里使用的工具有误。

由于您需要向用户显示一些表格数据,我更喜欢使用专门为此设计的控件,例如 列表视图控件(在 报告模式)。你可以有一个对话框,里面有一个列表视图控件,并用它来向用户展示你的数据。

由于您使用 MFC 标记标记了这个问题,您可以考虑 CListCtrl class(或 CodeProject 上免费提供的其他几个增强的列表视图控件 类)。

如果您真的想以 "console-mode" 样式 格式化表格数据中的一些文本,您可能需要创建一个带有静态文本控件的对话框 (或只读编辑控件),并将其字体设置为固定宽度(非比例);但我认为以前的列表视图控制方法质量更高。

我做过这样的东西,

#define TAB_WIDTH 56

/*codes skipped*/

CString szItems[4] = { _T("ITEM"), _T("XXXXXXXX"), _T("YYYYYYYYYYYYYYYY"), _T("ZZZ") };
CString szValues[4] = { _T("VALUE"), _T("1.0010"), _T("-0.0009"), _T("0.1001") };
CString szStatus[4] = { _T("STATUS"), _T("Passed"), _T("Failed"), _T("Passed") };
int nTabs[3][4] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } };

CDC *pDC = this->GetDC();

CFont *pOldFont = pDC->SelectObject(this->GetDlgItem(IDC_EDIT1)->GetFont());

SIZE sizeText;

for (int ni = 0; ni < 4; ni++)
{
    GetTextExtentPoint32(pDC->GetSafeHdc(), szItems[ni], szItems[ni].GetLength(), &sizeText);
    nTabs[0][ni] = sizeText.cx / TAB_WIDTH;

    GetTextExtentPoint32(pDC->GetSafeHdc(), szValues[ni], szValues[ni].GetLength(), &sizeText);
    nTabs[1][ni] = sizeText.cx / TAB_WIDTH;

    GetTextExtentPoint32(pDC->GetSafeHdc(), szStatus[ni], szStatus[ni].GetLength(), &sizeText);
    nTabs[2][ni] = sizeText.cx / TAB_WIDTH;
}

pDC->SelectObject(pOldFont);

int nBig[3] = { 0, 0, 0 };
nBig[0] = BiggestValue(nTabs[0], 4);
nBig[1] = BiggestValue(nTabs[1], 4);
nBig[2] = BiggestValue(nTabs[2], 4);

CString szDispStr = _T("");

for (int ni = 0; ni < 4; ni++)
{
    szDispStr += szItems[ni];

    for (int nj = nTabs[0][ni]; nj <= nBig[0]; nj++)
        szDispStr += _T("\t");

    szDispStr += szValues[ni];

    for (int nj = nTabs[1][ni]; nj <= nBig[1]; nj++)
        szDispStr += _T("\t");

    szDispStr += szStatus[ni];

    for (int nj = nTabs[2][ni]; nj <= nBig[2]; nj++)
        szDispStr += _T("\t");

    szDispStr += _T("\r\n");

    if (ni == 0)
        szDispStr += _T("\r\n");
}

this->GetDlgItem(IDC_EDIT1)->SetWindowTextW(szDispStr);

并且输出符合预期

显示的编辑控件


并显示 MessageBox()


这里 TAB_WIDTH 是我手动计算像素的 \t 的宽度。现在我必须通过代码找到\t的宽度。

在多行编辑控件中显示表格数据的正确方法是在设置文本之前设置制表位。参见 EM_SETTABSTOPS message

这类似于现在被遗忘的打字机制表符,按下 TAB 键会将插入符号移动到右边最近的制表位。但是,您将无法右对齐数字数据;为此,您需要使用 ListView。