Я был в состоянии сделать это, написав пользовательские обработчики DDX_. В приложении, в котором я работал, использовался пользовательский элемент управления (MCReal, полученный из CEdit), который будет принимать только десятичные значения между допустимым диапазоном, определенным в элементе управления. Когда пользователь вводит не десятичное значение или значение вне диапазона, код выдает собственное сообщение и возвращает значение, введенное в поле диалога.
Это было выполнено путем создания настраиваемого элемента управления и специального обработчика проверки. Вот что процедура DDX_ выглядела как:
void AFXAPI_EXPORT DDX_ProcessEditReal(CDataExchange* pDX, int nIDC, MCReal& mcr)
{
// prepare edit control
HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
// does control exist yet?
if (!IsWindow(mcr.m_hWnd))
{
// subclass the control
if (!mcr.SubclassWindow(hWndCtrl))
{
ASSERT(false);
// possibly trying to subclass twice?
AfxThrowNotSupportedException();
}
return;
}
if (!ValidateMCRealCtrl (mcr, pDX->m_pDlgWnd, (pDX->m_bSaveAndValidate == TRUE)))
{
pDX->Fail();
}
}
Я использовал стандартные процедуры DDX_ в качестве отправной точки, чтобы написать собственную версию. Настоящая работа выполнена в ValidateMCRealCtrl()
:
bool ValidateMCRealCtrl (MCReal &mcRealCtrl, CWnd *pParentWnd, bool bSaveAndValidate)
{
CString ctext;
double val = 0.0, r = 0.0;
double unit_factor = 0.0;
bool bDmsrg = false;
bool rc = false;
bool ret;
...
if (bSaveAndValidate) // Move from dialog to data
{
if (pParentWnd != nullptr && mcRealCtrl.CancelButtonClicked (pParentWnd))
{
return true;
}
if (!mcRealCtrl.IsWindowEnabled() || !mcRealCtrl.IsWindowVisible())
{
return true;; // don't update if not enabled
}
mcRealCtrl.GetWindowText (ctext);
...
// base field validation.
ret = mcRealCtrl.Validate();
if (!ret)
{
make_mcreal_str (r, ctext.GetBuffer (mcRealCtrl.maxlen), mcRealCtrl.maxlen, prec, mcRealCtrl.add_plus,
mcRealCtrl.m_strip_trailing == TRUE);
ctext.ReleaseBuffer();
InvalidRealField (mcRealCtrl); // Bad value
return false; // Reset Focus
}
...
ctext.ReleaseBuffer();
mcRealCtrl.SetWindowText (ctext);
}
else // Move from data to dialog
{
if (mcRealCtrl.angle) // If angle field...
{
val = mcRealCtrl.value * R2D; // Convert to degrees
}
else
{
val = mcRealCtrl.value; // Use data value
}
make_mcreal_str (val, ctext.GetBuffer (mcRealCtrl.maxlen), mcRealCtrl.maxlen, prec, mcRealCtrl.add_plus,
mcRealCtrl.m_strip_trailing == TRUE);
ctext.ReleaseBuffer();
mcRealCtrl.SetWindowText (ctext);
mcRealCtrl.SetLimitText (mcRealCtrl.maxlen);
}
...
return true;
}
(Примечание: я заменил код, который не относится к вашему вопросу с «...»)
Работа для возврата значения поля происходит в InvalidRealField()
. Этот код отображает всплывающее сообщение и использует предыдущее значение поля (сохраненное в текущем классе управления MCReal) до его изменения, чтобы вернуть значение.
Эта структура не была написана специально, чтобы вернуть неправильные значения полей диалога. Он обеспечивает гораздо больше, чем это, поскольку класс управления предоставляет некоторые дополнительные возможности. Однако наличие элемента управления, определенного в пользовательском классе, позволило мне выполнить выборочную проверку.
UpdateData возвращает FALSE, если он не работает, поэтому вы можете его исправить. Вы можете проверить значение элемента управления перед вызовом UpdateData. Сопоставьте элемент управления с CEdit. Вы также можете использовать PreTranslateMessage и подавлять нажатия клавиш, чтобы заставить пользователя вводить только определенные значения. –