vb.net 如何 SuggestAppend 包含字符串的 ComboBox

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26362806/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 18:20:57  来源:igfitidea点击:

How to SuggestAppend a ComboBox containing a string

vb.netwinformscomboboxautocompletecontains

提问by Alex

Goal

目标

I'd like to have my ComboBox items suggest and append its items when something is contained in them, not just via the StartsWith function.

我想让我的 ComboBox 项目在其中包含某些内容时建议并附加它的项目,而不仅仅是通过 StartsWith 函数。

My ComboBox is bound to a DataView which contains clients [CompanyName], [Address], [City] in a long concatenation.

我的 ComboBox 绑定到一个 DataView,其中包含一个长串联的客户 [ CompanyName]、[ Address]、 [ City]。

I want my users to be able to type in the city and still find the records which matches with all of the fields above. I know this is possible with Infragistics but I don't have that package.

我希望我的用户能够输入城市并仍然找到与上述所有字段匹配的记录。我知道 Infragistics 可以做到这一点,但我没有那个包。

Search Term: "Sher"

搜索词:“舍尔

  • Costco, 123 1st Avenue, Sherbrooke
  • Provigo, 344 Ball Street, Sherbrooke
  • Sherbox, 93 7th Street, Montreal
  • Costco, 123 1st Avenue, Sherbrooke
  • Provigo, 344 Ball Street, Sherbrooke
  • Sherbox, 93 7th Street, 蒙特利尔

Is this possible in VB.Net or should I be searching for something else?

这在 VB.Net 中是可能的还是我应该寻找其他东西?

回答by BenD

I did some research and found the following question:

我做了一些研究,发现了以下问题:

Override Winforms ComboBox Autocomplete Suggest Rule

覆盖 Winforms ComboBox 自动完成建议规则

In that question they reffer to another question:

在那个问题中,他们指的是另一个问题:

C# AutoComplete

C# 自动完成

Let's quote the best answer from that question

The existing AutoComplete functionality only supports searching by prefix. There doesn't seem to be any decent way to override the behavior.

Some people have implemented their own autocomplete functions by overriding the OnTextChangedevent. That's probably your best bet.

For example, you can add a ListBoxjust below the TextBoxand set its default visibility to false. Then you can use the OnTextChangedevent of the TextBoxand the SelectedIndexChangedevent of the ListBoxto display and select items.

This seems to work pretty well as a rudimentary example:

public Form1()
{
    InitializeComponent();


    acsc = new AutoCompleteStringCollection();
    textBox1.AutoCompleteCustomSource = acsc;
    textBox1.AutoCompleteMode = AutoCompleteMode.None;
    textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
}

private void button1_Click(object sender, EventArgs e)
{
    acsc.Add("[001] some kind of item");
    acsc.Add("[002] some other item");
    acsc.Add("[003] an orange");
    acsc.Add("[004] i like pickles");
}

void textBox1_TextChanged(object sender, System.EventArgs e)
{
    listBox1.Items.Clear();
    if (textBox1.Text.Length == 0)
    {
    hideResults();
    return;
    }

    foreach (String s in textBox1.AutoCompleteCustomSource)
    {
    if (s.Contains(textBox1.Text))
    {
        Console.WriteLine("Found text in: " + s);
        listBox1.Items.Add(s);
        listBox1.Visible = true;
    }
    }
}

void listBox1_SelectedIndexChanged(object sender, System.EventArgs e)
{
    textBox1.Text = listBox1.Items[listBox1.SelectedIndex].ToString();
    hideResults();
}

void listBox1_LostFocus(object sender, System.EventArgs e)
{
    hideResults();
}

void hideResults()
{
    listBox1.Visible = false;
}

There's a lot more you could do without too much effort: append text to the text box, capture additional keyboard commands, and so forth.

让我们引用那个问题的最佳答案

现有的自动完成功能仅支持按前缀搜索。似乎没有任何体面的方法来覆盖该行为。

有些人通过覆盖OnTextChanged事件实现了自己的自动完成功能。这可能是你最好的选择。

