簡體   English   中英

如何動態更改 C# combobox 或文本框中的自動完成條目?

[英]How can I dynamically change auto complete entries in a C# combobox or textbox?

我在 C# 中有一個 combobox 並且我想使用自動完成建議,但是我希望能夠將自動完成條目更改為用戶類型,因為可能的有效條目太多而無法在啟動時填充AutoCompleteStringCollection

例如,假設我讓用戶輸入名稱。 我有一個可能的名字列表(“Joe”、“John”)和一個姓氏列表(“Bloggs”、“Smith”),但如果我每個都有一千個,那么這將是一百萬個可能的字符串 -太多,無法放入自動完成條目。 因此,最初我只想將名字作為建議(“Joe”、“John”),然后一旦用戶輸入了名字(“Joe”),我想刪除現有的自動完成條目並替換他們有一個新的集合,包括選擇的名字和可能的姓氏(“Joe Bloggs”、“Joe Smith”)。 為了做到這一點,我嘗試了以下代碼:

void InitializeComboBox()
{
    ComboName.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
    ComboName.AutoCompleteSource = AutoCompleteSource.CustomSource;
    ComboName.AutoCompleteCustomSource = new AutoCompleteStringCollection();
    ComboName.TextChanged += new EventHandler( ComboName_TextChanged );
}

void ComboName_TextChanged( object sender, EventArgs e )
{
    string text = this.ComboName.Text;
    string[] suggestions = GetNameSuggestions( text );

    this.ComboQuery.AutoCompleteCustomSource.Clear();
    this.ComboQuery.AutoCompleteCustomSource.AddRange( suggestions );
}

但是,這不能正常工作。 似乎對 Clear() 的調用會導致自動完成機制“關閉”,直到下一個字符出現在組合框中,但是當然當下一個字符出現時,上面的代碼再次調用 Clear(),所以用戶永遠不會實際上看到了自動完成功能。 它還會導致組合框的全部內容被選中,因此在每次按鍵之間您必須取消選擇現有文本,這使其無法使用。 如果我刪除對 Clear() 的調用,則自動完成工作,但似乎AddRange()調用無效,因為我添加的新建議不會出現在自動完成下拉列表中。

我一直在尋找解決方案,並看到了各種建議,但我無法讓它們中的任何一個工作 - 自動完成功能似乎被禁用,或者沒有出現新字符串。 這是我嘗試過的事情的清單:

  • 在更改字符串之前調用BeginUpdate() ,之后調用EndUpdate()
  • 對所有現有字符串調用Remove()而不是 Clear()。
  • 在我更新字符串時清除 combobox 中的文本,然后將其添加回來。
  • 在我更改字符串時將AutoCompleteMode設置為“None”,然后將其設置回“SuggestAppend”。
  • 掛鈎TextUpdateKeyPress事件而不是TextChanged
  • 每次都用新的AutoCompleteStringCollection替換現有的AutoCompleteCustomSource

這些都沒有幫助,即使是各種組合。 Spence建議我嘗試覆蓋ComboBox function 以獲取要在自動完成中使用的字符串列表。 使用反射器,我在ComboBox class 中發現了一些看起來很有希望的方法 - GetStringsForAutoComplete()SetAutoComplete() ,但它們都是私有的,因此我無法從派生的 ZA2F2ED4F8EBC2CBBD4C21 訪問它們。 我不能再進一步了。

我嘗試用TextBox替換ComboBox ,因為自動完成界面是相同的,我發現行為略有不同。 使用TextBox ,它似乎工作得更好,因為自動完成的 Append 部分可以正常工作,但 Suggest 部分不能 - 建議框短暫閃爍,然后立即消失。

所以我想“好吧,我會在沒有 Suggest 功能的情況下生活,只使用 Append 代替”,但是當我將AutoCompleteMode設置為 Append 時,我得到了訪問沖突異常。 Suggest 也會發生同樣的事情 - 唯一不拋出異常的模式是SuggestAppend ,即使 Suggest 部分的行為不正確。

我認為使用 C# 托管代碼時應該不可能出現訪問沖突異常。 Avram建議我使用“鎖定”來解決此問題,但我不知道我應該鎖定什么 - 唯一具有 SyncRoot 成員的是AutoCompleteStringCollection ,並且鎖定不會阻止訪問沖突異常。 我還嘗試鎖定ComboBoxTextBox ,但這也無濟於事。 據我了解, lock 只會阻止其他鎖,所以如果底層代碼沒有使用 lock 那么我使用它不會有任何區別。

