如何动态更改 C# 组合框或文本框中的自动完成条目? [英] How can I dynamically change auto complete entries in a C# combobox or textbox?

查看:34
本文介绍了如何动态更改 C# 组合框或文本框中的自动完成条目?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 C# 中有一个组合框,我想对它使用自动完成建议,但是我希望能够在用户键入时更改自动完成条目,因为可能的有效条目太多而无法填充 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(对象发送者,EventArgs e){字符串文本 = this.ComboName.Text;string[] 建议 = GetNameSuggestions( text );this.ComboQuery.AutoCompleteCustomSource.Clear();this.ComboQuery.AutoCompleteCustomSource.AddRange( 建议);}

然而,这不能正常工作.似乎调用 Clear() 会导致自动完成机制关闭"直到下一个字符出现在组合框中,但当然,当下一个字符出现时,上面的代码再次调用 Clear(),因此用户实际上从未看到自动完成功能.它还会导致组合框的全部内容被选中,因此在每次按键之间您必须取消选择现有文本,这使其无法使用.如果我删除对 Clear() 的调用,则自动完成工作,但似乎 AddRange() 调用不起作用,因为我添加的新建议不会出现在自动完成中下拉菜单.

我一直在寻找解决方案,并看到了各种建议,但我无法让它们中的任何一个工作 - 自动完成功能似乎被禁用,或者没有出现新字符串.这是我尝试过的事情的列表:

  • 在更改字符串之前调用 BeginUpdate(),之后调用 EndUpdate().
  • 对所有现有字符串调用 Remove() 而不是 Clear().
  • 在更新字符串时从组合框中清除文本,然后再将其添加回来.
  • AutoCompleteMode 设置为无";当我更改字符串并将其设置回SuggestAppend"时;之后.
  • 挂钩 TextUpdateKeyPress 事件而不是TextChanged.
  • 每次都用新的 AutoCompleteStringCollection 替换现有的 AutoCompleteCustomSource.

这些都没有帮助,即使在各种组合中也是如此.Spence 建议我尝试覆盖 ComboBox 函数,该函数获取要在 auto 中使用的字符串列表完全的.使用反射器,我在 ComboBox 类中发现了一些看起来很有前景的方法 - GetStringsForAutoComplete()SetAutoComplete(),但它们都是private 所以我不能从派生类访问它们.我不能再这样下去了.

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

所以我想好吧,我会在没有 Suggest 功能的情况下继续生活,而只是使用 Append",但是当我将 AutoCompleteMode 设置为 Append 时,我收到了访问冲突异常.同样的事情发生在 Suggest - 唯一不抛出异常的模式是 SuggestAppend,即使 Suggest 部分的行为不正确.

我认为使用 C# 托管代码时应该不可能获得访问冲突异常.Avram 建议我使用lock";来解决这个问题,但我不知道我应该锁定什么 - 唯一具有 SyncRoot 成员的是 AutoCompleteStringCollection,并且锁定不会阻止访问冲突异常.我还尝试锁定 ComboBoxTextBox,但这也无济于事.据我了解,锁只能防止其他锁,所以如果底层代码没有使用锁,那么我使用它不会有任何区别.

这一切的结果是,我目前无法使用具有动态自动完成功能的 TextBoxComboBox.有没有人对我如何实现这一目标有任何见解?

更新:

我还没有让这个工作,但我发现了更多.也许其中一些会激励其他人提出解决方案.

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

所以我想好吧,我不会使用 Suggest 功能,而是使用 Append,"但是,当我将 AutoCompleteMode 设置为 Append 时,出现访问冲突异常.同样的事情发生在 Suggest - 唯一不抛出异常的模式是 SuggestAppend,即使 Suggest 部分的行为不正确.

我认为在使用 C# 托管代码时应该不可能获得访问冲突异常,但无论如何,结果是我目前无法使用 TextBoxComboBox 具有任何类型的动态自动完成功能.有没有人对我如何实现这一目标有任何见解?

更新 2:

在尝试了各种其他事情(例如更改工作线程中的自动完成功能,并使用 BeginInvoke() 模拟 PostMessage() 类型行为之后,我终于放弃了,只是实现了我自己的自动完成下拉菜单使用列表框.它比内置的响应更快,而且我花在这方面的时间比尝试让内置的工作要少得多,所以对于想要这种行为的其他人来说,教训是 - 你可能会过得更好自己实现.

解决方案

我遇到了同样的问题,并找到了一个非常简单的解决方法.和这里的其他人一样,我找不到任何方法来控制组件的行为,所以我不得不接受它.

自然的行为是:您不能在用户每次在文本框中键入内容时动态填充列表.您必须填充一次,然后 AutoComplete 机制取得控制权.结论是:您应该使用数据库中的每个可能的条目填充 AutoCompleteCustomSource 以使其按我们想要的方式工作.

当然,如果您有数百万条记录来填充列表,这当然是不可行的.数据传输中的性能问题和自动完成机制本身不允许您这样做.

我找到的折衷解决方案是:每次文本长度恰好达到 N 个字符(在我的情况下为 3 个)时,动态填充 AutoCompleteCustomSource.这是有效的,因为复杂性大大降低.从数据库中提取的与这 3 个初始字符匹配的记录数量足够小,可以避免任何性能问题.

主要缺点是:用户在输入第 N 个字符之前不会看到自动完成列表.但在输入 3 个字符之前,用户似乎并不真正期望一个有意义的自动完成列表.

希望这会有所帮助.

I have a combobox in C# and I want to use auto complete suggestions with it, however I want to be able to change the auto complete entries as the user types, because the possible valid entries are far too numerous to populate the AutoCompleteStringCollection at startup.

As an example, suppose I'm letting the user type in a name. I have a list of possible first names ("Joe", "John") and a list of surnames ("Bloggs", "Smith"), but if I have a thousand of each, then that would be a million possible strings - too many to put in the auto complete entries. So initially I want to have just the first names as suggestions ("Joe", "John") , and then once the user has typed the first name, ("Joe"), I want to remove the existing auto complete entries and replace them with a new set consisting of the chosen first name followed by the possible surnames ("Joe Bloggs", "Joe Smith"). In order to do this, I tried the following code:

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

However, this does not work properly. It seems that the call to Clear() causes the auto complete mechanism to "turn off" until the next character appears in the combo box, but of course when the next character appears the above code calls Clear() again, so the user never actually sees the auto complete functionality. It also causes the entire contents of the combo box to become selected, so between every keypress you have to deselect the existing text, which makes it unusable. If I remove the call to Clear() then the auto complete works, but it seems that then the AddRange() call has no effect, because the new suggestions that I add do not appear in the auto complete dropdown.

I have been searching for a solution to this, and seen various things suggested, but I cannot get any of them to work - either the auto complete functionality appears disabled, or new strings do not appear. Here is a list of things I have tried:

  • Calling BeginUpdate() before changing the strings and EndUpdate() afterward.
  • Calling Remove() on all the existing strings instead of Clear().
  • Clearing the text from the combobox while I update the strings, and adding it back afterward.
  • Setting the AutoCompleteMode to "None" while I change the strings, and setting it back to "SuggestAppend" afterwards.
  • Hooking the TextUpdate or KeyPress event instead of TextChanged.
  • Replacing the existing AutoCompleteCustomSource with a new AutoCompleteStringCollection each time.

None of these helped, even in various combinations. Spence suggested that I try overriding the ComboBox function that gets the list of strings to use in auto complete. Using a reflector I found a couple of methods in the ComboBox class that look promising - GetStringsForAutoComplete() and SetAutoComplete(), but they are both private so I can't access them from a derived class. I couldn't take that any further.

I tried replacing the ComboBox with a TextBox, because the auto complete interface is the same, and I found that the behaviour is slightly different. With the TextBox it appears to work better, in that the Append part of the auto complete works properly, but the Suggest part doesn't - the suggestion box briefly flashes to life but then immediately disappears.

So I thought "Okay, I'll live without the Suggest functionality and just use Append instead", however when I set the AutoCompleteMode to Append, I get an access violation exception. The same thing happens with Suggest - the only mode that doesn't throw exceptions is SuggestAppend, even though the Suggest part doesn't then behave correctly.

I thought that it was supposed to be impossible to get access violation exceptions when using C# managed code. Avram suggested I use "lock" to fix this, but I don't know what I should lock - the only thing that has a SyncRoot member is the AutoCompleteStringCollection, and locking that doesn't prevent the access violation exceptions. I also tried locking the ComboBox or TextBox, but that didn't help either. As I understand it, lock only prevents other locks, so if the underlying code isn't using lock then my using it won't make any difference.

The upshot of all this is that I can't currently use a TextBox or a ComboBox with dynamic auto complete. Does anyone have any insights into how I could achieve this?

Update:

I still haven't got this working, but I have found out some more. Maybe some of this will inspire someone else to come up with a solution.

I tried replacing the ComboBox with a TextBox, because the auto complete interface is the same, and I found that the behaviour is slightly different. With the TextBox it appears to work better, in that the Append part of the auto complete works properly, but the Suggest part doesn't - the suggestion box briefly flashes to life but then immediately disappears.

So I thought "Okay, I'll live without the Suggest functionality and just use Append instead," however when I set the AutoCompleteMode to Append, I get an access violation exception. The same thing happens with Suggest - the only mode that doesn't throw exceptions is SuggestAppend, even though the Suggest part doesn't then behave correctly.

I thought that it was supposed to be impossible to get access violation exceptions when using C# managed code, but anyway, the upshot is that I can't currently use a TextBox or a ComboBox with any kind of dynamic auto complete. Does anyone have any insights into how I could achieve this?

Update 2:

After trying various other things such as changing the autocomplete in a worker thread, and using BeginInvoke() to simulate PostMessage() type behaviour, I finally gave up and just implemented my own auto complete dropdown using a list box. It's much more responsive than the built-in one, and I spent less time doing that than I did trying to get the built-in one to work, so the lesson for anyone else who wants this behaviour is - you're probably better off implementing it yourself.

解决方案

I had the same problem, and found an extremely simple workaround. As everybody else here, I couldn't find any means to control de behaviour of the component, so I had to accept it.

The natural behaviour is: you can't dynamically populate the list every time the user types into the text box. You have to populate it once, and then the AutoComplete mechanism takes control. The conclusion is: you should populate the AutoCompleteCustomSource with every possible entry in you database to make it work as we want.

Of course this is not viable if you have millions of records to populate the list. Performance issues in data transfer and the AutoComplete mechanism itself will not allow you to do that.

The compromise solution I found was: dynamically populate the AutoCompleteCustomSource every time that the Text length reaches exactly N chars (3 in my case). This worked because complexity was drastically reduced. The number of records that are fetched from the database that match these 3 initial chars was small enough to avoid any performance issues.

The major drawback is: users will not be presented the AutoComplete list until they type the N-th char. But it seems like users don't really expect a meaningful AutoComplete list before 3 chars are typed.

Hope this helps.

这篇关于如何动态更改 C# 组合框或文本框中的自动完成条目?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