Autocomplete in combobox, matching any part of string.

dualshck012

Active member
Joined
Jan 14, 2018
Messages
29
Location
Leyte, Philippines
Programming Experience
1-3
I have a combobox control, and it contains items:
123 abc
12 ab
abc 123
def
ghm 123


I want when i write "123" into combobox, then dropdown list will show:
123 abc
abc 123
ghm 123

I already have a code but I'm getting this error message:

An unhandled exception of type 'System.Data.SyntaxErrorException' occurred in System.Data.dll
Additional information: Syntax error: Missing operand after 'diagtext' operator.


C#:
DataTable AllNames = new DataTable();
        private void Cward_combox_KeyPress(object sender, KeyPressEventArgs e)
        {
            string name = string.Format("{0}{1}", Cward_combox.Text, e.KeyChar.ToString());
            DataRow[] rows = table.Select(string.Format("select diagtext from hencdiag where diagtext LIKE '%{0}%'", name));
            DataTable filteredTable = AllNames.Clone();
            foreach (DataRow r in rows)
                filteredTable.ImportRow(r);
            Cward_combox.DataSource = null;
            Cward_combox.DataSource = filteredTable.DefaultView;
            Cward_combox.DisplayMember = "diagtext";
        }
 
It seems to me that you should probably get all the data up front, bind the DataTable and then set the RowFilter in the TextChanged event handler. Apart from anything else, your code doesn;t allow for the fact that the user could place the cursor in the middle of the text or at the beginning, instead of the end.
 
From a UI design perspective, if you have so many items in a combobox that a user needs autocomplete that looks at substrings to filter down the choices, it's a big hint that a combobox is not the correct control to use, and that you need to re-design your UI.

Perhaps what you really need is a text box that has autocomplete? Are you just using the combobox as poor man's autocomplete?
 
Hi, i just used a custom control combo box to make the effect. Much responsive than the autocomplete custom source combo box property

custom combobox control:
namespace SAMPLE.CustomControls
{
  
        public class MyComboBox : ComboBox
        {

            private IList<object> collectionList = null;

            public MyComboBox()
            {
                //InitializeComponent();
                collectionList = new List<object>();
            }

        private void InitializeComponent()
        {
            throw new NotImplementedException();
        }

        public MyComboBox(IContainer container)
            {
                container.Add(this);
                //InitializeComponent();
            }

            protected override void OnTextUpdate(EventArgs e)
            {
                IList<object> Values = collectionList
                    .Where(x => x.ToString().ToLower().Contains(Text.ToLower()))
                    .ToList<object>();

                this.Items.Clear();
                if (this.Text != string.Empty)
                    this.Items.AddRange(Values.ToArray());
                else
                    this.Items.AddRange(collectionList.ToArray());

                this.SelectionStart = this.Text.Length;
                this.DroppedDown = true;
            }

            protected override void OnTextChanged(EventArgs e)
            {
                if (this.Text == string.Empty)
                {
                    this.Items.Clear();
                    this.Items.AddRange(collectionList.ToArray());
                }
            }

            protected override void OnCreateControl()
            {
                base.OnCreateControl();
                collectionList = this.Items.OfType<object>().ToList();
            }
        }
    
}
 
From a UI design perspective, if you have so many items in a combobox that a user needs autocomplete that looks at substrings to filter down the choices, it's a big hint that a combobox is not the correct control to use, and that you need to re-design your UI.

Perhaps what you really need is a text box that has autocomplete? Are you just using the combobox as poor man's autocomplete?
well, a textbox custom control?
 
The suggestion being proposed is on the lines of AutoCompleteStringCollection Class (System.Windows.Forms) which I will briefly demonstrate, but it will require changing your UI to add the Textbox control (If you want a search textbox instead, which seems more appropriate. Because how else is your user to assume your search bar is also the control supplying the data?) Anyway. Whatever you decide, these two methods will do it. If you add a search bar, use this :
C#:
            textBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
            /** Use AutoCompleteMode = AutoCompleteMode.SuggestAppend which will let you edit as its suggested **/
            textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
            /** I went with the custom source because I want to assign a custom collection from the AutoCompleteStringCollection() class  **/
            AutoCompleteStringCollection predictive_Collection = new AutoCompleteStringCollection();
            /** Define your collection of predictive words and phrases **/
            string[] predictive_Words = { "my", "name", "is", "Sheepings", "my name is Sheepings" };
            /** Define your array of words or suggested text(s) **/
            predictive_Collection.AddRange(predictive_Words);
            /** Add your suggested text(s) of predicted words to the predictive collection, and then assign the source below. **/
            textBox1.AutoCompleteCustomSource = predictive_Collection;
            /** Now when you type, you will have auto predict on the textbox control. **/

Then, you can use whichever method you wish to execute the following snipped which will find the value in the combobox, and set the selected index on the comboBox providing the value can be found. I've demonstrated this with a button and it works well; although, you may want to implement some type of key press for the autocomplete, and on the textbox control, once the control sets the text, you could fire the following to pull up the relevant data from your combobox :
C#:
            foreach (var obj in from string item in comboBox1.Items
                              where item.Equals(textBox1.Text)
                              select new { })
            {
                comboBox1.SelectedIndex = comboBox1.Items.IndexOf(textBox1.Text);
            }
I want when i write "123" into combobox, then dropdown list will show:
My example above does show how to do this if you want to add a search bar as a textbox control.

With the below screenshot, you can set some additional options on the combobox itself to achieve just what you want without adding a search bar. However, I prefer to set stuff like this in the code, rather than the designer. Ie :
Screenshot_28.jpg

