How to suggestAppend ComboBox containing string

purpose

I would like my ComboBox objects to suggest and add their items when something was contained in them, not just via the StartsWith function.

My ComboBox is associated with a DataView which contains customers [ CompanyName ], [ Address ], [ City ] in a long concatenation.

I want my users to be able to enter a city and still find records that match all of the above fields. I know this is possible with Infragistics, but I don't have this package.

Search term: Cher "

  • Costco, 123 1st Avenue, Sher brooke
  • Provigo, 344 Ball Street, Cher brooke
  • Cher , 93 7th Street, Montreal

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

+3


source to share


5 answers


I did some research and found the following question:

Override Winforms ComboBox Autocomplete Suggest Rule

In this question, they turn to another question:



C # autocomplete

To quote the best answer from this question

Existing AutoComplete functions only support search by prefix. There seems to be no decent way to override the behavior.

Some people have implemented their own autocomplete functionality by overriding the event OnTextChanged

. This is probably the best choice.

For example, you can add ListBox

just below TextBox

and set its default visibility to false. Then you can use OnTextChanged

the event event TextBox

and SelectedIndexChanged

ListBox

to display and select items.

This seems to work pretty well, as an 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 you can do a lot with little effort: add text to the textbox, capture additional keyboard commands, etc.

+6


source


Improved the method shown by BenD in his answer to have a slightly more elegantly defined angle mechanism:



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
}

      

+4


source


Sorry for the other C # answer, but I have a better answer based on xDisruptor code.

Using curious behavior (decorator).

You don't need to subclass the ComboBox and change all existing combos as designed.

Be careful when using a data source instead of the Items collection because this will throw an exception.

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 improved by adding an extension to the ComboBox class like myCombo.ToAutoComplete ()

0


source


A ComboBox

, TextBox

and I think it DropDownList

has autocomplete properties Look at 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 AutoCompleteSource

-3


source


You can try the following lines, it worked for me

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

      

-3


source







All Articles