C++ 组合框中包含多个相同项的选择无效
我使用样式C++ 组合框中包含多个相同项的选择无效,c++,winapi,combobox,C++,Winapi,Combobox,我使用样式CBS\u下拉列表创建了组合框。此组合框包含多个具有名称的项目,例如: 项目a B项 B项 项目C 如您所见,第二项和第三项具有相同的名称。它是任务所要求的。当用户打开组合框的列表框并选择第三项时,其名称将被复制到组合框的编辑部分,并且我的类将收到CBN\u SELCHANGE通知。我发送消息CB_GETCURSEL,并接收到所选项目的索引等于“2”(基于零的计数)。在这个阶段,一切都很好 但是,当用户第二次打开组合框的列表框时,组合框显示为选中的第二项(索引为“1”)!我的代码没
CBS\u下拉列表创建了组合框。此组合框包含多个具有名称的项目,例如:
- 项目a
- B项
- B项
- 项目C
如您所见,第二项和第三项具有相同的名称。它是任务所要求的。当用户打开组合框的列表框并选择第三项时,其名称将被复制到组合框的编辑部分,并且我的类将收到CBN\u SELCHANGE
通知。我发送消息CB_GETCURSEL
,并接收到所选项目的索引等于“2”(基于零的计数)。在这个阶段,一切都很好
但是,当用户第二次打开组合框的列表框时,组合框显示为选中的第二项(索引为“1”)!我的代码没有收到任何关于项目选择更改的通知,所以为什么combo显示错误的选择
如果我将组合框样式从CBS\u DROPDOWN
更改为CBS\u DROPDOWNLIST
,它将正常工作。但我需要使用CBS_下拉列表
如何修复它?打开列表框时,控件会选择第一个列表框项目,该项目以编辑控件中显示的文本开头。毕竟,文本可能是由用户输入的。控件如何知道用户所指的两个列表框项中的哪一个 简单解决方案-为重复项添加后缀,如“项B(1)”、“项B(2)”等,使其唯一 如果这是不可能的,您可以对组合的列表框进行子类化,并阻止它处理组合框的选择更改请求 为此,请将以下代码放入组合框的
CBN\u下拉列表
通知处理程序中:
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\u EDITCHANGE
通知,并设置要签入ComboLBoxProc
的标志。在这种情况下,您将允许默认处理LB_SETCURSEL
。在列表框(CBN\u SELCHANGE
)中进行选择后,重置此标志
这是一个小技巧,所以我可能会采用“简单的解决方案”,在副本中添加后缀。非常感谢Zett42。 他关于combo列表框子类化的想法非常好。它是有效的。 这是我在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;
};
模板
类CComboDDownBox
:公共数据库
,受保护的ATL::CMessageMap
{
公众:
CComboDDownBox()
:m_lb(_T(“”),this,m_lbMapId)
,m_parent(_T(“”),this,m_parentMapId)
,m_块选择(假)
{};
虚拟~CComboDDownBox(){};
公众:
bool InitLB()
{
COMBOBOXINFO={sizeof(COMBOBOXINFO),0};
bool res=::GetComboBoxInfo(TBase::operator HWND(),&info)!=FALSE;
如果(res)
{
res=(::GetWindowLong(TBase::operator HWND(),GWL_样式)和CBS_下拉菜单)=CBS_下拉菜单;
如果(res)
{
res=m_lb.subclass窗口(info.hwnlist)!=FALSE;
如果(res)
{
res=m_parent.subclass窗口(::GetParent(TBase::operator HWND())!=FALSE;
}
}
}
返回res;
}
受保护的:
开始消息映射(CComboDDownBox)
ALT\U MSG\U映射(m\U lbMapId)
消息处理程序(LB_SETCURSEL、OnLbSetCurSel)
ALT_MSG_映射(m_parentMapId)
命令\代码\处理程序(CBN\ U下拉列表、OnCbDropDown)
命令\u代码\u处理程序(CBN\u特写、OnCBCloseUp或SelectionChanged)
命令\u代码\u处理程序(CBN\u SELCHANGE、ONCBCLOSEUP或SELECTIONCHANGED)
END_MSG_MAP()
LRESULT OnLbSetCurSel(UINT/*uMsg*/,WPARAM/*WPARAM*/,LPARAM/*LPARAM*/,BOOL&bHandled)
{
如果(!m_块选择)
{
bHandled=FALSE;
}
返回LB_ERR;
}
LRESULT OnCbDropDown(WORD/*wNotifyCode*/、WORD/*wID*/、HWND hWndCtl、BOOL和bHandled)
{
if(hWndCtl==TBase::operator HWND())
{
m_blockSelection=true;
}
bHandled=FALSE;
返回0;
}
LRESULT oncbcloseUporSelection已更改(WORD/*wNotifyCode*/、WORD/*wID*/、HWND hWndCtl、BOOL&bHandled)
{
if(hWndCtl==TBase::operator HWND())
{
m_blockSelection=false;
}
bHandled=FALSE;
返回0;
}
私人:
静态常数DWORD m_lbMapId=1;
静态常量DWORD m_parentMapId=2;
ATL::CContainedWindow m_lb;
ATL::CContainedWindow m_父母;
布尔m_区块选择;
};
您可以在对话中使用此模板,例如在MFC中:
class CMyDialog
: public CDialog
{
public:
// .... Other methods ...
virtual BOOL OnInitDialog()
{
CDialog::OnInitDialog();
m_combo.InitLB();
return TRUE;
}
private:
CComboDDownBox<CComboBox> m_combo;
};
类CMyDialog
:公共对话
{
公众:
//……其他方法。。。
虚拟BOOL-OnInitDialog()
{
CDialog::OnInitDialog();
m_combo.InitLB();
返回TRUE;
}
私人:
CComboDDownBox m_组合;
};
该组合是否有可能被发送到编辑框中当前值的CB_SELECTSTRING消息?这将导致列表选择更改为第一个可用的匹配项。设计思路:根据上面提供的信息,如果两个“条目B”条目确实相同,则用户选择哪一个并不重要。另一方面,如果它们在某些方面确实不同,则用户无法知道哪个是哪个,因此无法确保正确选择。不,我的代码不能做到这一点。当您打开列表框时,控件将选择以编辑控件中显示的文本开头的第一个列表框项。毕竟,文本可能是由用户输入的。控件如何知道两个列表框项中的哪一个是m