Catching ON_CBN_SELCHANGE and other dropdown events when extending CFileDialog
You need to provide a hook procedure to the dialog to capture the messages. Details here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms646931%28v=vs.85%29.aspx
Lou
Updated on June 04, 2022Comments
-
Lou almost 2 years
I am trying to create a custom file save dialog for XP using C++/VS2008. My dialog will have three dropdowns and an edit box. I need the contents of the dropdowns to dynamically update when selections are made on other dropdowns. Hence, my need to catch the CBN_ events.
I've created a class that extends CFileDialog and a class that extends CComboBox. I can get the custom dialog (with the combos) to display but I cannot trap the CBN_ events.
I used the VS 'Create MFC DLL' wizard to get two classes: The app class and the custom file save dialog class. I created a derived CComboBox class so I could add messages/event trapping code. I put in the message maps, DoDataExchange and event trapping code in the dialog class.
The code that follows is an abridged version of what I'm working with. I figure I don't need to post the creation of three dropdowns when I can't get even one dropdown to work. I also didn't put in any code for the edit box, since I'm not having issues with that.
Some code: From the app class - creating an instance of the custom dialog, calling DoModal:
BOOL CSaveAsApp::InitInstance() { CWinApp::InitInstance(); // Parse the command line to get the defaultExtension and the file filter CString cmdLineAsString = CString( theApp.m_lpCmdLine ); int curPos = 0 ; CString fileExtension = cmdLineAsString.Tokenize(" ", curPos ) ; CString fileFilter = cmdLineAsString.Tokenize(" ", curPos ) ; ; MyFileDialog dlg( FALSE, fileExtension.GetBuffer(), "Enter File Name Here", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, fileFilter.GetBuffer(), m_pMainWnd) ; m_pMainWnd = &dlg; int nResponse = dlg.DoModal(); if (nResponse == IDOK) { ... } else if (nResponse == IDCANCEL) ... return FALSE; }
Here is some code from the custom file save dialog:
IMPLEMENT_DYNAMIC(MyFileDialog, CFileDialog ) // LJM Put in last two parms: DWORD dwSize, BOOL bVistaStyle (0, 0 ) // bVistaStyle = 0 ==> XP-Style dialog MyFileDialog::MyFileDialog(BOOL bOpenFileDialog, LPCTSTR lpszDefExt, LPCTSTR lpszFileName, DWORD dwFlags, LPCTSTR lpszFilter, CWnd* pParentWnd) : CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName, dwFlags, lpszFilter, pParentWnd, 0, 0) { } MyFileDialog::~MyFileDialog() { } void MyFileDialog::DoDataExchange(CDataExchange* pDX) { CFileDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(MyFileDialog) //DDX_Control(pDX, IDC_CONTEXT_COMBO, m_ComboContext); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(MyFileDialog, CFileDialog) ON_CBN_SELCHANGE(IDC_CONTEXT_COMBO, &MyFileDialog::OnCbnSelchangeUniqueNumber) ON_CBN_SELENDOK(IDC_CONTEXT_COMBO, &MyFileDialog::OnCbnSelchangeUniqueNumber) END_MESSAGE_MAP() BOOL MyFileDialog::OnInitDialog() { CFileDialog::OnInitDialog(); //AfxEnableControlContainer(); const UINT iExtraSize = 300; // Get a pointer to the original dialog box. CWnd *wndDlg = GetParent(); RECT Rect; wndDlg->GetWindowRect(&Rect); // Change the size of FileOpen dialog wndDlg->SetWindowPos(NULL, 0, 0, Rect.right - Rect.left, Rect.bottom - Rect.top + iExtraSize, SWP_NOMOVE); CWnd *wndComboCtrl = wndDlg->GetDlgItem(cmb1); wndComboCtrl->GetWindowRect(&Rect); wndDlg->ScreenToClient(&Rect); // Remember it is child controls Rect.top += 60; Rect.bottom += 120; Rect.left += 50; m_ComboContext.Create(WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP, Rect, wndDlg, IDC_CONTEXT_COMBO); m_ComboContext.SetFont(wndComboCtrl->GetFont(), TRUE); m_ComboContext.AddString(_T("Lou1") ); m_ComboContext.AddString(_T("L432") ); m_ComboContext.AddString(_T("Lou2") ); m_ComboContext.AddString(_T("Lou3") ); return true ; } void MyFileDialog::OnCbnSelchangeUniqueNumber() { AfxMessageBox( _T("OnCbnSelchangeUniqueNumber"), 0, 0 ) ; }
And here's the derived class from CComboBox:
IMPLEMENT_DYNAMIC(LouComboBox, CComboBox) LouComboBox::LouComboBox() { } LouComboBox::~LouComboBox() { } BEGIN_MESSAGE_MAP(LouComboBox, CComboBox) ON_CONTROL_REFLECT(CBN_SELCHANGE, &LouComboBox::OnCbnSelchange) ON_CONTROL_REFLECT(CBN_KILLFOCUS, &LouComboBox::OnCbnKillfocus) ON_CONTROL_REFLECT(CBN_SELENDCANCEL, &LouComboBox::OnCbnSelendcancel) ON_CBN_SELCHANGE(IDC_CONTEXT_COMBO, &LouComboBox::OnCbnSelchangeUniqueNumber) END_MESSAGE_MAP() void LouComboBox::OnCbnSelchange() { // TODO: Add your control notification handler code here AfxMessageBox( _T("Menu selected"), 0, 0 ) ; } void LouComboBox::OnCbnKillfocus() { // TODO: Add your control notification handler code here AfxMessageBox( _T("Lost focus"), 0, 0 ) ; } void LouComboBox::OnCbnSelendcancel() { AfxMessageBox( _T("OnCbnSelendcancel"), 0, 0 ) ; } void LouComboBox::OnCbnSelchangeUniqueNumber() { // TODO: Add your control notification handler code here AfxMessageBox( _T("OnCbnSelchangeUniqueNumber"), 0, 0 ) ; }
I have tried versions where the dialog class has the messagemap/DoDataExchange, the ComboBox class has them, both classes have them..
I am able to catch some events - none with the CComboBox, though. I can get the dialog to respond to ON_NOTIFY, ON_WM_DESTROY and the edit box responds to ON_WM_KILLFOCUS.
I have this nagging thought that this is a forehead-slapper, that I'm too close to this and can't see the obvious.
ANy help, assistance, direction would be greatly appreciated.