我如何dynamic更改C#combobox或文本框中的自动完成条目?
我在C#中有一个combobox,我想使用自动完成的build议,但是我想能够改变自动完成条目作为用户键入,因为可能有效的条目太多,无法在启动时填充AutoCompleteStringCollection
。
作为一个例子,假设我让用户键入一个名字。 我有一个可能的名字(“乔”,“约翰”)和一个姓氏列表(“Bloggs”,“史密斯”)的名单,但如果我有一千个,那么这将是一百万个可能的string – 自动完成条目太多了。 所以最初我想只有第一个名字作为build议(“Joe”,“John”),然后一旦用户键入了名字(“Joe”),我想删除现有的自动完成条目并replace他们用一组新名字组成,后面跟着可能的姓氏(“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()会导致自动完成机制“closures”,直到下一个字符出现在combobox中,但当然当下一个字符出现时,上面的代码再次调用Clear(),所以用户从不实际上看到了自动完成function。 它也导致combobox的全部内容被选中,所以在每次按键之间你必须取消select现有的文本,这使得它不可用。 如果我删除对Clear()的调用,那么自动完成工作,但似乎那么AddRange()
调用没有效果,因为我添加的新build议不会出现在自动完成下拉列表中。
我一直在寻找解决scheme,并看到各种build议,但我不能让他们中的任何一个工作 – 自动完成function出现禁用,或新的string不出现。 以下是我尝试过的一些列表:
- 在更改string之前调用
BeginUpdate()
之后调用EndUpdate()
。 - 在所有现有string上调用
Remove()
而不是Clear()。 - 在更新string的同时清除combobox中的文本,然后将其添加回来。
- 在我更改string时将
AutoCompleteMode
设置为“None”,之后将其设置回“SuggestAppend”。 -
TextUpdate
或KeyPress
事件而不是TextChanged
。 - 每次使用新的
AutoCompleteStringCollection
replace现有的AutoCompleteCustomSource
。
这些都没有帮助,即使在各种组合。 Spencebuild议我尝试覆盖ComboBox
函数,该函数获取在自动完成中使用的string列表。 使用reflection器我在ComboBox
类中发现了一些看起来很有前途的方法 – GetStringsForAutoComplete()
和SetAutoComplete()
,但它们都是私有的,所以我无法从派生类访问它们。 我无法进一步采取。
我尝试用TextBox
replaceComboBox
,因为自动完成的接口是相同的,我发现行为有些不同。 随着TextBox
它似乎更好地工作,因为自动完成的附加部分正常工作,但build议部分不 – build议框短暂闪烁的生活,但然后立即消失。
所以我想“好吧,我将生活没有build议的function,只是使用附加”,但是当我设置AutoCompleteMode
追加,我得到一个访问冲突exception。 同样的事情发生在build议 – 唯一的模式,不抛出exception是SuggestAppend
,即使build议部分不正确的行为。
我认为在使用C#托pipe代码时,应该不可能出现访问冲突exception。 阿夫拉姆build议我使用“locking”来解决这个问题,但我不知道我应该locking什么 – 唯一拥有SyncRoot成员的是AutoCompleteStringCollection
,而且locking不会阻止访问冲突exception。 我也尝试lockingComboBox
或TextBox
,但是这也没有帮助。 据我了解,锁只能防止其他锁,所以如果底层代码不使用锁,那么我使用它不会有任何区别。
所有这一切的结果是,我目前不能使用dynamic自动完成的TextBox
或ComboBox
。 有没有人对我如何实现这一目标有所了解?
更新:
我还没有这个工作,但我发现了更多。 也许其中一些将激励别人提出一个解决scheme。
我尝试用TextBox
replaceComboBox
,因为自动完成的接口是相同的,我发现行为有些不同。 随着TextBox
它似乎更好地工作,因为自动完成的附加部分正常工作,但build议部分不 – build议框短暂闪烁的生活,但然后立即消失。
所以我想“好吧,我将生活没有Suggestfunction,只是使用Append”,但是当我设置AutoCompleteMode
为追加,我得到一个访问冲突exception。 同样的事情发生在build议 – 唯一的模式,不抛出exception是SuggestAppend
,即使build议部分不正确的行为。
我认为,使用C#托pipe代码时,应该是不可能出现访问冲突exception,但无论如何,结果是我目前无法使用任何types的dynamic自动完成的TextBox
或ComboBox
。 有没有人对我如何实现这一目标有所了解?
更新2:
在尝试了各种其他方法之后,例如在工作线程中更改自动完成,并使用BeginInvoke()
来模拟PostMessage()types的行为后,我终于放弃了,并使用列表框实现了自己的自动完成下拉菜单。 它比内build的响应要快得多,我花的时间也less于我尝试让内置的工作的时间,所以对于那些想要这种行为的人来说,教训是 – 你可能会更好自己实施。
我有同样的问题,并find一个非常简单的解决方法。 和其他人一样,我找不到任何方法来控制组件的行为,所以我不得不接受它。
自然的行为是:每当用户input文本框时,都不能dynamic填充列表。 您必须填充一次,然后自动完成机制才能控制。 得出的结论是:您应该填充AutoCompleteCustomSource,使其能够在您的数据库中进行任何input,以使其正常工作。
当然,如果你有数百万条logging来填充列表,这是不可行的。 数据传输和AutoComplete机制本身的性能问题不会允许你这样做。
我发现的妥协解决scheme是:每次文本长度达到正好N个字符(在我的情况下3)dynamic填充AutoCompleteCustomSource。 这工作,因为复杂性大大减less。 从数据库中提取的与这3个字符匹配的logging数足够小,可以避免任何性能问题。
主要缺点是:用户在input第N个字符之前不会显示“自动完成”列表。 但似乎用户并没有真正期望一个有意义的自动完成列表3个字符键入之前。
希望这可以帮助。
我想你可能想要退出reflection器,并重写combobox本身的自动完成行为。 我确定autocompletion会调用一个访问自动完成列表的函数。 如果你能find这个函数并覆盖它,你可以使用你想要的任何行为。
看看你可以find什么文件在combobox本身。
我没有testing这个,但可能值得一试。
保留两个实例,而不是清除AutoCompleteCustomSource,双缓冲区。 当文本更改时,调用GetNameSuggestions()并为当前未使用的string构buildstring,然后将ComboName.AutoCompleteCustomSource设置为刚设置的string。
我认为它应该看起来像这样。
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; }
更新:把锁放在这个地方的主要原因是
它的工作:)大部分“神秘的exception”,我曾经有过,这个把戏后消失
- 像这个代码中的锁,可以帮助你的例外
- 如前所述,使用文本框的问题较less
- 在这个代码中,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; } } }
山姆,你有这个想法吗? 我遇到了同样的情况。 Clear()似乎会导致exception。 我删除了通话清除,我收到正确的build议事件,虽然集合不断增长…
另外,关于私人成员:您可以使用reflection来访问它们:
PropertyInfo[] props = [object].GetType().GetProperties({flags go here}); props[0].SetValue(this, new object[] { 0 });
if(!textBox3.AutoCompleteCustomSource.Contains(textBox3.Text)) textBox3.AutoCompleteCustomSource.Add(textBox3.Text);
我最初来到这里寻找解决scheme,但现在已经find了我自己的解决scheme。
诀窍不是调用AutoCompleteCustomSource上的Clear(),而是删除for循环中的所有项目,然后使用新数据重build列表。 在我的情况下(书籍收集应用程序),我从数据库中检索作者姓名,而不是整个地段。 请注意,只有在combobox的文本框部分已经变为空的情况下才能使用。
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 ); } }
对我来说,秘密是使用TextChanged事件,没有KeyDown / Up / Press等
更新:在dynamic改变AutoCompleteCustomSource的其他问题之后,我最终放弃了使用内置的自动完成function,并在比原本浪费的时间更短的时间内实现了我自己的function。 非托pipe代码中似乎存在一些实现ComboBox控件的问题。 具体来说,我应该在TextChanged事件处理程序触发时遇到问题。 我决定只在我的自定义实现中使用OnKeyDown / Press / Up处理程序,而且似乎更可靠。
这对我来说,你不要添加addRange
到相同的AutoCompleteStringCollection
,而是每次创build一个新的。
form.fileComboBox.TextChanged += (sender, e) => { var autoComplete = new AutoCompleteStringCollection(); string[] items = CustomUtil.GetFileNames(); autoComplete.AddRange(items); form.fileComboBox.AutoCompleteCustomSource = autoComplete; };
最好的解决scheme是使用combobox的事件处理程序。 通过使用textUpdate KeyDown DropDown和ChangeCommit ,您可以模仿autocompletemode,您可以自定义要search的内容以及在下拉列表中显示的内容。
我发现这个答案很有用,但它是用visual c ++编写的,它是toolstripcombobox,但是这个概念是相同的 。 无论如何,.net中的c#和c ++有很大的相似性,在理解这个解决scheme时不应该是一个问题。
ToolStripCombobox在Visual C ++中的自定义自动search
使用这个代码
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; } }