[c#] 如何在C#組合框或文本框中動態更改自動完成條目?


Answers

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

查看您在combobox類本身上可以找到的文檔。

Question

我在C#中有一個組合框,我想用它來使用自動完成建議,但我希望能夠在用戶輸入時更改自動完成條目,因為可能的有效條目太多而無法在啟動時填充AutoCompleteStringCollection

例如,假設我讓用戶輸入名稱。 我有一個可能的名字列表(“喬”,“約翰”)和一個姓氏列表(“博客”,“史密斯”),但如果我有一千個,那麼這將是一百萬個可能的字符串 - 太多不能放入自動完成條目。 所以最初我想要只有名字作為建議(“喬”,“約翰”),然後一旦用戶鍵入了第一個名字(“喬”),我想刪除現有的自動完成條目並替換他們使用一個新的集合,包括所選的名字,然後是可能的姓氏(“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()。
  • 在更新字符串時清除組合框中的文本,然後將其添加回來。
  • 在我更改字符串時將AutoCompleteMode設置為“None”,然後將其設置回“SuggestAppend”。
  • 掛鉤TextUpdateKeyPress事件而不是TextChanged
  • 每次使用新的AutoCompleteStringCollection替換現有的AutoCompleteCustomSource

即使在各種組合中,這些都沒有幫助。 Spence建議我嘗試重寫ComboBox函數,該函數獲取要在auto complete中使用的字符串列表。 使用反射器我在ComboBox類中發現了一些看起來很有前景的方法 - GetStringsForAutoComplete()SetAutoComplete() ,但它們都是私有的,因此我無法從派生類訪問它們。 我不能再接受了。

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

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

我認為在使用C#託管代碼時,應該無法獲得訪問衝突異常。 Avram建議我使用“lock”來解決這個問題,但我不知道應該鎖定什麼 - 唯一具有SyncRoot成員的是AutoCompleteStringCollection ,並且鎖定不會阻止訪問衝突異常。 我也試過鎖定ComboBoxTextBox ,但這也沒有幫助。 據我了解,鎖只能防止其他鎖,所以如果底層代碼沒有使用鎖,那麼我使用它將沒有任何區別。

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

更新:

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

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

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

我認為在使用C#託管代碼時應該不可能獲得訪問衝突異常,但無論如何,結果是我目前無法使用任何類型的動態自動完成的TextBoxComboBox 。 有沒有人對我如何實現這一點有任何見解?

更新2:

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




山姆,你弄清楚了嗎? 我遇到了同樣的情況。 Clear()似乎導致異常。 我刪除了清除的電話,雖然收藏仍在增長,但我得到了正確的建議事件......

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

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



這對我addRange ,你不要將addRange到同一個AutoCompleteStringCollection ,而是每次都創建一個新的。

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



這是我所知道的一個非常古老的問題,但它仍然存在於今天。 我的解決方法是將自動完成模式和源屬性設置為“無”,並手動更新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;
    }



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

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




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

訣竅不是在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);
        }
    }





Related