例如,您可以在ListBox正下方添加一个TextBox并将其默认可见性设置为 false。然后就可以使用 的OnTextChanged事件TextBox和 的SelectedIndexChanged事件 ListBox来显示和选择项目。

作为一个基本的例子,这似乎很有效:

public Form1()
{
    InitializeComponent();


    acsc = new AutoCompleteStringCollection();
    textBox1.AutoCompleteCustomSource = acsc;
    textBox1.AutoCompleteMode = AutoCompleteMode.None;
    textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
}

private void button1_Click(object sender, EventArgs e)
{
    acsc.Add("[001] some kind of item");
    acsc.Add("[002] some other item");
    acsc.Add("[003] an orange");
    acsc.Add("[004] i like pickles");
}

void textBox1_TextChanged(object sender, System.EventArgs e)
{
    listBox1.Items.Clear();
    if (textBox1.Text.Length == 0)
    {
    hideResults();
    return;
    }

    foreach (String s in textBox1.AutoCompleteCustomSource)
    {
    if (s.Contains(textBox1.Text))
    {
        Console.WriteLine("Found text in: " + s);
        listBox1.Items.Add(s);
        listBox1.Visible = true;
    }
    }
}

void listBox1_SelectedIndexChanged(object sender, System.EventArgs e)
{
    textBox1.Text = listBox1.Items[listBox1.SelectedIndex].ToString();
    hideResults();
}

void listBox1_LostFocus(object sender, System.EventArgs e)
{
    hideResults();
}

void hideResults()
{
    listBox1.Visible = false;
}

您可以轻松完成更多操作:将文本附加到文本框、捕获额外的键盘命令等。

回答by XDS

Improved the technique demonstrated by BenD in his answer so as to have the mechanism handle a bit more elegantly certain cornercases:

改进了 BenD 在他的回答中展示的技术,以便让机制更优雅地处理某些角落:

public sealed class CCComboboxAutocomplete : ComboBox
{
    public CCComboboxAutocomplete()
    {
        AutoCompleteMode = AutoCompleteMode.Suggest; //crucial otherwise exceptions occur when the user types in text which is not found in the autocompletion list
    }

    protected override void OnTextChanged(EventArgs e)
    {
        try
        {
            if (DesignMode || !string.IsNullOrEmpty(Text) || !Visible) return;

            ResetCompletionList();
        }
        finally
        {
            base.OnTextChanged(e);
        }
    }

    protected override void OnKeyPress(KeyPressEventArgs e)
    {
        try
        {
            if (DesignMode) return;
            if (e.KeyChar == '\r' || e.KeyChar == '\n')
            {
                e.Handled = true;
                if (SelectedIndex == -1 && Items.Count > 0 && Items[0].ToString().ToLowerInvariant().StartsWith(Text.ToLowerInvariant()))
                {
                    Text = Items[0].ToString();
                }
                DroppedDown = false;
                return; //0
            }

            BeginInvoke(new Action(ReevaluateCompletionList)); //1
        }
        finally
        {
            base.OnKeyPress(e);
        }
    }
    //0 Guardclose when detecting any enter keypresses to avoid a glitch which was selecting an item by means of down arrow key followed by enter to wipe out the text within
    //1 Its crucial that we use begininvoke because we need the changes to sink into the textfield  Omitting begininvoke would cause the searchterm to lag behind by one character  That is the last character that got typed in

    private void ResetCompletionList()
    {
        _previousSearchterm = null;
        try
        {
            SuspendLayout();

            var originalList = (object[])Tag;
            if (originalList == null)
            {
                Tag = originalList = Items.Cast<object>().ToArray();
            }

            if (Items.Count == originalList.Length) return;

            while (Items.Count > 0)
            {
                Items.RemoveAt(0);
            }

            Items.AddRange(originalList);
        }
        finally
        {
            ResumeLayout(performLayout: true);
        }
    }

    private string _previousSearchterm;
    private void ReevaluateCompletionList()
    {
        var currentSearchterm = Text.ToLowerInvariant();
        if (currentSearchterm == _previousSearchterm) return; //optimization

        _previousSearchterm = currentSearchterm;
        try
        {
            SuspendLayout();

            var originalList = (object[])Tag;
            if (originalList == null)
            {
                Tag = originalList = Items.Cast<object>().ToArray(); //0
            }

            var newList = (object[])null;
            if (string.IsNullOrEmpty(currentSearchterm))
            {
                if (Items.Count == originalList.Length) return;

                newList = originalList;
            }
            else
            {
                newList = originalList.Where(x => x.ToString().ToLowerInvariant().Contains(currentSearchterm)).ToArray();
            }

            try
            {
                while (Items.Count > 0) //1
                {
                    Items.RemoveAt(0);
                }
            }
            catch
            {
                try
                {
                    Items.Clear();
                }
                catch
                {
                }
            }


            Items.AddRange(newList.ToArray()); //2
        }
        finally
        {
            if (currentSearchterm.Length >= 2 && !DroppedDown)
            {
                DroppedDown = true; //3
                Cursor.Current = Cursors.Default; //4
                Text = currentSearchterm; //5
                Select(currentSearchterm.Length, 0);
            }

            ResumeLayout(performLayout: true);
        }
    }
    //0 backup original list
    //1 clear list by loop through it otherwise the cursor would move to the beginning of the textbox
    //2 reset list
    //3 if the current searchterm is empty we leave the dropdown list to whatever state it already had
    //4 workaround for the fact the cursor disappears due to droppeddown=true  This is a known bu.g plaguing combobox which microsoft denies to fix for years now
    //5 Another workaround for a glitch which causes all text to be selected when there is a matching entry which starts with the exact text being typed in
}

回答by Hovhannes Hakobyan

Sorry for another answer in C# but I have a more improved answer based on xDisruptor's code.

很抱歉在 C# 中有另一个答案,但我有一个基于 xDisruptor 代码的更改进的答案。

Using kinda behavior (decorator).

使用某种行为(装饰器)。

You don't have to subclass ComboBox and change all existing combos in the designed.

您不必继承 ComboBox 并更改设计中的所有现有组合。

Be careful when using Datasource instead of Items collection, because it'll raise an exception.

使用 Datasource 而不是 Items 集合时要小心,因为它会引发异常。

Code:

代码:

public class AutoCompleteBehavior
{
    private readonly ComboBox comboBox;
    private string previousSearchterm;

    private object[] originalList;

    public AutoCompleteBehavior(ComboBox comboBox)
    {
        this.comboBox = comboBox;
        this.comboBox.AutoCompleteMode = AutoCompleteMode.Suggest; // crucial otherwise exceptions occur when the user types in text which is not found in the autocompletion list
        this.comboBox.TextChanged += this.OnTextChanged;
        this.comboBox.KeyPress += this.OnKeyPress;
        this.comboBox.SelectionChangeCommitted += this.OnSelectionChangeCommitted;
    }

    private void OnSelectionChangeCommitted(object sender, EventArgs e)
    {
        if (this.comboBox.SelectedItem == null)
        {
            return;
        }

        var sel = this.comboBox.SelectedItem;
        this.ResetCompletionList();
        this.comboBox.SelectedItem = sel;
    }

    private void OnTextChanged(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(this.comboBox.Text) || !this.comboBox.Visible || !this.comboBox.Enabled)
        {
            return;
        }

