2

I'm writing a program that at one point implements a TextBox with autocomplete. Currently, for purpose of simplicity I'm using CustomSource manually populated by several entries at design time. While autocomplete works fine, I'd like it to make suggestions that don't simply start with the currently entered text, but contain it at any position in the stored choices.

For example, if words "globe", "lobe", and "glide" are the stored options, typing in "gl" correctly suggests both "globe" and "glide".

However, I'd like it to suggest both "globe" and "lobe" when "lob" is typed in. I'm not exactly sure how to approach this.

Has anyone done this before? VB.NET or C# are both fine, as long as I can figure out a proper .NET way to do this.

Cheers! = )

Phonon
  • 12,549
  • 13
  • 64
  • 114

2 Answers2

2

So i was looking for something like this. A TextBox with AutoComplete with a Contains search instead of a StartsWith.

This rendition is from this WinForms | C# | AutoComplete in the Middle of a Textbox?

I was able to make this, here is my version of an autocomplete that uses Contains. I hope everyone finds this usable.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace TowInvoicing
{
    //from https://stackoverflow.com/questions/1437002/winforms-c-sharp-autocomplete-in-the-middle-of-a-textbox
    public class AutoCompleteTextBox : TextBox
    {
        private ListBox _listBox;
        private bool _isAdded;
        private String[] _values;
        private String _formerValue = String.Empty;

        public AutoCompleteTextBox()
        {
            InitializeComponent();
            ResetListBox();
        }

        private void InitializeComponent()
        {
            _listBox = new ListBox();
            this.KeyDown += this_KeyDown;
            this.KeyUp += this_KeyUp;
        }

        private void ShowListBox()
        {
            if (!_isAdded)
            {
                Parent.Controls.Add(_listBox);
                _listBox.Left = Left;
                _listBox.Top = Top + Height;
                _isAdded = true;
            }
            _listBox.Visible = true;
            _listBox.BringToFront();
        }

        private void ResetListBox()
        {
            _listBox.Visible = false;
        }

        private void this_KeyUp(object sender, KeyEventArgs e)
        {
            UpdateListBox();
        }

        private void this_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Enter:
                case Keys.Tab:
                    {
                        if (_listBox.Visible)
                        {
                            Text = _listBox.SelectedItem.ToString();
                            ResetListBox();
                            _formerValue = Text;
                            this.Select(this.Text.Length, 0);
                            e.Handled = true;
                        }
                        break;
                    }
                case Keys.Down:
                    {
                        if ((_listBox.Visible) && (_listBox.SelectedIndex < _listBox.Items.Count - 1))
                            _listBox.SelectedIndex++;
                        e.Handled = true;
                        break;
                    }
                case Keys.Up:
                    {
                        if ((_listBox.Visible) && (_listBox.SelectedIndex > 0))
                            _listBox.SelectedIndex--;
                        e.Handled = true;
                        break;
                    }


            }
        }

        protected override bool IsInputKey(Keys keyData)
        {
            switch (keyData)
            {
                case Keys.Tab:
                    if (_listBox.Visible)
                        return true;
                    else
                        return false;
                default:
                    return base.IsInputKey(keyData);
            }
        }

        private void UpdateListBox()
        {
            if (Text == _formerValue)
                return;

            _formerValue = this.Text;
            string word = this.Text;

            if (_values != null && word.Length > 0)
            {
                string[] matches = Array.FindAll(_values,
                                                 x => (x.ToLower().Contains(word.ToLower())));
                if (matches.Length > 0)
                {
                    ShowListBox();
                    _listBox.BeginUpdate();
                    _listBox.Items.Clear();
                    Array.ForEach(matches, x => _listBox.Items.Add(x));
                    _listBox.SelectedIndex = 0;
                    _listBox.Height = 0;
                    _listBox.Width = 0;
                    Focus();
                    using (Graphics graphics = _listBox.CreateGraphics())
                    {
                        for (int i = 0; i < _listBox.Items.Count; i++)
                        {
                            if (i < 20)
                                _listBox.Height += _listBox.GetItemHeight(i);
                            // it item width is larger than the current one
                            // set it to the new max item width
                            // GetItemRectangle does not work for me
                            // we add a little extra space by using '_'
                            int itemWidth = (int)graphics.MeasureString(((string)_listBox.Items[i]) + "_", _listBox.Font).Width;
                            _listBox.Width = (_listBox.Width < itemWidth) ? itemWidth : this.Width;;
                        }
                    }
                    _listBox.EndUpdate();
                }
                else
                {
                    ResetListBox();
                }
            }
            else
            {
                ResetListBox();
            }
        }

        public String[] Values
        {
            get
            {
                return _values;
            }
            set
            {
                _values = value;
            }
        }

        public List<String> SelectedValues
        {
            get
            {
                String[] result = Text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                return new List<String>(result);
            }
        }

    }

}
Community
  • 1
  • 1
Omzig
  • 861
  • 1
  • 12
  • 20
1

the list you are search in try use the condition to get in list or not

stringItemInList.IndexOf("txtwhatyousearchfor",StringComparison.OrdinalIgnoreCase) != -1

if you connect to a database use in you query

parameter LIKE '%txthere%'

Regards

UPDATE

after you comment

The best option I see would be to create your own implementation of IAutoComplete. Here is info on it: http://msdn.microsoft.com/en-us/library/bb776292%28VS.85%29.aspx

Also you can do some searches for some code samples of people creating their own IAutoComplete implementations.

Regards

Mhmd
  • 4,989
  • 3
  • 22
  • 29
  • Thanks, but that's not exactly what I'm looking for. It's not difficult to manually compile the list of suitable results. However, there seems to no access to the function one would have to overload to substitute its default search routine for this one. That was my primary difficulty. – Phonon Apr 12 '11 at 13:15
  • if you do not mind to tell the function code, to help you better – Mhmd Apr 12 '11 at 13:18
  • There is no function code. It's a `TextBox` with autocomplete enabled. from what I've realized, there are no ways to do it other than creating your own control. – Phonon Apr 13 '11 at 16:29
  • sorry for misunderstanding, i am using my own control, and i expexted you asking about it ... i recommend a solution as edit in answer, regards – Mhmd Apr 13 '11 at 22:37