所有這一切的結果是我目前無法使用具有動態自動完成功能的TextBoxComboBox 有人對我如何實現這一目標有任何見解嗎?

更新:

我還沒有得到這個工作,但我發現了更多。 也許其中一些會激發其他人提出解決方案。

我嘗試用TextBox替換ComboBox ,因為自動完成界面是相同的,我發現行為略有不同。 使用TextBox ,它似乎工作得更好,因為自動完成的 Append 部分可以正常工作,但 Suggest 部分不能 - 建議框短暫閃爍,然后立即消失。

所以我想“好吧,我會在沒有 Suggest 功能的情況下生活,而只使用 Append”,但是當我將AutoCompleteMode設置為 Append 時,我得到了訪問沖突異常。 Suggest 也會發生同樣的事情 - 唯一不拋出異常的模式是SuggestAppend ,即使 Suggest 部分的行為不正確。

我認為在使用 C# 托管代碼時應該不可能出現訪問沖突異常,但無論如何,結果是我目前無法使用具有任何動態自動完成功能的TextBoxComboBox 有人對我如何實現這一目標有任何見解嗎?

更新 2:

在嘗試了各種其他事情(例如更改工作線程中的自動完成功能,並使用BeginInvoke()模擬 PostMessage() 類型的行為之后,我終於放棄了,只是使用列表框實現了我自己的自動完成下拉菜單。 它比內置的響應速度更快,而且我花在這方面的時間比我試圖讓內置的工作的時間少,所以任何想要這種行為的人的教訓是 - 你可能會更好自己實施。

我遇到了同樣的問題,並找到了一個非常簡單的解決方法。 和這里的其他人一樣,我找不到任何方法來控制組件的行為,所以我不得不接受它。

自然的行為是:您不能在用戶每次在文本框中鍵入內容時動態填充列表。 您必須填充一次,然后 AutoComplete 機制取得控制權。 結論是:您應該使用數據庫中的每個可能條目填充 AutoCompleteCustomSource 以使其按我們想要的方式工作。

當然,如果您有數百萬條記錄來填充列表,這是不可行的。 數據傳輸中的性能問題和自動完成機制本身不允許您這樣做。

我找到的折衷解決方案是:每次文本長度恰好達到 N 個字符(在我的情況下為 3 個)時,動態填充 AutoCompleteCustomSource。 這是有效的,因為復雜性大大降低。 從數據庫中提取的與這 3 個初始字符匹配的記錄數量足夠小,可以避免任何性能問題。

主要缺點是:在用戶鍵入第 N 個字符之前,他們不會看到自動完成列表。 但在輸入 3 個字符之前,用戶似乎並不真正期望一個有意義的自動完成列表。

希望這可以幫助。

這對我addRange ,您不會將范圍addRange到同一個AutoCompleteStringCollection ,而是每次都創建一個新的。

form.fileComboBox.TextChanged += (sender, e) => {
    var autoComplete = new AutoCompleteStringCollection();
    string[] items = CustomUtil.GetFileNames();
    autoComplete.AddRange(items);
    form.fileComboBox.AutoCompleteCustomSource = autoComplete;
};

我認為您可能想要退出反射器並查看覆蓋組合框本身中的自動完成行為。 我確信自動完成會調用一個訪問自動完成列表的函數。 如果你能找到這個函數並覆蓋它,你就可以使用任何你想要的行為。

查看您可以在組合框類本身上找到哪些文檔。

我沒有測試過這個,但它可能值得一試。

不是清除 AutoCompleteCustomSource,而是通過保留兩個實例來加倍緩沖。 當文本更改時,調用 GetNameSuggestions() 並為當前未使用的字符串構建字符串,然后將 ComboName.AutoCompleteCustomSource 設置為您剛剛設置的字符串。

我認為它應該看起來像這樣。

AutoCompleteCustomSource accs_a;
AutoCompleteCustomSource accs_b;
bool accs_check = true; //true for accs_a, false for accs_b
void InitializeComboBox()
{
    ComboName.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
    ComboName.AutoCompleteSource = AutoCompleteSource.CustomSource;

    accs_a = new AutoCompleteStringCollection();
    accs_b = new AutoCompleteStringCollection();

    ComboName.AutoCompleteCustomSource = accs_a;
    ComboName.TextChanged += new EventHandler( ComboName_TextChanged );
}

void ComboName_TextChanged( object sender, EventArgs e )
{
    string text = this.ComboName.Text;

    if(accs_check)
    {
       accs_b.Clear();
       accs_b.AddRange(GetNameSuggestions( text ));
       accs_check = false;
    }
    else
    {
       accs_a.Clear();
       accs_a.AddRange(GetNameSuggestions( text ));
       accs_check = true;
    }

    this.ComboQuery.AutoCompleteCustomSource = accs_check? accs_a : accs_b;
}

山姆,你弄明白了嗎? 我遇到了同樣的情況。 Clear() 似乎導致異常。 我刪除了清除呼叫,盡管收藏不斷增加,但我收到了正確的建議事件......

此外,關於私有成員:您可以使用反射訪問它們:

PropertyInfo[] props = [object].GetType().GetProperties({flags go here});
props[0].SetValue(this, new object[] { 0 });

最好的解決方案是使用組合框的事件處理程序。 通過使用textUpdate KeyDown DropDownChangeCommit ,您可以模仿自動完成模式,您可以自定義要搜索的內容以及顯示在下拉列表中的內容。

我發現這個答案很有用,但它是用 Visual C++ 編碼的,它是 toolstripcombobox 但概念是相同的 無論如何,.net 中的 c# 和 c++ 有很大的相似性,理解解決方案應該不成問題。

Visual C++中ToolStripCombobox的自定義自動搜索

我來這里最初是為了尋找解決方案,但現在找到了自己的解決方案。

訣竅不是在 AutoCompleteCustomSource 上調用 Clear() 而是刪除 for 循環中的所有項目,然后用新數據重建列表。 就我而言(圖書收藏應用程序),我從數據庫中檢索作者姓名,其中包含特定的起始字母,而不是全部。 請注意,這僅在組合框的文本框部分為空或已為空時才有效。

    private void cboAuthor_KeyDown(object sender, KeyEventArgs e)
    {
        if (cboAuthor.Text.Length == 0)
        {
            // Next two lines simple load data from the database in the
            // into a collection (var gateway), base on first letter in
            // the combobox. This is specific to my app.
            var gateway = new AuthorTableGateway();
            gateway.LoadByFirstLetter(Char.ConvertFromUtf32(e.KeyValue)[0]);

            // Clear current source without calling Clear()
            for (int i = 0; i < authorsAutoComplete.Count; i++)
                authorsAutoComplete.RemoveAt(0);

            // Rebuild with new data
            foreach (var author in gateway)
                authorsAutoComplete.Add(author.AuthorName);
        }
    }

我知道這是一個非常古老的問題,但它今天仍然存在。 我的解決方法是將自動完成模式和源屬性設置為“無”並手動更新 KeyUp 事件上的項目。

我確定它很笨拙,但無論輸入數據的速度如何,它對我來說都是完美的,並且在很長一段時間內都沒有問題,而且我的頭發開始重新長出額外的好處。

您還可以選擇是僅建議,還是建議並附加。 我希望它可以幫助某人。

private void comboBox1_KeyUp(object sender, KeyEventArgs e)
    {

        if (string.IsNullOrWhiteSpace(comboBox1.Text))
        {
            e.Handled = true;
            return;
        }
        if (comboBox1.Text.Length < 3)
        {
            e.Handled = true;
            return;
        }

        if (e.KeyCode == Keys.Down || e.KeyCode == Keys.Up)
        {
            e.Handled = true;
            return;
        }
        else if (e.KeyCode == Keys.Back)
        {
            e.Handled = true;
            return;
        }

        string text = comboBox1.Text;

        if (e.KeyCode == Keys.Enter)
        {
            comboBox1.DroppedDown = false;
            comboBox1.SelectionStart = text.Length;
            e.Handled = true;
            return;
        }

        List<string> LS = Suggestions(comboBox1.Text);

        comboBox1.Items.Clear();
        comboBox1.Items.AddRange(LS.ToArray());

        //If you do not want to Suggest and Append
        //comment the following line to only Suggest
        comboBox1.Focus();

        comboBox1.DroppedDown = true;
        comboBox1.SelectionStart = text.Length;

        //Prevent cursor from getting hidden
        Cursor.Current = Cursors.Default;
        e.Handled = true;
    }

還沒有嘗試過,但對於您的具體情況,您可以編寫如下代碼:

    private void txtAutoComplete_KeyUp(object sender, KeyEventArgs e)
    {

        String text = txtAutoComplete.Text;

        if (text.EndsWith(" "))
        {

            string[] suggestions = GetNameSuggestions( text ); //put [text + " "] at the begin of each array element
            txtAutoComplete.AutoCompleteCustomSource.Clear();
            txtAutoComplete.AutoCompleteCustomSource.AddRange( suggestions );

        }

    }

更新:把鎖放在這個地方的主要原因是

它的工作:) 在這個技巧消失之后,我曾經擁有的大部分“神秘異常”


  1. 像這段代碼中的鎖,可以幫助您解決異常
  2. 正如您之前提到的,使用文本框的問題較少
  3. 在這段代碼中,SuggestAppend 工作正常


    private void Form1_Load(object sender, EventArgs e)
    {
        textBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
        textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;

        textBox1.TextChanged+=new EventHandler(textBox1_TextChanged);

        col1.AddRange(new string[] { "avi avi", "avram avram" });
        col2.AddRange(new string[] { "boria boria", "boris boris" });

        textBox1.AutoCompleteCustomSource = col1;
        textBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
    }
    AutoCompleteStringCollection col1 = new AutoCompleteStringCollection();
    AutoCompleteStringCollection col2 = new AutoCompleteStringCollection();

    object locker = new object();
    private void textBox1_TextChanged(object sender, EventArgs e)
    {
        lock (locker)
        {
            if (textBox1.Text.StartsWith("a") && textBox1.AutoCompleteCustomSource != col1)
            {
                textBox1.AutoCompleteCustomSource = col1;
            }
            if (textBox1.Text.StartsWith("b") && textBox1.AutoCompleteCustomSource != col2)
            {
                textBox1.AutoCompleteCustomSource = col2;
            }
        }
    }