        this.ResetCompletionList();
    }

    private void OnKeyPress(object sender, KeyPressEventArgs e)
    {
        if (e.KeyChar == '\r' || e.KeyChar == '\n')
        {
            e.Handled = true;
            if (this.comboBox.SelectedIndex == -1 && this.comboBox.Items.Count > 0
                && this.comboBox.Items[0].ToString().ToLowerInvariant().StartsWith(this.comboBox.Text.ToLowerInvariant()))
            {
                this.comboBox.Text = this.comboBox.Items[0].ToString();
            }

            this.comboBox.DroppedDown = false;

            // Guardclause when detecting any enter keypresses to avoid a glitch which was selecting an item by means of down arrow key followed by enter to wipe out the text within
            return;
        }

        // Its crucial that we use begininvoke because we need the changes to sink into the textfield  Omitting begininvoke would cause the searchterm to lag behind by one character  That is the last character that got typed in
        this.comboBox.BeginInvoke(new Action(this.ReevaluateCompletionList));
    }

    private void ResetCompletionList()
    {
        this.previousSearchterm = null;
        try
        {
            this.comboBox.SuspendLayout();

            if (this.originalList == null)
            {
                this.originalList = this.comboBox.Items.Cast<object>().ToArray();
            }

            if (this.comboBox.Items.Count == this.originalList.Length)
            {
                return;
            }

            while (this.comboBox.Items.Count > 0)
            {
                this.comboBox.Items.RemoveAt(0);
            }

            this.comboBox.Items.AddRange(this.originalList);
        }
        finally
        {
            this.comboBox.ResumeLayout(true);
        }
    }

    private void ReevaluateCompletionList()
    {
        var currentSearchterm = this.comboBox.Text.ToLowerInvariant();
        if (currentSearchterm == this.previousSearchterm)
        {
            return;
        }

        this.previousSearchterm = currentSearchterm;
        try
        {
            this.comboBox.SuspendLayout();

            if (this.originalList == null)
            {
                this.originalList = this.comboBox.Items.Cast<object>().ToArray(); // backup original list
            }

            object[] newList;
            if (string.IsNullOrEmpty(currentSearchterm))
            {
                if (this.comboBox.Items.Count == this.originalList.Length)
                {
                    return;
                }

                newList = this.originalList;
            }
            else
            {
                newList = this.originalList.Where(x => x.ToString().ToLowerInvariant().Contains(currentSearchterm)).ToArray();
            }

            try
            {
                // clear list by loop through it otherwise the cursor would move to the beginning of the textbox
                while (this.comboBox.Items.Count > 0)
                {
                    this.comboBox.Items.RemoveAt(0);
                }
            }
            catch
            {
                try
                {
                    this.comboBox.Items.Clear();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }
            }

            this.comboBox.Items.AddRange(newList.ToArray()); // reset list
        }
        finally
        {
            if (currentSearchterm.Length >= 1 && !this.comboBox.DroppedDown)
            {
                this.comboBox.DroppedDown = true; // if the current searchterm is empty we leave the dropdown list to whatever state it already had
                Cursor.Current = Cursors.Default; // workaround for the fact the cursor disappears due to droppeddown=true  This is a known bu.g plaguing combobox which microsoft denies to fix for years now
                this.comboBox.Text = currentSearchterm; // Another workaround for a glitch which causes all text to be selected when there is a matching entry which starts with the exact text being typed in
                this.comboBox.Select(currentSearchterm.Length, 0);
            }

            this.comboBox.ResumeLayout(true);
        }
    }
}

Usege:

用途:

new AutoCompleteBehavior(this.comboBoxItems);
this.comboBoxItems.Items.AddRange(new object[] { "John", "Tina", "Doctor", "Alaska" });

TIP: Can be further improved by making an extension to the ComboBox class like myCombo.ToAutoComplete()

提示:可以通过对 ComboBox 类进行扩展,如 myCombo.ToAutoComplete() 来进一步改进

回答by Amorphis

A ComboBox,TextBoxand I think a DropDownListhas AutoComplete properties Look at http://msdn.microsoft.com/en-us/library/system.windows.forms.combobox.autocompletemode(v=vs.110).aspx

A ComboBoxTextBox我认为DropDownList有 AutoComplete 属性 看看http://msdn.microsoft.com/en-us/library/system.windows.forms.combobox.autocompletemode(v=vs.110).aspx

It explains which AutoCompleteMode you should use and how to set the AutoCompleteSource

它解释了您应该使用哪种 AutoCompleteMode 以及如何设置 AutoCompleteSource

回答by DoIt

You could try the following lines, it worked for me

您可以尝试以下几行,它对我有用

 cbxName.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
 cbxName.AutoCompleteSource = AutoCompleteSource.ListItems;