The first block of code above is the same as setting theses properties on the designer for the comboBox, except you'd need to change textbox1 to comboBox1 like so :
C#:
            comboBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
            /** Use AutoCompleteMode = AutoCompleteMode.SuggestAppend which will let you edit as its suggested **/
            comboBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
            /** I went with the custom source because I want to assign a custom collection from the AutoCompleteStringCollection() class **/
            AutoCompleteStringCollection predictive_Collection = new AutoCompleteStringCollection();
            /** Define your collection of predictive words and phrases **/
            string[] predictive_Words = { "my", "name", "is", "Sheepings", "my name is Sheepings" };
            /** Define your array of words or suggested text(s) **/
            predictive_Collection.AddRange(predictive_Words);
            /** Add your suggested text(s) of predicted words to the predictive collection, and then assign the source below. **/
            comboBox1.AutoCompleteCustomSource = predictive_Collection;
            /** Now when you type, you will have auto predict on the combobox control. **/

Screenshot_27.jpg


Both work well, but if you want to see if your predicted text exists in a separate control, ie a textBox, and search the comboBox for a predicted item from another control which provides
AutoCompleteStringCollection functionality, I'd actually drop the Linq loop for a simple string search and set the selected index of the comboBox if the result can be found :
C#:
comboBox1.SelectedIndex = comboBox1.FindString(textBox1.Text);

Hope that helps. Sorry if I went around the houses a bit on that one, I was just playing around with it as I was typing this out. :)
 
Last edited:
Hi, i just used a custom control combo box to make the effect. Much responsive than the autocomplete custom source combo box property

custom combobox control:
namespace SAMPLE.CustomControls
{
 
        public class MyComboBox : ComboBox
        {

            private IList<object> collectionList = null;

            public MyComboBox()
            {
                //InitializeComponent();
                collectionList = new List<object>();
            }

        private void InitializeComponent()
        {
            throw new NotImplementedException();
        }

        public MyComboBox(IContainer container)
            {
                container.Add(this);
                //InitializeComponent();
            }

            protected override void OnTextUpdate(EventArgs e)
            {
                IList<object> Values = collectionList
                    .Where(x => x.ToString().ToLower().Contains(Text.ToLower()))
                    .ToList<object>();

                this.Items.Clear();
                if (this.Text != string.Empty)
                    this.Items.AddRange(Values.ToArray());
                else
                    this.Items.AddRange(collectionList.ToArray());

                this.SelectionStart = this.Text.Length;
                this.DroppedDown = true;
            }

            protected override void OnTextChanged(EventArgs e)
            {
                if (this.Text == string.Empty)
                {
                    this.Items.Clear();
                    this.Items.AddRange(collectionList.ToArray());
                }
            }

            protected override void OnCreateControl()
            {
                base.OnCreateControl();
                collectionList = this.Items.OfType<object>().ToList();
            }
        }
   
}
There are two problems,
1. you can't use data source.
2. you must add items in form construtor because of OnCreateControl overrided.
Have you a solution please?
 
I've solved it :)

You need to:

1. Have a normal ComboBox (DropDownStyle = DropDown, AutoCompleteMode = None, AutoCompleteSource = None), lets call it: comboBox1
2. Add the events SelectedIndexChanged, and TextUpdate

Then use the following code:

C#:
    using System;
    using System.Collections.Generic;
    using System.Windows.Forms;
 
    namespace CB_Contains
    {
        public partial class Form1 : Form
        {
            private Dictionary<String, System.Int32> CBFullList;
            private Dictionary<String, System.Int32> CBFilteredList;
 
            bool ComboBoxBusy;
 
            public Form1()
            {
                InitializeComponent();
 
                ComboBoxBusy = false;
                CBFullList = new Dictionary<string, Int32>();
                CBFilteredList = new Dictionary<string, Int32>();
            }
 
            private void Form1_Load(object sender, EventArgs e)
            {
                CBFullList.Add("None", 0);
                CBFullList.Add("123 abc", 1);
                CBFullList.Add("12 ab", 2);
                CBFullList.Add("abc 123", 3);
                CBFullList.Add("def", 4);
                CBFullList.Add("ghm 123", 5);
 
                FilterList(false);
            }
 
            private void FilterList(bool show)
            {
                if (ComboBoxBusy == false)
                {
                    String orgText;
 
                    ComboBoxBusy = true;
                    orgText = comboBox1.Text;
 
                    comboBox1.DroppedDown = false;
 
                    CBFilteredList.Clear();
 
                    foreach (KeyValuePair<string, Int32> item in CBFullList)
                    {
                        if (item.Key.ToUpper().Contains(orgText.ToUpper()))
                            CBFilteredList.Add(item.Key, item.Value);
                    }
 
                    if (CBFilteredList.Count < 1)
                        CBFilteredList.Add("None", 0);
 
                    comboBox1.BeginUpdate();
                    comboBox1.DataSource = new BindingSource(CBFilteredList, null);
                    comboBox1.DisplayMember = "Key";
                    comboBox1.ValueMember = "Value";
 
                    comboBox1.DroppedDown = show;
                    comboBox1.SelectedIndex = -1;
                    comboBox1.Text = orgText;
                    comboBox1.Select(comboBox1.Text.Length, 0);
                    comboBox1.EndUpdate();
                    Cursor.Current = Cursors.Default;
 
                    ComboBoxBusy = false;
                }
            }
 
            private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
            {
                if (ComboBoxBusy == false)
                {
                    FilterList(false);
                }
            }
 
            private void comboBox1_TextUpdate(object sender, EventArgs e)
            {
                FilterList(true);
            }
        }
     }

Well, that's it :) I hope my way is very easy
 
Last edited:
Back
Top Bottom