在嘗試了這里提供的所有解決方案(沒有成功)之后,我發現了一些對我有用的東西:

private void CellBox_TextChanged(object sender, EventArgs e)
{
    ((TextBox)sender).TextChanged -= CellBox_TextChanged;
    ((TextBox)dataGridView1.EditingControl).AutoCompleteMode = AutoCompleteMode.None;
    ((TextBox)dataGridView1.EditingControl).AutoCompleteCustomSource = null;                
    aCSC.Clear();
    foreach (string value in Autocompletevalues())
    {
        aCSC.Add(value);
    }
    ((TextBox)dataGridView1.EditingControl).AutoCompleteCustomSource = aCSC;
    ((TextBox)dataGridView1.EditingControl).AutoCompleteMode = AutoCompleteMode.Suggest;
    ((TextBox)sender).TextChanged += CellBox_TextChanged;
}

腳步:

  • 禁用事件處理程序
  • 禁用自動完成模式
  • 將源設置為空
  • 更新 AutoCompleteStringCollection (aCSC)
  • 將 Source 設置為更新的 AutoCompleteStringCollection
  • 激活自動完成模式
  • 啟用事件處理程序

我希望它可以幫助某人..

我測試了所有解決方案都沒有成功,並且還發生了崩潰(AccessViolation)並開始尋找可行的解決方案。 我找到了一種讓它動態工作的方法,並在我的博客中進行了解釋:

TextBox Winforms - 動態自動完成

您需要同時繼承 TextBox 和 AutoCompleteStringCollection 。

對我來說,秘密是使用 TextChanged 事件,而沒有使用 KeyDown/Up/Press 等。

更新:在動態更改 AutoCompleteCustomSource 遇到其他問題后,我最終放棄了使用內置自動完成功能,並在比我最初浪費在它上面的時間短得多的時間內實現了我自己的功能。 實現 ComboBox 控件的非托管代碼中似乎存在一些問題。 具體來說,我在 TextChanged 事件處理程序應該觸發時遇到了問題。 我決定在我的自定義實現中只使用 OnKeyDown/Press/Up 處理程序,這似乎更可靠。

if(!textBox3.AutoCompleteCustomSource.Contains(textBox3.Text))
   textBox3.AutoCompleteCustomSource.Add(textBox3.Text);

使用此代碼

private void dataGridView1_EditingControlShowing(object sender,DataGridViewEditingControlShowingEventArgs e)
    {

        if (e.Control is DataGridViewComboBoxEditingControl)
        {
            ((ComboBox)e.Control).DropDownStyle = ComboBoxStyle.DropDown;
            ((ComboBox)e.Control).AutoCompleteSource = AutoCompleteSource.ListItems;
            ((ComboBox)e.Control).AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;
        }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM