You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++Builder中实现支持全文搜索自动补全的ComboBox控件

C++Builder实现带全文搜索自动补全ComboBox的问题修复

以下是代码中存在的问题及修复方案:

存在的核心错误

  • 消息响应函数CNCommand声明与绑定错误:VCL自定义消息响应函数需要使用MESSAGE_HANDLER宏绑定到消息映射,且参数必须为引用传递,值传递会导致内存访问错误。
  • 循环逻辑越界:FilterItems函数中遍历FStoredItems的条件写为i < FStoredItems->Count - 1,会漏掉最后一个元素,极端情况会触发越界访问。
  • 逻辑运算符错误:KeyPress函数中判断按键范围的逻辑运算符&&误写为按位与&,导致条件判断完全失效。
  • 字符串列表赋值逻辑错误:SetStoredItems函数中直接将外部传入的TStringList指针赋值给内部成员FStoredItems,会导致内存泄漏、野指针问题。
  • CB_GETEDITSEL传参错误:该消息要求传入存储光标起始、结束位置的变量指针,直接传值无法获取正确的光标位置,后续操作会触发异常。
  • 缺少下拉列表自动弹出逻辑:过滤完成后没有主动弹出下拉列表展示匹配结果,功能不完整。

修复后代码

头文件代码

class PACKAGE TSmartComboBox : public TComboBox
{
private:
    TStringList* FStoredItems;
    bool dofilter;
    int storeditemindex;
    void FilterItems();
    void __fastcall StoredItemsChange(TObject* Sender);
    void __fastcall CNCommand(TWMCommand& AMessage); // 修改为引用传递
protected:
    BEGIN_MESSAGE_MAP
        MESSAGE_HANDLER(CN_COMMAND, TWMCommand, CNCommand) // 绑定消息映射
    END_MESSAGE_MAP(TComboBox)
    DYNAMIC void __fastcall KeyPress(System::WideChar &Key);
    DYNAMIC void __fastcall CloseUp(void);
    DYNAMIC void __fastcall Click(void);
public:
    __fastcall TSmartComboBox(TComponent* Owner);
    __fastcall ~TSmartComboBox();
    void InitSmartCombo();

__published:
    void __fastcall SetStoredItems(TStringList* Value);
    __property TStringList* StoredItems =
    {read = FStoredItems, write = SetStoredItems};
};

实现文件代码

static inline void ValidCtrCheck(TSmartComboBox *)
{
    new TSmartComboBox(NULL);
}
//---------------------------------------------------------------------------
__fastcall TSmartComboBox::TSmartComboBox(TComponent* Owner)
    : TComboBox(Owner)
{
    FStoredItems = new TStringList();
    dofilter = false;
    storeditemindex = -1;
}

__fastcall TSmartComboBox::~TSmartComboBox()
{
    delete FStoredItems;
}

void __fastcall TSmartComboBox::SetStoredItems(TStringList* Value)
{
    // 统一使用Assign,不直接接管外部指针
    FStoredItems->Assign(Value);
}

void __fastcall TSmartComboBox::KeyPress(System::WideChar &Key)
{
    // 修正逻辑运算符为&&
    if(dofilter && (Key >= 13 && Key <= 27))
    {
        if(Items->Count != 0 && !DroppedDown)
        {
            SendMessage(Handle, CB_SHOWDROPDOWN, 1, 0);
        }
    }
}

void __fastcall TSmartComboBox::CloseUp(void)
{
    AnsiString x;

    if(dofilter)
    {
        if(Items->Count == 1 && ItemIndex == 0)
        {
            Text = Items->Strings[ItemIndex];
        } else if((Text != "" && ItemIndex != -1 && Text != Items->Strings[ItemIndex]) || (Text == "" && ItemIndex == 0))
        {
            storeditemindex = ItemIndex;
            x = Text;
            ItemIndex = Items->IndexOf(Text);
            if(ItemIndex == -1)
            {
                Text = x;
            }
        } else
        {
            storeditemindex = -1;
        }
    }
}

void __fastcall TSmartComboBox::Click(void)
{
    if(dofilter)
    {
        if(storeditemindex != -1)
        {
            ItemIndex = storeditemindex;
        }
        storeditemindex = -1;
    }
}

void TSmartComboBox::InitSmartCombo()
{
    FStoredItems->OnChange = NULL;
    FStoredItems->Assign(Items);
    AutoComplete = false;
    FStoredItems->OnChange = StoredItemsChange;
    dofilter = true;
    storeditemindex = -1;
}

void __fastcall TSmartComboBox::StoredItemsChange(TObject* Sender)
{
    if(FStoredItems)
    {
        FilterItems();
    }
}

void TSmartComboBox::FilterItems()
{
    int i;
    DWORD startSel, endSel;

    // 修正CB_GETEDITSEL传参方式,传变量地址
    SendMessage(Handle, CB_GETEDITSEL, WPARAM(&startSel), LPARAM(&endSel));

    Items->BeginUpdate();

    if (Text != "")
    {
        Items->Clear();
        // 修正循环条件,去掉-1
        for (i = 0; i < FStoredItems->Count; i++)
        {
            if(Pos(UpperCase(Text), UpperCase(FStoredItems->Strings[i])) > 0)
            {
                Items->Add(FStoredItems->Strings[i]);
            }
        }
    } else
    {
        Items->Assign(FStoredItems);
    }

    Items->EndUpdate();

    // 恢复光标位置
    SendMessage(Handle, CB_SETEDITSEL, 0, MakeLParam(startSel, endSel));
    
    // 自动弹出下拉列表
    if(Items->Count > 0 && !DroppedDown)
    {
        SendMessage(Handle, CB_SHOWDROPDOWN, 1, 0);
    }
}

void __fastcall TSmartComboBox::CNCommand(TWMCommand& AMessage)
{
    if(AMessage.NotifyCode == CBN_EDITUPDATE && dofilter)
    {
        FilterItems();
    }
    // 调用基类默认处理
    TComboBox::Dispatch(&AMessage);
}

//---------------------------------------------------------------------------
namespace Comboboxsmart
{
    void __fastcall PACKAGE Register()
    {
         TComponentClass classes[1] = {__classid(TSmartComboBox)};
         RegisterComponents(L"polpress", classes, 0);
    }
}

使用方法

把控件添加到窗体后,先给ComboBox的Items添加全部选项,然后调用一次InitSmartCombo()即可启用搜索功能。

内容的提问来源于stack exchange,提问作者ppitu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 15:15:03