さて、私はそれを解決することができました。将来の参考のために、ここに私がやったことがあります:
最初に、等幅フォントを見つけようとしましたが、本当に等幅フォント (ラテン文字と漢字が同じ幅) を見つけることができませんでした。
次に、テキストをウィンドウの中央に配置しようとしました。Auto HScroll を true (リッチ エディット コントロール用に定義された ES_AUTOHSCROLL) に設定すると、setParaFormat がテキストを中央に配置しようとしても無視されることに気付くまで、これを行うことができませんでした。無効にして、描画可能なテキスト領域のサイズを手動で設定した後、テキストを中央に配置できました。念のため、リッチ エディット ボックスで描画可能領域の幅を設定するために使用したコードを次に示します。
CDC* pDC = GetDC();
long lw = 99999999;
SetTargetDevice(*GetDC(), lw);
テキストの中央揃えが機能するかどうかをテストできるように、 lw を任意に大きく設定しました。それはしませんでした。結局のところ、リッチ エディット コントロールがテキストを中央揃えにすると、文字数ではなく、テキストの描画幅に基づいて配置されます。クエリ文字列の両側に同じ数の文字があるため、文字列が中央に配置されると思いましたが、そうではありませんでした。
私が試した最終的な解決策は、ymett によって提案されたものでした。少し調整した後、すべてのテキストがリッチ エディット コントロールに挿入された後に呼び出される alignText() という関数を思いつきました。関数は次のとおりです (注: コントロールの各行には、この関数が呼び出される前にタブが挿入されていました。1 つは行の先頭に、もう 1 つはクエリ文字列の後に挿入されています。たとえば、"string1-query-string2" は "\tstring1-query\t- string2")
void CFormViewConcordanceRichEditCtrl::alignText()
{
long maxSize = 0;
CFont font;
LOGFONT lf = {0};
CHARFORMAT cf = {0};
this->GetDefaultCharFormat(cf);
//convert a CHARFORMAT struct into a LOGFONT struct
if (cf.dwEffects & CFE_BOLD)
lf.lfWeight = FW_BOLD;
else
lf.lfWeight = FW_NORMAL;
if (cf.dwEffects & CFE_ITALIC)
lf.lfItalic = true;
if (cf.dwEffects & CFE_UNDERLINE)
lf.lfUnderline = true;
if (cf.dwEffects & CFE_STRIKEOUT)
lf.lfStrikeOut = true;
lf.lfHeight = cf.yHeight;
_stprintf(lf.lfFaceName, _T("%s"), cf.szFaceName);
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
font.CreateFontIndirect(&lf);
//create a display context
CClientDC dc(this);
dc.SetMapMode(MM_TWIPS);
CFont *pOldFont = dc.SelectObject(&font);
//find the line that as the longest preceding string when drawn
for(int i = 0; i < m_pParent->m_nDataArray; i++)
{
CString text = m_pParent->DataArray[i].text.Left(BUFFER_LENGTH + m_pParent->generateText.GetLength());
text.Replace(_T("\r"), _T(" "));
text.Replace(_T("\n"), _T(" "));
text.Replace(_T("\t"), _T(" "));
CRect rc(0,0,0,0);
dc.DrawText(text, &rc, DT_CALCRECT);
int width = 1.0*cf.yHeight/fabs((double)rc.bottom - rc.top)*(rc.right - rc.left);
width = dc.GetTextExtent(text).cx;
if(width > maxSize)
maxSize = width;
}
dc.SelectObject(pOldFont);
//this calulates where to place the first tab. The 0.8 is a rought constant calculated by guess & check, it may be innacurate.
long tab = maxSize*0.8;
PARAFORMAT pf;
pf.cbSize = sizeof(PARAFORMAT);
pf.dwMask = PFM_TABSTOPS;
pf.cTabCount = 2;
pf.rgxTabs[0] = tab + (2 << 24); //make the first tab right-aligned
pf.rgxTabs[1] = tab + 25;
//this is to preserve the user's selection and scroll positions when the selection is changed
int vScroll = GetScrollPos(SB_VERT);
int hScroll = GetScrollPos(SB_HORZ);
long spos, epos;
GetSel(spos, epos);
//select all the text
SetSel(0, -1);
//this call is very important, but I'm not sure why
::SendMessage(GetSafeHwnd(), EM_SETTYPOGRAPHYOPTIONS, TO_ADVANCEDTYPOGRAPHY, TO_ADVANCEDTYPOGRAPHY);
this->SetParaFormat(pf);
//now reset the user's selection and scroll positions
SetSel(spos, epos);
::SendMessage(GetSafeHwnd(),
WM_HSCROLL,
(WPARAM) ((hScroll) << 16) + SB_THUMBPOSITION,
(LPARAM) NULL);
::SendMessage(GetSafeHwnd(),
WM_VSCROLL,
(WPARAM) ((vScroll) << 16) + SB_THUMBPOSITION,
(LPARAM) NULL);
}
基本的に、この関数が行うことは、最初のタブ ストップを右揃えにして、コントロール内の x 方向の右に設定することです。次に、2 番目のタブ ストップをその少し右側に配置し、左揃えにします。したがって、各行の先頭からクエリ文字列の末尾まで (最初の \t から 2 番目の \t まで) のすべてのテキストは、最初のタブ ストップに対して右に押し出され、残りのすべてのテキストは左に押し出されます。 2 番目のタブ ストップに対して、クエリ文字列がコントロール内のすべての行の間に配置されます。関数の最初の部分は x を見つけ (各行が描画される長さを調べて最大値を取得することにより)、2 番目の部分はタブ ストップを設定します。
ソリューションを提供してくれた ymett に再び感謝します。