問題1:RichEditCtrl在用DDX進行數據交換的時候會發生數據丟失問題?為什麼!
當我們在拖了一個控件到程序裡後,通常的做發是Ctrl+W,用類向導給控件關聯一個變量,然後依靠DDX/DDV進行數據交換,如果我們用同樣的方法來給RICHEDIT關聯一個CString類型的變量就會存在一個問題,就是如果我們的數據大於了64K,數據就會丟失。
通過查MSDN發現,WM_GETTEXT消息並沒有設計在RICHEDIT的數據大於64K的時候怎樣處理。而類向導生成的代碼是用DDX_Text來交換控件和CString變量的數據。恰好,DDX_Text函數是調用GetWindowText函數,而這個函數又會發出WM_GETTEXT消息到控件來返回控件裡的數據。WM_GETTEXT消息不能接受超過64K的數據,因此導致了RICHEDIT在數據交換的時候發生了丟失。
為了解決這個問題,我們要用到DDX_RichText函數。添加下面兩個函數到工程
DWORD CALLBACK ES2MemCallBack(DWORD_PTR dwCookie,LPBYTE pbBuff, LONG cb, LONG *pcb)
{
LPTSTR& lpszStrFill = *(LPTSTR*)dwCookie;
memcpy(lpszStrFill, pbBuff, *pcb = cb);
lpszStrFill += cb;
*lpszStrFill = TCHAR('\0');
return 0;
}
void AFXAPI DDX_RichText(CDataExchange* pDX, int nIDC, CString& value)
{
extern void AFXAPI AfxSetWindowText(HWND hWndCtrl, LPCTSTR lpszNew);
HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
if (pDX->m_bSaveAndValidate)
{
int nLen = ::GetWindowTextLength(hWndCtrl);
LPTSTR lpszStrFill = value.GetBufferSetLength(nLen);
EDITSTREAM es = { (DWORD_PTR) &lpszStrFill, 0, ES2MemCallBack };
::SendMessage(hWndCtrl, EM_STREAMOUT, SF_TEXT, (LPARAM) &es);
value.ReleaseBuffer();
}
else
{
AfxSetWindowText(hWndCtrl, value);
}
}
之後我們還需要修改工程的.clw文件,用文本方式打開.clw文件。參考裡面類的格式加下面兩行代碼:
ExtraDDXCount=1
ExtraDDX1=7;;TextOver64KB;CString;;RichText;Retrieves text in excess of 64KB from RichEdit controls
如果沒有采用上面的步驟,我們就需要手動修改代碼,把所有的DDX_Text改為DDX_RichText。同時要把他們移到類向導控制代碼的外面。也就是移出:
//{{AFX_DATA_INIT(...)
//}}AFX_DATA_INIT
//{{AFX_DATA_MAP(...)
//}}AFX_DATA_MAP
reference:
Q280447 BUG: Text from a Rich Edit Control Is Truncated During Dialog Data Exchange (DDX)
問題2:當我們用類向導給richedit添加了EN_SETFOCUS, EN_KILLFOCUS的函數後卻不能響應,我發現這個響應函數根本就沒有被調用。即使是一個MessageBox()函數也不會調用。
原來是默認的消息映射添加錯誤了。
正確的消息影射和響應應該是:
ON_EN_SETFOCUS(IDC_RICHEDIT1,OnSetfocusRichedit1)
ON_EN_KILLFOCUS(IDC_RICHEDIT1,OnKillfocusRichedit1)
響應函數形式為:
afx_msgvoidOnSetfocusRichedit1();
afx_msgvoidOnKillfocusRichedit1();
但是如果我們用類向導來直接添加,生成的代碼卻是:
ON_NOTIFY(EN_SETFOCUS,IDC_RICHEDIT1,OnSetfocusRichedit1)
ON_NOTIFY(EN_KILLFOCUS,IDC_RICHEDIT1,OnKillfocusRichedit1)
我們需要自己手動改為上面的形式。
還有一個問題就是RichEditCtrl有時候不會出現在類向導的控件ID列表裡。這就需要我們自己添加DDX/DDV函數。自己動手啦!^_^
-- sampledlg.h --
class CSampleDlg : public CDialog
{
public:
CSampleDlg(CWnd* pParent = NULL);
// Dialog Data
//{{AFX_DATA(CSampleDlg)
enum { IDD = IDD_SAMPLE_DIALOG };
CString m_edit; // 類向導為EDIT空間生成的變量
//}}AFX_DATA
//俺們自己給RICHEDIT加個
CRichEditCtrl m_richEditCtrl;
.......
sampledlg.cpp --
......
void CSampleDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CSampleDlg)
DDX_Text(pDX, IDC_EDIT, m_edit);
DDV_MaxChars(pDX, m_edit, 10);
//}}AFX_DATA_MAP
// 再手動為RICHEDIT添加函數DDX_Control, DDX_Text and DDV_MaxChars
DDX_Control(pDX, IDC_RICHEDIT1, m_richEditCtrl);
DDX_Text(pDX, IDC_RICHEDIT1, m_richedit);
DDV_MaxChars(pDX, m_richedit, 10);
}