MFC Controls Overview
MFC provides C++ wrapper classes for every standard Windows common control — CButton, CEdit, CListBox, CComboBox, CListCtrl, CTreeCtrl, CProgressCtrl, CSliderCtrl, and more — each derived from CWnd, so a control gets the same Create()/DestroyWindow()/message-map machinery as a top-level window while also exposing control-specific member functions like CListCtrl::InsertColumn() or CComboBox::AddString() that wrap the underlying LVM_/CB_ window messages.
Cricket analogy: It's like a coaching manual giving every fielding position — slip, gully, mid-on — its own specialized drill set, even though every fielder still shares the same base fitness and catching fundamentals.
Binding Controls to Data with Control and Value Variables
Class Wizard historically generated two kinds of member variables for a dialog control: a 'control' variable of the wrapper type (e.g., CListBox m_lstNames) that gives direct access to the control's own API, and a 'value' variable (e.g., CString m_strSelected) hooked through DDX for simple get/set access; using the control variable directly is necessary whenever you need functionality DDX doesn't expose, such as CListCtrl::SetItemState() for multi-selection or CComboBox::SetItemData() for storing an associated pointer per entry.
Cricket analogy: It's like a captain sometimes reading a simple scoreboard summary for a quick glance, but pulling up the full ball-by-ball data feed when they need granular detail like exact field placements per delivery.
Common Controls in Practice
CListCtrl and CComboBox Examples
CListCtrl supports several visual styles via SetExtendedStyle() such as LVS_EX_FULLROWSELECT and LVS_EX_GRIDLINES, and populating a report-style list involves InsertColumn() for headers followed by InsertItem() and SetItemText() for rows, while CComboBox distinguishes CBS_DROPDOWN (editable text plus a list) from CBS_DROPDOWNLIST (selection-only, no free typing), a style choice that directly affects whether users can enter values not present in the list.
Cricket analogy: It's like choosing between a scoreboard that only shows preset totals versus one that lets the scorer manually key in a rare event like a penalty run not covered by the standard buttons.
// In DoDataExchange:
DDX_Control(pDX, IDC_LIST_NAMES, m_lstNames); // control variable
DDX_Control(pDX, IDC_COMBO_CITY, m_cboCity);
// OnInitDialog:
BOOL CMyDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
m_lstNames.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
m_lstNames.InsertColumn(0, _T("Name"), LVCFMT_LEFT, 150);
m_lstNames.InsertColumn(1, _T("Score"), LVCFMT_RIGHT, 80);
int idx = m_lstNames.InsertItem(0, _T("Ada Lovelace"));
m_lstNames.SetItemText(idx, 1, _T("98"));
m_cboCity.AddString(_T("Bengaluru"));
m_cboCity.AddString(_T("Chennai"));
m_cboCity.SetCurSel(0); // CBS_DROPDOWNLIST — user can only pick, not type
return TRUE;
}
Every common control wrapper still exposes SendMessage()/SendDlgItemMessage() escape hatches — if a wrapper class is missing a specific member function for a rarer control message, you can always send the raw LVM_/CB_/TCM_ message directly and get the same effect.
Calling control member functions like InsertColumn() before the control's HWND exists (e.g., in the constructor instead of OnInitDialog() or after DDX_Control has run) will fail silently or assert, because the CWnd wrapper has no attached window handle yet at that point.
- Every standard Windows common control has an MFC wrapper class derived from CWnd (CButton, CEdit, CListCtrl, CComboBox, etc.).
- Control wrapper classes expose typed member functions that internally send the appropriate window messages (LVM_, CB_, TCM_, etc.).
- DDX_Control binds a 'control' variable of the wrapper type for direct API access, distinct from a simple DDX 'value' variable.
- CListCtrl needs InsertColumn() for headers and InsertItem()/SetItemText() for rows in report view.
- CComboBox's CBS_DROPDOWN allows free typing; CBS_DROPDOWNLIST restricts selection to the existing list.
- Control member functions require a valid HWND, so they must run after DDX_Control/creation, typically in OnInitDialog().
- Raw SendMessage() calls remain available as an escape hatch for control messages the wrapper class doesn't expose.
Practice what you learned
1. What is the base class of MFC control wrapper classes like CListCtrl and CComboBox?
2. What does DDX_Control bind, as distinct from DDX_Text?
3. What must be called before InsertItem() to define a CListCtrl's report-view headers?
4. What is the key behavioral difference between CBS_DROPDOWN and CBS_DROPDOWNLIST combo box styles?
5. Why would calling m_lstNames.InsertColumn() inside a dialog class's constructor fail?
Was this page helpful?
You May Also Like
Dialog-Based Applications
How MFC's dialog-based application template uses DoModal and DDX/DDV to build a self-contained UI without a document/view frame.
Creating Windows with MFC
How MFC's CWnd/CFrameWnd hierarchy and CWinApp::InitInstance replace manual Win32 window registration and message-loop code.
Message Maps
How MFC's message-map macros build a static dispatch table that replaces a hand-written WndProc, plus how command routing and UI updates work on top of it.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics
Microsoft TechnologiesMVVM Design Pattern Study Notes
.NET · 30 topics