home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / dlgdata.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  19KB  |  670 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12. #include "occimpl.h"
  13.  
  14. #ifdef AFX_CORE3_SEG
  15. #pragma code_seg(AFX_CORE3_SEG)
  16. #endif
  17.  
  18. #ifdef _DEBUG
  19. #undef THIS_FILE
  20. static char THIS_FILE[] = __FILE__;
  21. #endif
  22.  
  23. /////////////////////////////////////////////////////////////////////////////
  24. // CDataExchange member functions (contructor is in wincore.cpp for swap tuning)
  25.  
  26. HWND CDataExchange::PrepareEditCtrl(int nIDC)
  27. {
  28.     HWND hWndCtrl = PrepareCtrl(nIDC);
  29.     ASSERT(hWndCtrl != NULL);
  30.     m_bEditLastControl = TRUE;
  31.     return hWndCtrl;
  32. }
  33.  
  34. HWND CDataExchange::PrepareCtrl(int nIDC)
  35. {
  36.     ASSERT(nIDC != 0);
  37.     ASSERT(nIDC != -1); // not allowed
  38.     HWND hWndCtrl;
  39.     m_pDlgWnd->GetDlgItem(nIDC, &hWndCtrl);
  40.     if (hWndCtrl == NULL)
  41.     {
  42.         TRACE1("Error: no data exchange control with ID 0x%04X.\n", nIDC);
  43.         ASSERT(FALSE);
  44.         AfxThrowNotSupportedException();
  45.     }
  46.     m_hWndLastControl = hWndCtrl;
  47.     m_bEditLastControl = FALSE; // not an edit item by default
  48.     ASSERT(hWndCtrl != NULL);   // never return NULL handle
  49.     return hWndCtrl;
  50. }
  51.  
  52. void CDataExchange::Fail()
  53. {
  54.     if (!m_bSaveAndValidate)
  55.     {
  56.         TRACE0("Warning: CDataExchange::Fail called when not validating.\n");
  57.         // throw the exception anyway
  58.     }
  59.     else if (m_hWndLastControl != NULL)
  60.     {
  61.         // restore focus and selection to offending field
  62.         ::SetFocus(m_hWndLastControl);
  63.         if (m_bEditLastControl) // select edit item
  64.             ::SendMessage(m_hWndLastControl, EM_SETSEL, 0, -1);
  65.     }
  66.     else
  67.     {
  68.         TRACE0("Error: fail validation with no control to restore focus to.\n");
  69.         // do nothing more
  70.     }
  71.  
  72.     AfxThrowUserException();
  73. }
  74.  
  75. /////////////////////////////////////////////////////////////////////////////
  76. // Notes for implementing dialog data exchange and validation procs:
  77. //  * always start with PrepareCtrl or PrepareEditCtrl
  78. //  * always start with 'pDX->m_bSaveAndValidate' check
  79. //  * pDX->Fail() will throw an exception - so be prepared
  80. //  * avoid creating temporary HWNDs for dialog controls - i.e.
  81. //      use HWNDs for child elements
  82. //  * validation procs should only act if 'm_bSaveAndValidate'
  83. //  * use the suffices:
  84. //      DDX_ = exchange proc
  85. //      DDV_ = validation proc
  86. //
  87. /////////////////////////////////////////////////////////////////////////////
  88.  
  89. // only supports '%d', '%u', '%sd', '%su', '%ld' and '%lu'
  90. AFX_STATIC BOOL AFXAPI _AfxSimpleScanf(LPCTSTR lpszText,
  91.     LPCTSTR lpszFormat, va_list pData)
  92. {
  93.     ASSERT(lpszText != NULL);
  94.     ASSERT(lpszFormat != NULL);
  95.  
  96.     ASSERT(*lpszFormat == '%');
  97.     lpszFormat++;        // skip '%'
  98.  
  99.     BOOL bLong = FALSE;
  100.     BOOL bShort = FALSE;
  101.     if (*lpszFormat == 'l')
  102.     {
  103.         bLong = TRUE;
  104.         lpszFormat++;
  105.     }
  106.     else if (*lpszFormat == 's')
  107.     {
  108.         bShort = TRUE;
  109.         lpszFormat++;
  110.     }
  111.  
  112.     ASSERT(*lpszFormat == 'd' || *lpszFormat == 'u');
  113.     ASSERT(lpszFormat[1] == '\0');
  114.  
  115.     while (*lpszText == ' ' || *lpszText == '\t')
  116.         lpszText++;
  117.     TCHAR chFirst = lpszText[0];
  118.     long l, l2;
  119.     if (*lpszFormat == 'd')
  120.     {
  121.         // signed
  122.         l = _tcstol(lpszText, (LPTSTR*)&lpszText, 10);
  123.         l2 = (int)l;
  124.     }
  125.     else
  126.     {
  127.         // unsigned
  128.         if (*lpszText == '-')
  129.             return FALSE;
  130.         l = (long)_tcstoul(lpszText, (LPTSTR*)&lpszText, 10);
  131.         l2 = (unsigned int)l;
  132.     }
  133.     if (l == 0 && chFirst != '0')
  134.         return FALSE;   // could not convert
  135.  
  136.     while (*lpszText == ' ' || *lpszText == '\t')
  137.         lpszText++;
  138.     if (*lpszText != '\0')
  139.         return FALSE;   // not terminated properly
  140.  
  141.     if (bShort)
  142.     {
  143.         if ((short)l != l)
  144.             return FALSE;   // too big for short
  145.         *va_arg(pData, short*) = (short)l;
  146.     }
  147.     else
  148.     {
  149.         ASSERT(sizeof(long) == sizeof(int));
  150.         ASSERT(l == l2);
  151.         *va_arg(pData, long*) = l;
  152.     }
  153.  
  154.     // all ok
  155.     return TRUE;
  156. }
  157.  
  158. AFX_STATIC void AFX_CDECL _Afx_DDX_TextWithFormat(CDataExchange* pDX, int nIDC,
  159.     LPCTSTR lpszFormat, UINT nIDPrompt, ...)
  160.     // only supports windows output formats - no floating point
  161. {
  162.     va_list pData;
  163.     va_start(pData, nIDPrompt);
  164.  
  165.     HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
  166.     TCHAR szT[32];
  167.     if (pDX->m_bSaveAndValidate)
  168.     {
  169.         // the following works for %d, %u, %ld, %lu
  170.         ::GetWindowText(hWndCtrl, szT, _countof(szT));
  171.         if (!_AfxSimpleScanf(szT, lpszFormat, pData))
  172.         {
  173.             AfxMessageBox(nIDPrompt);
  174.             pDX->Fail();        // throws exception
  175.         }
  176.     }
  177.     else
  178.     {
  179.         wvsprintf(szT, lpszFormat, pData);
  180.             // does not support floating point numbers - see dlgfloat.cpp
  181.         AfxSetWindowText(hWndCtrl, szT);
  182.     }
  183.  
  184.     va_end(pData);
  185. }
  186.  
  187. /////////////////////////////////////////////////////////////////////////////
  188. // Simple formatting to text item
  189.  
  190. void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, BYTE& value)
  191. {
  192.     int n = (int)value;
  193.     if (pDX->m_bSaveAndValidate)
  194.     {
  195.         _Afx_DDX_TextWithFormat(pDX, nIDC, _T("%u"), AFX_IDP_PARSE_BYTE, &n);
  196.         if (n > 255)
  197.         {
  198.             AfxMessageBox(AFX_IDP_PARSE_BYTE);
  199.             pDX->Fail();        // throws exception
  200.         }
  201.         value = (BYTE)n;
  202.     }
  203.     else
  204.         _Afx_DDX_TextWithFormat(pDX, nIDC, _T("%u"), AFX_IDP_PARSE_BYTE, n);
  205. }
  206.  
  207. void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, short& value)
  208. {
  209.     if (pDX->m_bSaveAndValidate)
  210.         _Afx_DDX_TextWithFormat(pDX, nIDC, _T("%sd"), AFX_IDP_PARSE_INT, &value);
  211.     else
  212.         _Afx_DDX_TextWithFormat(pDX, nIDC, _T("%hd"), AFX_IDP_PARSE_INT, value);
  213. }
  214.  
  215. void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, int& value)
  216. {
  217.     if (pDX->m_bSaveAndValidate)
  218.         _Afx_DDX_TextWithFormat(pDX, nIDC, _T("%d"), AFX_IDP_PARSE_INT, &value);
  219.     else
  220.         _Afx_DDX_TextWithFormat(pDX, nIDC, _T("%d"), AFX_IDP_PARSE_INT, value);
  221. }
  222.  
  223. void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, UINT& value)
  224. {
  225.     if (pDX->m_bSaveAndValidate)
  226.         _Afx_DDX_TextWithFormat(pDX, nIDC, _T("%u"), AFX_IDP_PARSE_UINT, &value);
  227.     else
  228.         _Afx_DDX_TextWithFormat(pDX, nIDC, _T("%u"), AFX_IDP_PARSE_UINT, value);
  229. }
  230.  
  231. void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, long& value)
  232. {
  233.     if (pDX->m_bSaveAndValidate)
  234.         _Afx_DDX_TextWithFormat(pDX, nIDC, _T("%ld"), AFX_IDP_PARSE_INT, &value);
  235.     else
  236.         _Afx_DDX_TextWithFormat(pDX, nIDC, _T("%ld"), AFX_IDP_PARSE_INT, value);
  237. }
  238.  
  239. void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, DWORD& value)
  240. {
  241.     if (pDX->m_bSaveAndValidate)
  242.         _Afx_DDX_TextWithFormat(pDX, nIDC, _T("%lu"), AFX_IDP_PARSE_UINT, &value);
  243.     else
  244.         _Afx_DDX_TextWithFormat(pDX, nIDC, _T("%lu"), AFX_IDP_PARSE_UINT, value);
  245. }
  246.  
  247. void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, CString& value)
  248. {
  249.     HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
  250.     if (pDX->m_bSaveAndValidate)
  251.     {
  252.         int nLen = ::GetWindowTextLength(hWndCtrl);
  253.         ::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1);
  254.         value.ReleaseBuffer();
  255.     }
  256.     else
  257.     {
  258.         AfxSetWindowText(hWndCtrl, value);
  259.     }
  260. }
  261.  
  262. void AFXAPI DDX_Text(CDataExchange* pDX, int nIDC, LPTSTR value, int nMaxLen)
  263. {
  264.     ASSERT(nMaxLen != 0);
  265.  
  266.     HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC);
  267.     if (pDX->m_bSaveAndValidate)
  268.     {
  269.         int nLen = ::GetWindowTextLength(hWndCtrl);
  270.         int nRetrieved = ::GetWindowText(hWndCtrl, value, nMaxLen);
  271.         if (nLen > nRetrieved)
  272.             TRACE1("Text in control ID %d is too long. Call DDV_MaxChars()!\n", nIDC);
  273.     }
  274.     else
  275.     {
  276.         AfxSetWindowText(hWndCtrl, value);
  277.     }
  278. }
  279.  
  280. /////////////////////////////////////////////////////////////////////////////
  281. // Data exchange for special control
  282.  
  283. void AFXAPI DDX_Check(CDataExchange* pDX, int nIDC, int& value)
  284. {
  285.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  286.     if (pDX->m_bSaveAndValidate)
  287.     {
  288.         value = (int)::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L);
  289.         ASSERT(value >= 0 && value <= 2);
  290.     }
  291.     else
  292.     {
  293.         if (value < 0 || value > 2)
  294.         {
  295.             TRACE1("Warning: dialog data checkbox value (%d) out of range.\n",
  296.                  value);
  297.             value = 0;  // default to off
  298.         }
  299.         ::SendMessage(hWndCtrl, BM_SETCHECK, (WPARAM)value, 0L);
  300.     }
  301. }
  302.  
  303. void AFXAPI DDX_Radio(CDataExchange* pDX, int nIDC, int& value)
  304.     // must be first in a group of auto radio buttons
  305. {
  306.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  307.  
  308.     ASSERT(::GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP);
  309.     ASSERT(::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON);
  310.  
  311.     if (pDX->m_bSaveAndValidate)
  312.         value = -1;     // value if none found
  313.  
  314.     // walk all children in group
  315.     int iButton = 0;
  316.     do
  317.     {
  318.         if (::SendMessage(hWndCtrl, WM_GETDLGCODE, 0, 0L) & DLGC_RADIOBUTTON)
  319.         {
  320.             // control in group is a radio button
  321.             if (pDX->m_bSaveAndValidate)
  322.             {
  323.                 if (::SendMessage(hWndCtrl, BM_GETCHECK, 0, 0L) != 0)
  324.                 {
  325.                     ASSERT(value == -1);    // only set once
  326.                     value = iButton;
  327.                 }
  328.             }
  329.             else
  330.             {
  331.                 // select button
  332.                 ::SendMessage(hWndCtrl, BM_SETCHECK, (iButton == value), 0L);
  333.             }
  334.             iButton++;
  335.         }
  336.         else
  337.         {
  338.             TRACE0("Warning: skipping non-radio button in group.\n");
  339.         }
  340.         hWndCtrl = ::GetWindow(hWndCtrl, GW_HWNDNEXT);
  341.  
  342.     } while (hWndCtrl != NULL &&
  343.         !(GetWindowLong(hWndCtrl, GWL_STYLE) & WS_GROUP));
  344. }
  345.  
  346. /////////////////////////////////////////////////////////////////////////////
  347. // Listboxes, comboboxes
  348.  
  349. void AFXAPI DDX_LBString(CDataExchange* pDX, int nIDC, CString& value)
  350. {
  351.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  352.     if (pDX->m_bSaveAndValidate)
  353.     {
  354.         int nIndex = (int)::SendMessage(hWndCtrl, LB_GETCURSEL, 0, 0L);
  355.         if (nIndex != -1)
  356.         {
  357.             int nLen = (int)::SendMessage(hWndCtrl, LB_GETTEXTLEN, nIndex, 0L);
  358.             ::SendMessage(hWndCtrl, LB_GETTEXT, nIndex,
  359.                     (LPARAM)(LPVOID)value.GetBufferSetLength(nLen));
  360.         }
  361.         else
  362.         {
  363.             // no selection
  364.             value.Empty();
  365.         }
  366.         value.ReleaseBuffer();
  367.     }
  368.     else
  369.     {
  370.         // set current selection based on data string
  371.         if (::SendMessage(hWndCtrl, LB_SELECTSTRING, (WPARAM)-1,
  372.           (LPARAM)(LPCTSTR)value) == LB_ERR)
  373.         {
  374.             // no selection match
  375.             TRACE0("Warning: no listbox item selected.\n");
  376.         }
  377.     }
  378. }
  379.  
  380. void AFXAPI DDX_LBStringExact(CDataExchange* pDX, int nIDC, CString& value)
  381. {
  382.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  383.     if (pDX->m_bSaveAndValidate)
  384.     {
  385.         DDX_LBString(pDX, nIDC, value);
  386.     }
  387.     else
  388.     {
  389.         // set current selection based on data string
  390.         int i = (int)::SendMessage(hWndCtrl, LB_FINDSTRINGEXACT, (WPARAM)-1,
  391.           (LPARAM)(LPCTSTR)value);
  392.         if (i < 0)
  393.         {
  394.             // no selection match
  395.             TRACE0("Warning: no listbox item selected.\n");
  396.         }
  397.         else
  398.         {
  399.             // select it
  400.             SendMessage(hWndCtrl, LB_SETCURSEL, i, 0L);
  401.         }
  402.     }
  403. }
  404.  
  405. void AFXAPI DDX_CBString(CDataExchange* pDX, int nIDC, CString& value)
  406. {
  407.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  408.     if (pDX->m_bSaveAndValidate)
  409.     {
  410.         // just get current edit item text (or drop list static)
  411.         int nLen = ::GetWindowTextLength(hWndCtrl);
  412.         if (nLen > 0)
  413.         {
  414.             // get known length
  415.             ::GetWindowText(hWndCtrl, value.GetBufferSetLength(nLen), nLen+1);
  416.         }
  417.         else
  418.         {
  419.             // for drop lists GetWindowTextLength does not work - assume
  420.             //  max of 255 characters
  421.             ::GetWindowText(hWndCtrl, value.GetBuffer(255), 255+1);
  422.         }
  423.         value.ReleaseBuffer();
  424.     }
  425.     else
  426.     {
  427.         // set current selection based on model string
  428.         if (::SendMessage(hWndCtrl, CB_SELECTSTRING, (WPARAM)-1,
  429.             (LPARAM)(LPCTSTR)value) == CB_ERR)
  430.         {
  431.             // just set the edit text (will be ignored if DROPDOWNLIST)
  432.             AfxSetWindowText(hWndCtrl, value);
  433.         }
  434.     }
  435. }
  436.  
  437. void AFXAPI DDX_CBStringExact(CDataExchange* pDX, int nIDC, CString& value)
  438. {
  439.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  440.     if (pDX->m_bSaveAndValidate)
  441.     {
  442.         DDX_CBString(pDX, nIDC, value);
  443.     }
  444.     else
  445.     {
  446.         // set current selection based on data string
  447.         int i = (int)::SendMessage(hWndCtrl, CB_FINDSTRINGEXACT, (WPARAM)-1,
  448.           (LPARAM)(LPCTSTR)value);
  449.         if (i < 0)
  450.         {
  451.             // just set the edit text (will be ignored if DROPDOWNLIST)
  452.             AfxSetWindowText(hWndCtrl, value);
  453.         }
  454.         else
  455.         {
  456.             // select it
  457.             SendMessage(hWndCtrl, CB_SETCURSEL, i, 0L);
  458.         }
  459.     }
  460. }
  461.  
  462. void AFXAPI DDX_LBIndex(CDataExchange* pDX, int nIDC, int& index)
  463. {
  464.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  465.     if (pDX->m_bSaveAndValidate)
  466.         index = (int)::SendMessage(hWndCtrl, LB_GETCURSEL, 0, 0L);
  467.     else
  468.         ::SendMessage(hWndCtrl, LB_SETCURSEL, (WPARAM)index, 0L);
  469. }
  470.  
  471. void AFXAPI DDX_CBIndex(CDataExchange* pDX, int nIDC, int& index)
  472. {
  473.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  474.     if (pDX->m_bSaveAndValidate)
  475.         index = (int)::SendMessage(hWndCtrl, CB_GETCURSEL, 0, 0L);
  476.     else
  477.  
  478.         ::SendMessage(hWndCtrl, CB_SETCURSEL, (WPARAM)index, 0L);
  479. }
  480.  
  481. void AFXAPI DDX_Scroll(CDataExchange* pDX, int nIDC, int& value)
  482. {
  483.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  484.     if (pDX->m_bSaveAndValidate)
  485.         value = GetScrollPos(hWndCtrl, SB_CTL);
  486.     else
  487.         SetScrollPos(hWndCtrl, SB_CTL, value, TRUE);
  488. }
  489.  
  490. void AFXAPI DDX_Slider(CDataExchange* pDX, int nIDC, int& value)
  491. {
  492.     HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  493.     if (pDX->m_bSaveAndValidate)
  494.         value = (int) ::SendMessage(hWndCtrl, TBM_GETPOS, 0, 0l);
  495.     else
  496.         ::SendMessage(hWndCtrl, TBM_SETPOS, TRUE, value);
  497. }
  498.  
  499. /////////////////////////////////////////////////////////////////////////////
  500. // Range Dialog Data Validation
  501.  
  502. AFX_STATIC void AFXAPI _AfxFailMinMaxWithFormat(CDataExchange* pDX,
  503.      long minVal, long maxVal, LPCTSTR lpszFormat, UINT nIDPrompt)
  504.     // error string must have '%1' and '%2' strings for min and max values
  505.     // wsprintf formatting uses long values (format should be '%ld' or '%lu')
  506. {
  507.     ASSERT(lpszFormat != NULL);
  508.  
  509.     if (!pDX->m_bSaveAndValidate)
  510.     {
  511.         TRACE0("Warning: initial dialog data is out of range.\n");
  512.         return;     // don't stop now
  513.     }
  514.     TCHAR szMin[32];
  515.     TCHAR szMax[32];
  516.     wsprintf(szMin, lpszFormat, minVal);
  517.     wsprintf(szMax, lpszFormat, maxVal);
  518.     CString prompt;
  519.     AfxFormatString2(prompt, nIDPrompt, szMin, szMax);
  520.     AfxMessageBox(prompt, MB_ICONEXCLAMATION, nIDPrompt);
  521.     prompt.Empty(); // exception prep
  522.     pDX->Fail();
  523. }
  524.  
  525. //NOTE: don't use overloaded function names to avoid type ambiguities
  526. void AFXAPI DDV_MinMaxByte(CDataExchange* pDX, BYTE value, BYTE minVal, BYTE maxVal)
  527. {
  528.     ASSERT(minVal <= maxVal);
  529.     if (value < minVal || value > maxVal)
  530.         _AfxFailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, _T("%u"),
  531.             AFX_IDP_PARSE_INT_RANGE);
  532. }
  533.  
  534. void AFXAPI DDV_MinMaxShort(CDataExchange* pDX, short value, short minVal, short maxVal)
  535. {
  536.     ASSERT(minVal <= maxVal);
  537.     if (value < minVal || value > maxVal)
  538.         _AfxFailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, _T("%ld"),
  539.             AFX_IDP_PARSE_INT_RANGE);
  540. }
  541.  
  542. void AFXAPI DDV_MinMaxInt(CDataExchange* pDX, int value, int minVal, int maxVal)
  543. {
  544.     ASSERT(minVal <= maxVal);
  545.     if (value < minVal || value > maxVal)
  546.         _AfxFailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, _T("%ld"),
  547.             AFX_IDP_PARSE_INT_RANGE);
  548. }
  549.  
  550. void AFXAPI DDV_MinMaxLong(CDataExchange* pDX, long value, long minVal, long maxVal)
  551. {
  552.     ASSERT(minVal <= maxVal);
  553.     if (value < minVal || value > maxVal)
  554.         _AfxFailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, _T("%ld"),
  555.             AFX_IDP_PARSE_INT_RANGE);
  556. }
  557.  
  558. void AFXAPI DDV_MinMaxUInt(CDataExchange* pDX, UINT value, UINT minVal, UINT maxVal)
  559. {
  560.     ASSERT(minVal <= maxVal);
  561.     if (value < minVal || value > maxVal)
  562.         _AfxFailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, _T("%lu"),
  563.             AFX_IDP_PARSE_INT_RANGE);
  564. }
  565.  
  566. void AFXAPI DDV_MinMaxDWord(CDataExchange* pDX, DWORD value, DWORD minVal, DWORD maxVal)
  567. {
  568.     ASSERT(minVal <= maxVal);
  569.     if (value < minVal || value > maxVal)
  570.         _AfxFailMinMaxWithFormat(pDX, (long)minVal, (long)maxVal, _T("%lu"),
  571.             AFX_IDP_PARSE_INT_RANGE);
  572. }
  573.  
  574. void AFXAPI DDV_MinMaxSlider(CDataExchange* pDX, DWORD value, DWORD minVal, DWORD maxVal)
  575. {
  576.     ASSERT(minVal <= maxVal);
  577.  
  578.     if (!pDX->m_bSaveAndValidate)
  579.     {
  580.         if (minVal > value || maxVal < value)
  581.         {
  582. #ifdef _DEBUG
  583.             int nIDC = GetWindowLong(pDX->m_hWndLastControl, GWL_ID);
  584.             TRACE1("Warning: initial dialog data is out of range in control ID %d.\n", nIDC);
  585. #endif
  586.             return;     // don't stop now
  587.         }
  588.     }
  589.  
  590.     ::SendMessage(pDX->m_hWndLastControl, TBM_SETRANGEMIN, FALSE, (LPARAM) minVal);
  591.     ::SendMessage(pDX->m_hWndLastControl, TBM_SETRANGEMIN, TRUE, (LPARAM) maxVal);
  592. }
  593.  
  594. /////////////////////////////////////////////////////////////////////////////
  595. // Max Chars Dialog Data Validation
  596.  
  597. void AFXAPI DDV_MaxChars(CDataExchange* pDX, CString const& value, int nChars)
  598. {
  599.     ASSERT(nChars >= 1);        // allow them something
  600.     if (pDX->m_bSaveAndValidate && value.GetLength() > nChars)
  601.     {
  602.         TCHAR szT[32];
  603.         wsprintf(szT, _T("%d"), nChars);
  604.         CString prompt;
  605.         AfxFormatString1(prompt, AFX_IDP_PARSE_STRING_SIZE, szT);
  606.         AfxMessageBox(prompt, MB_ICONEXCLAMATION, AFX_IDP_PARSE_STRING_SIZE);
  607.         prompt.Empty(); // exception prep
  608.         pDX->Fail();
  609.     }
  610.     else if (pDX->m_hWndLastControl != NULL && pDX->m_bEditLastControl)
  611.     {
  612.         // limit the control max-chars automatically
  613.         ::SendMessage(pDX->m_hWndLastControl, EM_LIMITTEXT, nChars, 0);
  614.     }
  615. }
  616.  
  617. /////////////////////////////////////////////////////////////////////////////
  618. // Special DDX_ proc for subclassing controls
  619.  
  620. void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl)
  621. {
  622.     if (rControl.m_hWnd == NULL)    // not subclassed yet
  623.     {
  624.         ASSERT(!pDX->m_bSaveAndValidate);
  625.  
  626.         HWND hWndCtrl = pDX->PrepareCtrl(nIDC);
  627.  
  628.         if (!rControl.SubclassWindow(hWndCtrl))
  629.         {
  630.             ASSERT(FALSE);      // possibly trying to subclass twice?
  631.             AfxThrowNotSupportedException();
  632.         }
  633. #ifndef _AFX_NO_OCC_SUPPORT
  634.         else
  635.         {
  636.             // If the control has reparented itself (e.g., invisible control),
  637.             // make sure that the CWnd gets properly wired to its control site.
  638.             if (pDX->m_pDlgWnd->m_hWnd != ::GetParent(rControl.m_hWnd))
  639.                 rControl.AttachControlSite(pDX->m_pDlgWnd);
  640.         }
  641. #endif //!_AFX_NO_OCC_SUPPORT
  642.  
  643.     }
  644. }
  645.  
  646. /////////////////////////////////////////////////////////////////////////////
  647. // Global failure dialog helpers (used by database classes)
  648.  
  649. void AFXAPI AfxFailMaxChars(CDataExchange* pDX, int nChars)
  650. {
  651.     TCHAR lpszTemp[32];
  652.     wsprintf(lpszTemp, _T("%d"), nChars);
  653.     CString prompt;
  654.     AfxFormatString1(prompt, AFX_IDP_PARSE_STRING_SIZE, lpszTemp);
  655.     AfxMessageBox(prompt, MB_ICONEXCLAMATION, AFX_IDP_PARSE_STRING_SIZE);
  656.     prompt.Empty(); // exception prep
  657.     pDX->Fail();
  658. }
  659.  
  660. void AFXAPI AfxFailRadio(CDataExchange* pDX)
  661. {
  662.     CString prompt;
  663.     AfxFormatStrings(prompt, AFX_IDP_PARSE_RADIO_BUTTON, NULL, 0);
  664.     AfxMessageBox(prompt, MB_ICONEXCLAMATION, AFX_IDP_PARSE_RADIO_BUTTON);
  665.     prompt.Empty(); // exception prep
  666.     pDX->Fail();
  667. }
  668.  
  669. /////////////////////////////////////////////////////////////////////////////
  670.