如何动态更改C#组合框或文本框中的自动完成条目?

如何动态更改C#组合框或文本框中的自动完成条目?,c#,winforms,autocomplete,combobox,textbox,C#,Winforms,Autocomplete,Combobox,Textbox,我在C#中有一个组合框,我希望使用自动完成建议,但是我希望能够在用户类型中更改自动完成条目,因为可能的有效条目太多,无法在启动时填充AutoCompleteStringCollection 例如,假设我让用户输入一个名称。我有一个可能的名字列表(“乔”、“约翰”)和一个姓氏列表(“布洛格斯”、“史密斯”),但如果我每个都有一千个,那么可能会有一百万个字符串——太多了,无法放入自动完成的条目中。因此,最初我只想将名字作为建议(“Joe”、“John”),然后,一旦用户输入了名字(“Joe”),我想

我在C#中有一个组合框,我希望使用自动完成建议,但是我希望能够在用户类型中更改自动完成条目,因为可能的有效条目太多,无法在启动时填充
AutoCompleteStringCollection

例如,假设我让用户输入一个名称。我有一个可能的名字列表(“乔”、“约翰”)和一个姓氏列表(“布洛格斯”、“史密斯”),但如果我每个都有一千个,那么可能会有一百万个字符串——太多了,无法放入自动完成的条目中。因此,最初我只想将名字作为建议(“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()
  • 在更新字符串时清除组合框中的文本,然后再添加回来
  • 更改字符串时将
    自动完成模式设置为“None”,然后将其设置回“SuggestAppend”
  • 挂起
    TextUpdate
    KeyPress
    事件,而不是
    TextChanged
  • 每次都将现有的
    AutoCompleteCustomSource
    替换为新的
    AutoCompleteStringCollection
即使是在各种组合中,这些都没有起到任何作用。建议我尝试重写用于获取自动完成中使用的字符串列表的
组合框
函数。使用反射器,我在
组合框中找到了两个看起来很有前途的方法-
getStringsFrautoComplete()
SetAutoComplete()
,但它们都是私有的,所以我无法从派生类访问它们。我再也受不了了

我尝试用
文本框
替换
组合框
,因为自动完成界面是相同的,我发现行为略有不同。使用
文本框
时,它似乎工作得更好,因为自动完成的附加部分工作正常,但建议部分工作不正常-建议框短暂闪烁,但随后立即消失

所以我想“好吧,我将不使用建议功能,而只使用Append”,但是当我将
AutoCompleteMode
设置为Append时,我得到了一个访问冲突异常。Suggest也会发生同样的情况-唯一不引发异常的模式是
SuggestAppend
,即使SuggestAppend部分的行为不正确

我认为在使用C#托管代码时,不可能获得访问冲突异常。建议我使用“lock”来修复此问题,但我不知道应该锁定什么-唯一具有SyncRoot成员的是
AutoCompleteTestringCollection
,以及无法防止访问冲突异常的锁定。我还试着锁定
组合框
文本框
,但也没有用。据我所知,lock只阻止其他锁,所以如果底层代码没有使用lock,那么我使用它不会有任何区别

所有这一切的结果是,我目前无法使用动态自动完成的
文本框
组合框
。有人对我如何做到这一点有什么见解吗

更新: 我仍然没有让这个工作,但我发现了更多。也许这会激励其他人想出一个解决方案

我尝试用
文本框
替换
组合框
,因为自动完成界面是相同的,我发现行为略有不同。使用
文本框
时,它似乎工作得更好,因为自动完成的附加部分工作正常,但建议部分工作不正常-建议框短暂闪烁,但随后立即消失

所以我想“好吧,我将不使用建议功能,而只使用Append。”然而,当我将
AutoCompleteMode
设置为Append时,我得到了一个访问冲突异常。Suggest也会发生同样的情况-唯一不引发异常的模式是
SuggestAppend
,即使SuggestAppend部分的行为不正确

我认为在使用C#托管代码时不可能获得访问冲突异常,但无论如何,结果是我目前无法使用任何类型的动态自动完成的
文本框
组合框
。有人对我如何实现目标有什么见解吗
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;
}
    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;
            }
        }
    }
if(!textBox3.AutoCompleteCustomSource.Contains(textBox3.Text))
   textBox3.AutoCompleteCustomSource.Add(textBox3.Text);
PropertyInfo[] props = [object].GetType().GetProperties({flags go here});
props[0].SetValue(this, new object[] { 0 });
    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);
        }
    }
    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 );

        }

    }
form.fileComboBox.TextChanged += (sender, e) => {
    var autoComplete = new AutoCompleteStringCollection();
    string[] items = CustomUtil.GetFileNames();
    autoComplete.AddRange(items);
    form.fileComboBox.AutoCompleteCustomSource = autoComplete;
};
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;
        }

}
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 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;
}