首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++Builder ComboBox 自動完成 (AutoComplete) 實作技巧

     編輯:關於C++

需求場景

使用者在 TComboBox (Style = csDropDown) 輸入文字時,自動從下拉清單中尋找符合前綴的項目並補全,游標停在補全部分並反白,方便接受或繼續輸入。VCL 內建 AutoComplete / AutoCompleteOptions (BCB 2009+) 功能有限,舊版本需自行實作。

核心實作程式碼


// Unit1.h
class TForm1 : public TForm
{
__published:
    TComboBox *ComboBox1;
    void __fastcall ComboBox1Change(TObject *Sender);
    void __fastcall ComboBox1KeyDown(TObject *Sender, WORD &Key, TShiftState Shift);
private:
    WORD FLastKey;  // 記錄上一個按鍵
public:
    __fastcall TForm1(TComponent* Owner);
};

// Unit1.cpp
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner), FLastKey(0) {}

void __fastcall TForm1::ComboBox1KeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
{
    FLastKey = Key; // 記錄按鍵,供 Change 事件判斷
}

//---------------------------------------------------------------------------
void __fastcall TForm1::ComboBox1Change(TObject *Sender)
{
    AnsiString Text = ComboBox1->Text;

    // 1. 若使用者按 Backspace 或 Delete,視為刪除操作,不自動補全
    if (FLastKey == VK_BACK || FLastKey == VK_DELETE) {
        FLastKey = 0;
        return;
    }
    FLastKey = 0;

    // 2. 僅在游標位於文字末尾時觸發 (避免中間插入字元時誤觸發)
    if (ComboBox1->SelStart != Text.Length())
        return;

    // 3. 從下拉清單尋找以目前文字為前綴的項目 (不區分大小寫)
    int Index = SendMessage(ComboBox1->Handle, CB_FINDSTRING, (WPARAM)-1, (LPARAM)Text.c_str());

    if (Index >= 0) {
        // 4. 找到:設定 ItemIndex 顯示完整項目
        ComboBox1->ItemIndex = Index;

        // 5. 設定編輯區選取範圍:從原輸入長度到結尾反白
        //    使用者可直接輸入覆蓋,或按 Enter/Tab 接受
        SendMessage(ComboBox1->Handle, CB_SETEDITSEL, 0,
                    MAKELPARAM(Text.Length(), -1)); // -1 表示到結尾
    }
    // 未找到則保持原輸入,不做任何事
}

關鍵 API 說明

訊息/函數用途
CB_FINDSTRING在 ComboBox 清單中搜尋以指定字串開頭的項目,傳回索引 (0-based),找不到傳回 CB_ERR (-1)。wParam = -1 從頭搜尋。
CB_SETEDITSEL設定編輯區選取範圍。lParam 低字 = 起始位置,高字 = 結束位置 (-1 表結尾)。
VK_BACK (0x08), VK_DELETE (0x2E)虛擬鍵碼,判斷刪除鍵避免誤觸發補全。

進階增強:支援鍵盤導航與大小寫敏感度選項


// 表單私有欄位
bool FCaseSensitive; // 預設 false

void __fastcall TForm1::ComboBox1Change(TObject *Sender)
{
    // ... 前略 ...

    // 3. 自訂比較函式 (支援大小寫敏感)
    int Index = -1;
    for (int i = 0; i < ComboBox1->Items->Count; ++i) {
        AnsiString Item = ComboBox1->Items->Strings[i];
        if (FCaseSensitive) {
            if (Item.SubString(1, Text.Length()) == Text) { Index = i; break; }
        } else {
            if (Item.SubString(1, Text.Length()).LowerCase() == Text.LowerCase()) { Index = i; break; }
        }
    }

    // ... 後略 ...
}

現代替代方案 (BCB 2009 / RAD Studio 10+)

VCL 內建 IAutoComplete2 介面支援更強大的自動完成 (檔案系統、歷史記錄、自訂來源):


#include <shlobj.hpp> // IAutoComplete, IAutoComplete2

void __fastcall TForm1::EnableShellAutoComplete(TComboBox* Combo)
{
    _di_IAutoComplete2 ac;
    HRESULT hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER,
                                  IID_IAutoComplete2, (void**)&ac);
    if (SUCCEEDED(hr)) {
        _di_IEnumString enumStr;
        // 可掛載檔案系統、URL 歷史、或自訂 IEnumString 實作
        hr = SHCreateStdEnumString(NULL, NULL, &enumStr); // 範例:檔案系統
        if (SUCCEEDED(hr)) {
            ac->Init(Combo->Handle, enumStr, NULL, NULL);
            ac->SetOptions(ACO_AUTOSUGGEST | ACO_AUTOAPPEND | ACO_SEARCH);
        }
    }
}

需連結 shell32.lib、ole32.lib,並在專案啟用 COM (CoInitialize)。

常見問題

  • 游標位置錯誤:確保 SelStart == Text.Length() 才觸發,否則中間插入字元會導致選取範圍錯亂。
  • 中文輸入法干擾:IME 組合字串期間 Change 會多次觸發,建議檢查 GetCompositionString 或簡單忽略 SelLength > 0 時的自動補全。
  • 大量項目效能:CB_FINDSTRING 為線性搜尋,項目超過數千時明顯延遲。改用 TStringList 排序後二分搜尋,或資料庫 LIKE 'prefix%' 查詢。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved