MFC `CRichEditCtrl` 功能強大支援富文本,但預設不響應滑鼠右鍵,也缺乏內建的字體選擇對話框整合。本文示範繼承 `CRichEditCtrl` 實作 `CMyRichEdit`,加入右鍵彈出選單(撤銷、剪貼、複製、貼上、清除、全選、字型)、選單項目啟用/禁用狀態動態更新、字體對話框雙向同步(選取文字套用/預設字體設定),完整可直接放入專案使用。
class CMyRichEdit : public CRichEditCtrl
{
DECLARE_DYNAMIC(CMyRichEdit)
public:
CMyRichEdit();
virtual ~CMyRichEdit();
protected:
// 選單命令 ID
enum { ID_UNDO = 101, ID_CUT, ID_COPY, ID_PASTE, ID_CLEAR, ID_SELECTALL, ID_SETFONT };
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg void OnUndo(); afx_msg void OnCut();
afx_msg void OnCopy(); afx_msg void OnPaste();
afx_msg void OnClear(); afx_msg void OnSelectAll();
afx_msg void OnSetFont();
DECLARE_MESSAGE_MAP()
};
void CMyRichEdit::OnRButtonDown(UINT nFlags, CPoint point)
{
SetFocus(); // 確保焦點在編輯框
CMenu menu;
menu.CreatePopupMenu();
// 加入項目
menu.AppendMenu(MF_STRING, ID_UNDO, _T("&Undo"));
menu.AppendMenu(MF_SEPARATOR);
menu.AppendMenu(MF_STRING, ID_CUT, _T("Cu&t"));
menu.AppendMenu(MF_STRING, ID_COPY, _T("&Copy"));
menu.AppendMenu(MF_STRING, ID_PASTE, _T("&Paste"));
menu.AppendMenu(MF_STRING, ID_CLEAR, _T("C&lear"));
menu.AppendMenu(MF_SEPARATOR);
menu.AppendMenu(MF_STRING, ID_SELECTALL, _T("Select &All"));
menu.AppendMenu(MF_SEPARATOR);
menu.AppendMenu(MF_STRING, ID_SETFONT, _T("&Font..."));
// 動態啟用/禁用
BOOL bCanUndo = CanUndo();
menu.EnableMenuItem(ID_UNDO, bCanUndo ? MF_ENABLED : MF_GRAYED);
BOOL bHasSel = (GetSelectionType() != SEL_EMPTY);
menu.EnableMenuItem(ID_CUT, bHasSel ? MF_ENABLED : MF_GRAYED);
menu.EnableMenuItem(ID_COPY, bHasSel ? MF_ENABLED : MF_GRAYED);
menu.EnableMenuItem(ID_CLEAR, bHasSel ? MF_ENABLED : MF_GRAYED);
BOOL bCanPaste = CanPaste();
menu.EnableMenuItem(ID_PASTE, bCanPaste ? MF_ENABLED : MF_GRAYED);
// 顯示選單:螢幕座標
ClientToScreen(&point);
menu.TrackPopupMenu(TPM_RIGHTBUTTON | TPM_LEFTALIGN, point.x, point.y, this);
menu.DestroyMenu();
CRichEditCtrl::OnRButtonDown(nFlags, point);
}
BEGIN_MESSAGE_MAP(CMyRichEdit, CRichEditCtrl)
ON_WM_RBUTTONDOWN()
ON_COMMAND(ID_UNDO, OnUndo)
ON_COMMAND(ID_CUT, OnCut)
ON_COMMAND(ID_COPY, OnCopy)
ON_COMMAND(ID_PASTE, OnPaste)
ON_COMMAND(ID_CLEAR, OnClear)
ON_COMMAND(ID_SELECTALL, OnSelectAll)
ON_COMMAND(ID_SETFONT, OnSetFont)
END_MESSAGE_MAP()
void CMyRichEdit::OnUndo() { Undo(); }
void CMyRichEdit::OnCut() { Cut(); }
void CMyRichEdit::OnCopy() { Copy(); }
void CMyRichEdit::OnPaste() { Paste(); }
void CMyRichEdit::OnClear() { Clear(); }
void CMyRichEdit::OnSelectAll() { SetSel(0, -1); }
void CMyRichEdit::OnSetFont()
{
CHARFORMAT2 cf = {0}; cf.cbSize = sizeof(CHARFORMAT2);
LOGFONT lf = {0};
// 取得目前選取或預設字體格式
BOOL bHasSel = (GetSelectionType() != SEL_EMPTY);
if (bHasSel) GetSelectionCharFormat(cf);
else GetDefaultCharFormat(cf);
// CHARFORMAT2 → LOGFONT
lf.lfCharSet = cf.bCharSet;
lf.lfHeight = cf.yHeight / 20; // twips → 華氏點
lf.lfPitchAndFamily = cf.bPitchAndFamily;
lf.lfWeight = (cf.dwEffects & CFE_BOLD) ? FW_BOLD : FW_NORMAL;
lf.lfItalic = (cf.dwEffects & CFE_ITALIC) ? TRUE : FALSE;
lf.lfUnderline = (cf.dwEffects & CFE_UNDERLINE) ? TRUE : FALSE;
lf.lfStrikeOut = (cf.dwEffects & CFE_STRIKEOUT) ? TRUE : FALSE;
_tcscpy_s(lf.lfFaceName, LF_FACESIZE, cf.szFaceName);
CFontDialog dlg(&lf, CF_EFFECTS | CF_SCREENFONTS, NULL, this);
dlg.m_cf.rgbColors = cf.crTextColor; // 傳入目前文字顏色
if (dlg.DoModal() == IDOK)
{
dlg.GetCharFormat(cf); // 取回使用者選擇的格式
if (bHasSel)
SetSelectionCharFormat(cf); // 套用到選取文字
else
SetWordCharFormat(cf); // 設定預設字體(後續輸入生效)
}
}

右鍵彈出選單,項目依編輯狀態啟用/禁用,選擇 Font 彈出標準字型對話框,確定後選取文字或後續輸入即時生效。
僅繼承 `CRichEditCtrl`、覆寫 `OnRButtonDown` 建構選單、映射 7 個命令、整合 `CFontDialog` 雙向同步 `CHARFORMAT2`/`LOGFONT`,即可賦予 RichEdit 專業文字處理器級的右鍵編輯與字體設定能力。`CMyRichEdit` 封裝完整、零依賴,適合直接納入專案共用控制項庫,為任何富文本編輯需求提供堅實基礎。