我创建了带有样式CBS_DROPDOWN
的组合框。此组合框包含多个具有名称的项目,例如:
如您所见,第二项和第三项具有相同的名称。它是任务所需要的。当用户打开组合的列表框并选择第三个项目时,会复制其名称以编辑组合框的一部分,并且我的班级会收到CBN_SELCHANGE
通知。我发送消息CB_GETCURSEL
并接收所选项目的索引等于“2”(基于零的计算)。在这个阶段,一切都很好。
但是,当用户第二次打开组合的列表框时,组合框显示为选定的第二项(索引为“1”)!我的代码没有收到有关项目选择更改的任何通知,那么为什么组合显示错误的选择?
如果我将组合框样式从CBS_DROPDOWN
更改为CBS_DROPDOWNLIST
,则可以正常使用。但我需要使用CBS_DROPDOWN
。
如何解决?
答案 0 :(得分:2)
当您打开列表框时,控件将选择以编辑控件中显示的文本开头的第一个列表框项。毕竟,文本可能已由用户输入。控件如何知道用户所指的两个列表框项目中的哪一个?
简单解决方案 - 为“项目B(1)”,“项目B(2)”等重复项添加后缀,使其独一无二。
如果无法做到这一点,您可以继承组合的列表框,并阻止它处理组合框的选择更改请求。
为此,请将以下代码放入组合框的CBN_DROPDOWN
通知处理程序中:
COMBOBOXINFO info{ sizeof(info) };
GetComboBoxInfo( hwndOfComboBox, &info );
SetWindowSubclass( info.hwndList, ComboLBoxProc, 0, 0 );
ComboLBoxProc
是一个回调函数,可能如下所示:
LRESULT CALLBACK ComboLBoxProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
switch( uMsg )
{
case LB_SETCURSEL:
return LB_ERR; // to prevent selection change
}
return DefSubclassProc( hWnd, uMsg, wParam, lParam );
}
使用上面的代码,用户仍然可以更改列表框中的选择,但不会再在列表框中选择在编辑控件中输入的文本。如果您想保留此功能,可以处理组合框的CBN_EDITCHANGE
通知,并设置一个您将在ComboLBoxProc
中检查的标记。在这种情况下,您将允许LB_SETCURSEL
的默认处理。在列表框(CBN_SELCHANGE
)中进行了选择后,重置此标志。
这有点像黑客,所以我可以选择为重复项添加后缀的“简单解决方案”。
答案 1 :(得分:1)
非常感谢 Zett42 。 他关于combo的listbox子类化的想法很棒。它有效。 这是我在ATL中实现他的方法。
template<typename TBase>
class CComboDDownBox
: public TBase
, protected ATL::CMessageMap
{
public:
CComboDDownBox()
: m_lb(_T(""), this, m_lbMapId)
, m_parent(_T(""), this, m_parentMapId)
, m_blockSelection(false)
{};
virtual ~CComboDDownBox() {};
public:
bool InitLB()
{
COMBOBOXINFO info = { sizeof(COMBOBOXINFO), 0 };
bool res = ::GetComboBoxInfo(TBase::operator HWND(), &info) != FALSE;
if (res)
{
res = (::GetWindowLong(TBase::operator HWND(), GWL_STYLE) & CBS_DROPDOWN) == CBS_DROPDOWN;
if (res)
{
res = m_lb.SubclassWindow(info.hwndList) != FALSE;
if (res)
{
res = m_parent.SubclassWindow(::GetParent(TBase::operator HWND())) != FALSE;
}
}
}
return res;
}
protected:
BEGIN_MSG_MAP(CComboDDownBox<TBase>)
ALT_MSG_MAP(m_lbMapId)
MESSAGE_HANDLER(LB_SETCURSEL, OnLbSetCurSel)
ALT_MSG_MAP(m_parentMapId)
COMMAND_CODE_HANDLER(CBN_DROPDOWN, OnCbDropDown)
COMMAND_CODE_HANDLER(CBN_CLOSEUP, OnCbCloseUpOrSelectionChanged)
COMMAND_CODE_HANDLER(CBN_SELCHANGE, OnCbCloseUpOrSelectionChanged)
END_MSG_MAP()
LRESULT OnLbSetCurSel(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
if (!m_blockSelection)
{
bHandled = FALSE;
}
return LB_ERR;
}
LRESULT OnCbDropDown(WORD /*wNotifyCode*/, WORD /*wID*/, HWND hWndCtl, BOOL& bHandled)
{
if (hWndCtl == TBase::operator HWND())
{
m_blockSelection = true;
}
bHandled = FALSE;
return 0;
}
LRESULT OnCbCloseUpOrSelectionChanged(WORD /*wNotifyCode*/, WORD /*wID*/, HWND hWndCtl, BOOL& bHandled)
{
if (hWndCtl == TBase::operator HWND())
{
m_blockSelection = false;
}
bHandled = FALSE;
return 0;
}
private:
static const DWORD m_lbMapId = 1;
static const DWORD m_parentMapId = 2;
ATL::CContainedWindow m_lb;
ATL::CContainedWindow m_parent;
bool m_blockSelection;
};
您可以在对话中使用此模板,例如在MFC:
中class CMyDialog
: public CDialog
{
public:
// .... Other methods ...
virtual BOOL OnInitDialog()
{
CDialog::OnInitDialog();
m_combo.InitLB();
return TRUE;
}
private:
CComboDDownBox<CComboBox> m_combo;
};