The combo box control sends the CBN_SELENDOK
notification (in WM_COMMAND
) to the parent window when the selection has finally changed. You can handle this event both in your CExtComboBox
-derived class or in its parent window. We recommend you to handle this event in your combo box class. Handling messages sent to parent windows in source windows is called reflection. It is handy to create a CExtComboBox
-derived class which reflects selection change and get access to all other combo boxes of the same type in your application. When this combo box catches the selection change, it can change selection in all other combo boxes.
Here it is the class declaration:
class CComboBoxForMrDouglas : public CExtComboBox
{
static CTypedPtrList < CPtrList, CComboBoxForMrDouglas * >
g_listAllComboBoxesForMrDouglas;
static bool g_bSynchronizingCurSel;
public:
DECLARE_DYNAMIC( CComboBoxForMrDouglas );
CComboBoxForMrDouglas();
virtual ~CComboBoxForMrDouglas();
void SynchronizeCurSel();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump( CDumpContext & dc ) const;
#endif
public:
// Overrides
//{{AFX_VIRTUAL(CComboBoxForMrDouglas)
virtual void PostNcDestroy();
protected:
virtual void PreSubclassWindow();
//}}AFX_VIRTUAL
public:
//{{AFX_MSG(CComboBoxForMrDouglas)
//}}AFX_MSG
afx_msg void OnSelEndOK();
DECLARE_MESSAGE_MAP()
};
Here it is the class implementation:
IMPLEMENT_DYNAMIC( CComboBoxForMrDouglas, CExtComboBox );
CTypedPtrList < CPtrList, CComboBoxForMrDouglas * >
CComboBoxForMrDouglas::g_listAllComboBoxesForMrDouglas;
bool CComboBoxForMrDouglas::g_bSynchronizingCurSel = false;
CComboBoxForMrDouglas::CComboBoxForMrDouglas()
{
}
CComboBoxForMrDouglas::~CComboBoxForMrDouglas()
{
}
#ifdef _DEBUG
void CComboBoxForMrDouglas::AssertValid() const
{
CExtComboBox::AssertValid();
}
void CComboBoxForMrDouglas::Dump( CDumpContext & dc ) const
{
CExtComboBox::Dump( dc );
}
#endif
BEGIN_MESSAGE_MAP( CComboBoxForMrDouglas, CExtComboBox )
//{{AFX_MSG_MAP(CComboBoxForMrDouglas)
//}}AFX_MSG_MAP
ON_CONTROL_REFLECT( CBN_SELENDOK, OnSelEndOK )
END_MESSAGE_MAP()
void CComboBoxForMrDouglas::PostNcDestroy()
{
ASSERT_VALID( this );
CExtComboBox::PostNcDestroy();
g_listAllComboBoxesForMrDouglas.AddTail( this );
}
void CComboBoxForMrDouglas::PreSubclassWindow()
{
ASSERT_VALID( this );
POSITION pos = g_listAllComboBoxesForMrDouglas.Find( this );
ASSERT( pos != NULL );
g_listAllComboBoxesForMrDouglas.RemoveAt( pos );
CExtComboBox::PreSubclassWindow();
}
void CComboBoxForMrDouglas::OnSelEndOK()
{
ASSERT_VALID( this );
Default();
SynchronizeCurSel();
}
void CComboBoxForMrDouglas::SynchronizeCurSel()
{
ASSERT_VALID( this );
if( g_bSynchronizingCurSel )
return;
g_bSynchronizingCurSel = true;
int nCurSel = GetCurSel();
POSITION pos =
g_listAllComboBoxesForMrDouglas.GetHeadPosition();
for( ; pos != NULL; )
{
CComboBoxForMrDouglas * pComboBoxForMrDouglas =
g_listAllComboBoxesForMrDouglas.GetNext( pos );
ASSERT_VALID( pComboBoxForMrDouglas );
if( pComboBoxForMrDouglas == this )
continue;
pComboBoxForMrDouglas->SetCurSel( nCurSel );
}
g_bSynchronizingCurSel = false;
}
You can programmatically change selection of any combo box of this type and then invoke its
SynchronizeCurSel()
method to make other combo boxes having the same selection. This method is also called from the
OnSelEndOK()
message reflection handler. You can also append your specific code to the
OnSelEndOK()
method. Please note that we keep only pointers to the combo box windows which have the
m_hWnd
property as a property of a valid window because we add these pointers to a list in the
PreSubclassWindow()
method and remove them in the
PostNcDestroy()
method. So, each combo box window should know all the created combo boxes of the same type. This makes possible synchronization of the currently selected item. Of course, this approach assumes all combo boxes have equal content.