Customize TextBox autocomplete
Looking at your code you have everything you need but 1 line of code. That line is:
This will only work if the start of a string is entered
//Suggestion only
textBoxName.AutoCompleteMode = AutoCompleteMode.Suggest;
//Suggest and autocomplete
textBoxName.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
This will work as a contains
method but works with a custom control
You can also make a custom textbox control that fits your needs.
Custom textbox class:
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);
}
}
}
Usage:
string[] nameArray = { "name1", "name2", "name3", "bla name" };
AutoCompleteTextBox tb = new AutoCompleteTextBox();
tb.Values = nameArray;
tb.Location = new Point(10,10);
tb.Size = new Size(25,75);
this.Controls.Add( tb );
I got the code for the custom control from: SO Question - Autocomplete contains
How can I get C# textbox autocomplete to make suggestions based on contains rather than starts with?
This is not possible with the built-in TextBox.
Reading the reference source reveals that the AutoComplete feature calls into the native function SHAutoComplete to do it's job. That one does only prefix matching.
You would have to subclass TextBox and provide your own suggestion feature (or purchase one / find an open source one), to do this.
C# WinForms Vertical Alignment for TextBox, etc
If you're turning off AutoSize
on a control, it must be a Label
, since TextBox
doesn't have an AutoSize
property. The TextAlign
property of a Label
is of type ContentAligment
, so you can set both horizontal and vertical alignment.
For various boring reasons, TextBoxes
in Windows are intended to auto-adjust their heights to the font used. To control the height and vertically center the text, you can quickly create a custom UserControl
, that you can use for replacing all your TextBoxes
with.
On your UserControl
, set the BorderStyle
to Fixed3D
and the BackColor
to System.Window
. Add a TextBox
and set its BorderStyle
to None
. In the Resize event for the control, add code that makes the TextBox
the same width as the user control's client area (accounting for the border pixels) and left-aligns it (i.e. textBox1.Left = 0;
) and vertically centers it (e.g. textBox1.Top = (this.Height - textBox1.Height) / 2;
).
Finally, add to the user control any TextBox
-type properties and events you need (probably just Text and TextChanged
, I would guess), and wire them up so that they pass through to the TextBox
inside your control, like this:
public string Text
{
get => textBox1.Text;
set => textBox1.Text = value;
}
If you wanted to get super-fancy with this, you could even replace your user control's TextAlign
property with one that is actually of type ContentAlignment
(like the Label) and then align the inner TextBox
to match.
This same approach works for a ComboBox
, although it will look slightly odd. With the ComboBox
, you set its FlatStyle
property to Flat - otherwise you deal with it the same as a TextBox
. It will look odd because the drop-down arrow box won't be quite at the top and bottom of the panel.
Related Topics
.Net (C#): Getting Child Windows When You Only Have a Process Handle or Pid
Identityserver4 Role Based Authorization for Web API with ASP.NET Core Identity
Accessing UI Control from Backgroundworker Thread
Force Download of a File on Web Server - Asp .Net C#
How to Get a Uri of the Image Stored in the Resources
JSON.Net Serialize Specific Private Field
Server Execution Failed (Exception from Hresult: 0X80080005 (Co_E_Server_Exec_Failure))
Passing Object in Redirecttoaction
How to Find Out When You'Ve Been Loaded via Xml Serialization
Lambda Expression Not Returning Expected Memberinfo
Query Extremely Slow in Code But Fast in Ssms
How to Pass Parameters to the Custom Action
Why Isn't Arraylist Marked [Obsolete]
Why Appdomain.Currentdomain.Basedirectory Not Contains "Bin" in ASP.NET App
How to Find the State of Numlock, Capslock and Scrolllock in .Net
Capturing Binary Output from Process.Standardoutput
Mono Https Webrequest Fails with "The Authentication or Decryption Has Failed"