/*
* Made by Lonami, the 01/12/2014 at 18:30
* (C) LonamiWebs
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
public class AutoComplete {
#region Private variables
readonly TextBox TB;
readonly Form form = new Form {
Size = new Size(120, 60),
FormBorderStyle = FormBorderStyle.None,
TopMost = true
};
readonly ListBox listbox = new ListBox {
Dock = DockStyle.Fill
};
bool working;
#endregion
#region Public variables
public int Width {
get { return form.Size.Width; }
set { form.Size = new Size(value, form.Size.Height); }
}
public int Height {
get { return form.Size.Height; }
set { form.Size = new Size(form.Size.Width, value); }
}
public Point Offset = new Point(10, 10);
public List<Keys> AutocompleteKeys = new List<Keys>
{ Keys.Enter, Keys.Tab, Keys.Space, Keys.OemPeriod };
List<string> Suggestions = new List<string>();
public List<string> GetSuggestions() {
return Suggestions;
}
public void AddSuggestion(string suggestion) {
if (!Suggestions.Contains(suggestion))
Suggestions.Add(suggestion);
}
public void AddSuggestions(IEnumerable<string> suggestions) {
foreach (var suggestion in suggestions)
AddSuggestion(suggestion);
}
public int MaximumSuggestions = 50;
#endregion
#region Initialize
public AutoComplete(TextBox textboxToAsign) {
TB = textboxToAsign;
TB.KeyDown += textboxToAsign_KeyDown;
TB.KeyUp += (s, e) => RefreshSuggestList(true);
TB.Click += (s, e) => HideSuggests();
TB.LostFocus += TB_LostFocus;
TB.LocationChanged += (s, e) => RefreshLocation();
var parent = TB.Parent;
while (parent != null) {
parent.LocationChanged += (s, e) => RefreshLocation();
parent = parent.Parent;
}
listbox.MouseEnter += (s, e) => working = true;
listbox.MouseLeave += (s, e) => working = false;
listbox.MouseDoubleClick += (s, e) => SelectSuggestion();
form.Controls.Add(listbox);
form.GotFocus += (s, e) => TB.Focus();
}
#endregion
#region Show/hide form
void textboxToAsign_KeyDown(object sender, KeyEventArgs e)
{
working = true;
if (e.KeyCode == Keys.Up) {
HandleKEA(ref e);
MoveUp();
}
else if (e.KeyCode == Keys.Down) {
HandleKEA(ref e);
MoveDown();
}
else if (AutocompleteKeys.Contains(e.KeyCode)) {
HandleKEA(ref e);
SelectSuggestion(e.KeyCode);
}
else if (!e.Control && IsLetterOrDigit(e.KeyCode, e.Shift)) {
RefreshSuggestList();
ShowSuggests();
}
else
HideSuggests();
working = false;
}
void HideSuggests() {
form.Hide();
}
void ShowSuggests() {
if (listbox.Items.Count > 0) {
form.Show();
RefreshLocation();
TB.Focus();
} else
HideSuggests();
}
void RefreshLocation() {
Point p = TB.GetPositionFromCharIndex(TB.SelectionStart > 0 ? TB.SelectionStart - 1 : 0);
form.Location = TB.PointToScreen(new Point(p.X + Offset.X, p.Y + Offset.Y));
}
void TB_LostFocus(object sender, EventArgs e) {
if (!working)
HideSuggests();
}
#endregion
#region Suggestions
void RefreshSuggestList(bool keepindex = false) {
int lindex = keepindex ? listbox.SelectedIndex : 0;
listbox.BeginUpdate();
listbox.Items.Clear();
string[] w = GetSuggestions(GetLastWord());
string[] x = null;
if (w.Length > MaximumSuggestions) {
x = new string[MaximumSuggestions];
for (int i = 0; i < MaximumSuggestions; i++)
x[i] = w[i];
}
listbox.Items.AddRange(x ?? w);
if (listbox.Items.Count > lindex)
listbox.SelectedIndex = lindex;
else if (listbox.Items.Count > 0)
listbox.SelectedIndex = 0;
else {
listbox.SelectedIndex = -1;
HideSuggests();
}
listbox.EndUpdate();
}
void MoveDown() {
if (listbox.SelectedIndex + 1 < listbox.Items.Count)
listbox.SelectedIndex++;
}
void MoveUp() {
if (listbox.SelectedIndex > 0)
listbox.SelectedIndex--;
}
void SelectSuggestion(Keys key = Keys.Enter) {
if (!form.Visible)
return;
working = true;
int lindex = TB.SelectionStart;
if (listbox.SelectedIndex > -1) {
string wrd = listbox.SelectedItem.ToString();
switch (key) {
case Keys.Space:
wrd += " ";
break;
case Keys.OemPeriod:
wrd += ".";
break;
}
lindex = ReplaceLastWord(wrd);
}
TB.Focus();
TB.SelectionStart = lindex;
TB.ScrollToCaret();
HideSuggests();
working = false;
}
string[] GetSuggestions(string startingWord) {
return Suggestions.Where(x => x.StartsWith(startingWord, StringComparison.InvariantCultureIgnoreCase)).ToArray();
}
#endregion
#region Utils
string GetLastWord() {
int i = TB.SelectionStart, j = i--;
while (i > 0) {
if (Char.IsLetterOrDigit(TB.Text[i])) i--;
else { i++; break; }
}
return i < 0 ? "" : TB.Text.Substring(i, j - i);
}
int ReplaceLastWord(string replacewith) {
int i = TB.SelectionStart, j = i--;
while (i > 0) {
if (Char.IsLetterOrDigit(TB.Text[i])) i--;
else { i++; break; }
}
TB.Text = i < 0 ? "" : TB.Text.Substring(0, i) +
replacewith + TB.Text.Substring(j, TB.Text.Length - j);
int alreadytyped = j - i;
return j + replacewith.Length - alreadytyped;
}
bool IsLetterOrDigit(Keys key, bool shift) {
int k = (int)key;
return (k > 64 && k < 91) || (!shift && (k > 47 && k < 58));
}
void HandleKEA(ref KeyEventArgs e) { e.Handled = e.SuppressKeyPress = form.Visible; }
#endregion
}
If you want to add Autocomplete feature to any multiline TextBox, this is your piece of code!
Be the first to comment
You can use [html][/html], [css][/css], [php][/php] and more to embed the code. Urls are automatically hyperlinked. Line breaks and paragraphs are automatically generated.