Search code examples
c++stringmfcmessagebox

Tabular string formatting in Edit Control or MessageBox()


I need to display some data in table format in a read-only multi-line edit control. Since the the edit control's font doesn't have even width for all texts, I could not use this formatting "%-20s", so I chose to use \t formatting (see code at the below). But that doesn't help me completely because it displays like in the image.

enter image description here

I tried using GetTextExtentPoint32() API but it could not find the exact width of \t. So, how do I align the texts correctly?

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);

Note:
1. The strings would be generated during run-time, so it can be of any length.
2. I don't want to use ListCtrl or ListView because I should allow the user to do copy/paste the result.


Solution

  • I've made something like this,

    #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);
    

    and the output is as expected

    the edit control displayed
    enter image description here

    and the MessageBox() displayed
    enter image description here

    here TAB_WIDTH is the width of \t which I counted the pixels manually. Now I have to find the width of \t through code.