Window Control Wrappers


Child Window Management

You might have noticed in the last two samples that whenever I needed to manipulate a child controlfor example, when getting and setting the edit control's text, or enabling and disabling the OK buttonI used a dialog item function. The ultimate base class of CDialogImpl, CWindow, provides a number of helper functions to manipulate child controls:

class CWindow {                                                  public:                                                            ...                                                              // Dialog-Box Item Functions                                     BOOL CheckDlgButton(int nIDButton, UINT nCheck);                 BOOL CheckRadioButton(int nIDFirstButton, int nIDLastButton,                           int nIDCheckButton);                       int DlgDirList(LPTSTR lpPathSpec, int nIDListBox,                  int nIDStaticPath, UINT nFileType);                            int DlgDirListComboBox(LPTSTR lpPathSpec, int nIDComboBox,         int nIDStaticPath, UINT nFileType);                            BOOL DlgDirSelect(LPTSTR lpString, int nCount,                     int nIDListBox);                                               BOOL DlgDirSelectComboBox(LPTSTR lpString, int nCount,             int nIDComboBox);                                              UINT GetDlgItemInt(int nID, BOOL* lpTrans = NULL,                                   BOOL bSigned = TRUE) const;                   UINT GetDlgItemText(int nID, LPTSTR lpStr,                         int nMaxCount) const;                                          BOOL GetDlgItemText(int nID, BSTR& bstrText) const;              HWND GetNextDlgGroupItem(HWND hWndCtl,                             BOOL bPrevious = FALSE) const;                                 HWND GetNextDlgTabItem(HWND hWndCtl,                               BOOL bPrevious = FALSE) const;                                 UINT IsDlgButtonChecked(int nIDButton) const;                    LRESULT SendDlgItemMessage(int nID, UINT message,                  WPARAM wParam = 0, LPARAM lParam = 0);                         BOOL SetDlgItemInt(int nID, UINT nValue, BOOL bSigned = TRUE);   BOOL SetDlgItemText(int nID, LPCTSTR lpszString);              };                                                               


ATL adds no overhead to these functions, but because they're just inline wrappers of Windows functions, you can feel that something's not quite as efficient as it could be when you use one of these functions. Every time we pass in a child control ID, the window probably does a lookup to figure out the HWND and then calls the actual function on the window. For example, if I ran the zoo, SetDlgItemText would be implemented like this:

BOOL SetDlgItemText(HWND hwndParent, int nID,   LPCTSTR lpszString) {   HWND hwndChild = GetDlgItem(hwndParent, nID);   if( !hwndChild ) return FALSE;   return SetWindowText(hwndChild, lpszString); } 


That implementation is fine for family, but when your friends come over, it's time to pull out the good dishes. I'd prefer to cache the HWND and use SetWindowText. Plus, if I want to refer to a dialog and or a main window with an HWND, why would I want to refer to my child windows with UINT? Instead, I find it convenient to wrap windows created by the dialog manager with CWindow objects in OnInitDialog:

LRESULT CStringDlg::OnInitDialog(UINT, WPARAM, LPARAM, BOOL&) {   CenterWindow();   // Cache HWNDs   m_edit.Attach(GetDlgItem(IDC_STRING));   m_ok.Attach(GetDlgItem(IDOK));   // Copy the string from the data member   // to the child control (DDX)   m_edit.SetWindowText(m_sz);   // Check the string length (DDV)   CheckValidString();   return 1; // Let dialog manager set initial focus } 


Now, the functions that used any of the dialog item family of functions can use CWindow member functions instead:

void CStringDlg::CheckValidString() {   // Check the length of the string   int cchString = m_edit.GetWindowTextLength();   // Enable the OK button only if the string is   // of non-zero length   m_ok.EnableWindow(cchString ? TRUE : FALSE); } LRESULT CStringDlg::OnOK(WORD, UINT, HWND, BOOL&) {   // Copy the string from the child control to   // the data member (DDX)   m_edit.GetWindowText(m_sz, lengthof(m_sz));   EndDialog(IDOK);   return 0; } 


A Better Class of Wrappers

Now, my examples have been purposefully simple. A dialog box with a single edit control is not much work, no matter how you build it. However, let's mix things up a little. What if we were to build a dialog with a single list box control instead, as shown in Figure 10.6?

Figure 10.6. A dialog with a list box


This simple change makes the implementation more complicated. Instead of being able to use SetWindowText, as we could with an edit control, we must use special window messages to manipulate this list box control. For example, populating the list box and setting the initial selection involves the following code:

LRESULT CStringListDlg::OnInitDialog(UINT, WPARAM, LPARAM, BOOL&) {   CenterWindow();   // Cache list box HWND   m_lb.Attach(GetDlgItem(IDC_LIST));   // Populate the list box   m_lb.SendMessage(LB_ADDSTRING, 0,     (LPARAM)__T("Hello, ATL"));   m_lb.SendMessage(LB_ADDSTRING, 0,     (LPARAM)__T("Ain't ATL Cool?"));   m_lb.SendMessage(LB_ADDSTRING, 0,     (LPARAM)__T("ATL is your friend"));   // Set initial selection   int n = m_lb.SendMessage(LB_FINDSTRING, 0, (LPARAM)m_sz);   if( n == LB_ERR ) n = 0;   m_lb.SendMessage(LB_SETCURSEL, n);   return 1; // Let dialog manager set initial focus } 


Likewise, pulling out the final selection in OnOK is just as much fun:

LRESULT CStringListDlg::OnOK(WORD, UINT, HWND, BOOL&) {   // Copy the selected item   int n = m_lb.SendMessage(LB_GETCURSEL);   if( n == LB_ERR ) n = 0;   m_lb.SendMessage(LB_GETTEXT, n, (LPARAM)(LPCTSTR)m_sz);   EndDialog(IDOK);   return 0; } 


The problem is that although CWindow provides countless wrapper functions that are common to all windows, it does not provide any wrappers for the built-in Windows controls (for example, list boxes). And although MFC provides such wrapper classes as CListBox, ATL doesn't. However, unofficially, buried deep in the atlcon[9] sample lives an undocumented and unsupported set of classes that fit the bill nicely. The atlcontrols.h file defines the following window control wrappers inside the ATLControls namespace:

[9] As of this writing, this sample is available online at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcsample/html/_sample_atl_ATLCON.asp (http://tinysells.com/52).

CAnimateCtrl

CButton

CComboBox

CComboBoxEx

CDateTimePickerCtrl

CDragListBox

CEdit

CFlatScrollBar

CheaderCtrl

CHotKeyCtrl

CImageList

CIPAddressCtrl

ClistBox

CListViewCtrl

CMonthCalendarCtrl

CPagerCtrl

CProgressBarCtrl

CReBarCtrl

CRichEditCtrl

CScrollBar

CStatic

CStatusBarCtrl

CTabCtrl

CToolBarCtrl

CToolInfo

CToolTipCtrl

CTrackBarCtrl

CTreeItem

CTreeViewCtrl

CTreeViewCtrlEx

CupDownCtrl

For example, the CListBox class provides a set of inline wrapper functions, one per LB_XXX message. The following shows the ones that would be useful for the sample:

template <class Base>                                                     class CListBoxT : public Base {                                           public:                                                                     ...                                                                       // for single-selection listboxes                                         int GetCurSel() const                                                     { return (int)::SendMessage(m_hWnd, LB_GETCURSEL, 0, 0L); }               int SetCurSel(int nSelect)                                                {                                                                           return (int)::SendMessage(m_hWnd, LB_SETCURSEL, nSelect, 0L);           }                                                                         ...                                                                       // Operations                                                             // manipulating listbox items                                             int AddString(LPCTSTR lpszItem)                                           { return (int)::SendMessage(m_hWnd, LB_ADDSTRING, 0, (LPARAM)lpszItem); }   ...                                                                       // selection helpers                                                      int FindString(int nStartAfter, LPCTSTR lpszItem) const                   { return (int)::SendMessage(m_hWnd, LB_FINDSTRING, nStartAfter,                                       (LPARAM)lpszItem); }                          ...                                                                     };                                                                        typedef CListBoxT<CWindow> CListBox;                                      


Assuming a data member of type ATLControls::CListBox, the updated sample dialog code now looks much more pleasing:

LRESULT CStringListDlg::OnInitDialog(UINT, WPARAM, LPARAM, BOOL&) {     CenterWindow();     // Cache listbox HWND     m_lb.Attach(GetDlgItem(IDC_LIST));     // Populate the listbox     m_lb.AddString(__T("Hello, ATL"));     m_lb.AddString(__T("Ain't ATL Cool?"));     m_lb.AddString(__T("ATL is your friend"));     // Set initial selection     int n = m_lb.FindString(0, m_sz);     if( n == LB_ERR ) n = 0;     m_lb.SetCurSel(n);     return 1; // Let dialog manager set initial focus   }   LRESULT CStringListDlg::OnOK(WORD, UINT, HWND, BOOL&) {     // Copy the selected item     int n = m_lb.GetCurSel();     if( n == LB_ERR ) n = 0;     m_lb.GetText(n, m_sz);     EndDialog(IDOK);     return 0;   } 


Because the window control wrappers are merely a collection of inline functions that call SendMessage for you, the generated code is the same. The good news, of course, is that you don't have to pack the messages yourself.

The ATLCON sample itself hasn't been updated since 2001, but the atlcontrols.h header still works just fine. Unfortunately, a lot of new and updated common controls have been released since then, and, of course, the header doesn't reflect these updates. If you need updated control wrappers or want support for more sophisticated UI functions, check out the Windows Template Library (WTL).[10] WTL is a library that is written on top of and in the same style as ATL, but it provides much of the functionality that MFC does: message routing, idle time processing, convenient control wrappers, DDX/DDV support, and more.

[10] Available at http://wtl.sourceforge.net.




ATL Internals. Working with ATL 8
ATL Internals: Working with ATL 8 (2nd Edition)
ISBN: 0321159624
EAN: 2147483647
Year: 2004
Pages: 172

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net