I have a textbox that does autocompletion like so:
txtName.AutoCompleteMode = AutoCompleteMode.Suggest;
txtName.AutoCompleteSource = AutoCompleteSource.CustomSource;
txtName.AutoCompleteCustomSource = namesCollection;
It works, but only at the beginning of a textbox. I'd like autocomplete to kick in for any word the user is entering, at any position in the textbox.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace TubeUploader
{
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();
KeyDown += this_KeyDown;
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.Tab:
{
if (_listBox.Visible)
{
InsertWord((String)_listBox.SelectedItem);
ResetListBox();
_formerValue = Text;
}
break;
}
case Keys.Down:
{
if ((_listBox.Visible) && (_listBox.SelectedIndex < _listBox.Items.Count - 1))
_listBox.SelectedIndex++;
break;
}
case Keys.Up:
{
if ((_listBox.Visible) && (_listBox.SelectedIndex > 0))
_listBox.SelectedIndex--;
break;
}
}
}
protected override bool IsInputKey(Keys keyData)
{
switch (keyData)
{
case Keys.Tab:
return true;
default:
return base.IsInputKey(keyData);
}
}
private void UpdateListBox()
{
if (Text == _formerValue) return;
_formerValue = Text;
String word = GetWord();
if (_values != null && word.Length > 0)
{
String[] matches = Array.FindAll(_values,
x => (x.StartsWith(word, StringComparison.OrdinalIgnoreCase) && !SelectedValues.Contains(x)));
if (matches.Length > 0)
{
ShowListBox();
_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++)
{
_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 : _listBox.Width;
}
}
}
else
{
ResetListBox();
}
}
else
{
ResetListBox();
}
}
private String GetWord()
{
String text = Text;
int pos = SelectionStart;
int posStart = text.LastIndexOf(' ', (pos < 1) ? 0 : pos - 1);
posStart = (posStart == -1) ? 0 : posStart + 1;
int posEnd = text.IndexOf(' ', pos);
posEnd = (posEnd == -1) ? text.Length : posEnd;
int length = ((posEnd - posStart) < 0) ? 0 : posEnd - posStart;
return text.Substring(posStart, length);
}
private void InsertWord(String newTag)
{
String text = Text;
int pos = SelectionStart;
int posStart = text.LastIndexOf(' ', (pos < 1) ? 0 : pos - 1);
posStart = (posStart == -1) ? 0 : posStart + 1;
int posEnd = text.IndexOf(' ', pos);
String firstPart = text.Substring(0, posStart) + newTag;
String updatedText = firstPart + ((posEnd == -1) ? "" : text.Substring(posEnd, text.Length - posEnd));
Text = updatedText;
SelectionStart = firstPart.Length;
}
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);
}
}
}
}
Sample Usage
using System;
using System.Windows.Forms;
namespace AutoComplete
{
public partial class TestForm : Form
{
private readonly String[] _values = { "one", "two", "three", "tree", "four", "fivee" };
public TestForm()
{
InitializeComponent();
// AutoComplete is our special textbox control on the form
AutoComplete.Values = _values;
}
}
}
I made a few changes to the solution proposed by #PaRiMaL RaJ because the list box was not being displayed when the text box was inside a UserControl that was not tall enough. Basically, instead of adding the list box to the parent of the text box, I added to the form and I calculate the absolute position in the form.
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)
{
Form parentForm = this.FindForm(); // new line added
parentForm.Controls.Add(_listBox); // adds it to the form
Point positionOnForm = parentForm.PointToClient(this.Parent.PointToScreen(this.Location)); // absolute position in the form
_listBox.Left = positionOnForm.X;
_listBox.Top = positionOnForm.Y + 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);
}
}
}
The other solutions didn't work for me in a multiline environment to my needs, so I've added to #Francisco Goldenstein's answer to enable this. What I needed was to autocomplete any 'word' in the TextBox and in any position/line. After minimal testing, this class seems to work well enough for me in a multiline TextBox. Hope it helps someone.
Main changes are in UpdateListBox() and this_KeyDown(), to deal with the 'current' word, i.e. the one just before the caret position, rather than the entire textbox contents.
Change the definition of separators in UpdateListBox() to suit your needs.
using System;
using System.Drawing;
using System.Windows.Forms;
class MultiLineAutoCompleteTextBox : TextBox
{
private ListBox _listBox;
private bool _isAdded;
private String[] _values;
private String _formerValue = String.Empty;
private int _prevBreak;
private int _nextBreak;
private int _wordLen;
public MultiLineAutoCompleteTextBox()
{
InitializeComponent();
ResetListBox();
}
private void InitializeComponent()
{
_listBox = new ListBox();
KeyDown += this_KeyDown;
KeyUp += this_KeyUp;
}
private void ShowListBox()
{
if (!_isAdded)
{
Form parentForm = FindForm();
if (parentForm == null) return;
parentForm.Controls.Add(_listBox);
Point positionOnForm = parentForm.PointToClient(Parent.PointToScreen(Location));
_listBox.Left = positionOnForm.X;
_listBox.Top = positionOnForm.Y + 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:
case Keys.Space:
{
if (_listBox.Visible)
{
Text = Text.Remove(_prevBreak == 0 ? 0 : _prevBreak + 1, _prevBreak == 0 ? _wordLen + 1 : _wordLen);
Text = Text.Insert(_prevBreak == 0 ? 0 : _prevBreak + 1, _listBox.SelectedItem.ToString());
ResetListBox();
_formerValue = Text;
Select(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;
if (Text.Length == 0)
{
_listBox.Visible = false;
return;
}
_formerValue = Text;
var separators = new[] { '|', '[', ']', '\r', '\n', ' ', '\t' };
_prevBreak = Text.LastIndexOfAny(separators, CaretIndex > 0 ? CaretIndex - 1 : 0);
if (_prevBreak < 1) _prevBreak = 0;
_nextBreak = Text.IndexOfAny(separators, _prevBreak + 1);
if (_nextBreak == -1) _nextBreak = CaretIndex;
_wordLen = _nextBreak - _prevBreak - 1;
if (_wordLen < 1) return;
string word = Text.Substring(_prevBreak + 1, _wordLen);
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 : Width; ;
}
}
_listBox.EndUpdate();
}
else
{
ResetListBox();
}
}
else
{
ResetListBox();
}
}
public int CaretIndex => SelectionStart;
public String[] Values
{
get
{
return _values;
}
set
{
_values = value;
}
}
}
Related
Here is a picture of when I run the program. I want the user to be able to only type on the current line. They shouldn't be able to edit the lines above.
Here is the link to the github if you want to downlaod it to use it for yourself. https://github.com/TeddyRoche/Calculator
Here is the code that is controlling the whole program.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Calculator
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
float number_Initial = 0;
float prev_Answer = 0;
List<float> number_List = new List<float>();
List<string> equations = new List<string>();
bool valid = true;
int key_Press = 0;
int maxLines = 0;
string lastLine = "";
public MainWindow()
{
InitializeComponent();
}
private void NumPressedButton(int i)
{
if (valid == false)
{
MessageBox.Show("Please enter only numbers.");
displayText.Text = displayText.Text.Remove(displayText.Text.Length - 1);
}
else
{
if (key_Press < 12)
{
this.displayText.Text += i;
number_Initial = number_Initial * 10 + i;
key_Press++;
}
}
valid = true;
}
private void NumPressedKeyboard(int i)
{
if (valid == false)
{
MessageBox.Show("Please enter only numbers.");
displayText.Text = displayText.Text.Remove(displayText.Text.Length - 1);
}
else
{
if (key_Press < 12)
{
number_Initial = number_Initial * 10 + i;
key_Press++;
}
}
valid = true;
}
private void OutputSymbol(string x)
{
switch (x)
{
case "+":
equations.Add("add");
break;
case "-":
equations.Add("sub");
break;
case "*":
equations.Add("mul");
break;
case "/":
equations.Add("div");
break;
}
}
private void SymbolPressedButton(String x)
{
if (lastLine == "")
{
this.displayText.AppendText("Ans");
number_List.Add(prev_Answer);
this.displayText.AppendText(x);
OutputSymbol(x);
key_Press = key_Press + 4;
}
else
{
if (number_Initial == 0)
{
this.displayText.AppendText(x);
OutputSymbol(x);
key_Press++;
}
else
{
number_List.Add(number_Initial);
this.displayText.AppendText(x);
OutputSymbol(x);
number_Initial = 0;
key_Press++;
}
}
}
private void SymbolPressedKeyboard(String x)
{
if (lastLine == "")
{
this.displayText.AppendText("Ans");
number_List.Add(prev_Answer);
OutputSymbol(x);
key_Press = key_Press + 4;
}
else
{
if (number_Initial == 0)
{
OutputSymbol(x);
key_Press++;
}
else
{
number_List.Add(number_Initial);
OutputSymbol(x);
number_Initial = 0;
key_Press++;
}
}
}
//Display____________________________________________________________________________________________________________________________________________________
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
maxLines = displayText.LineCount;
if(maxLines > 0)
{
lastLine = displayText.GetLineText(maxLines - 1);
}
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
//check to see if what the user presses is a number or one of the appropriate symbols allowed
//if not sets valid to false
valid = true;
if(e.Key < Key.D0 || e.Key > Key.D9)
{
if(e.Key < Key.NumPad0 || e.Key > Key.NumPad9)
{
valid = false;
if (e.Key == Key.Add || e.Key == Key.Subtract || e.Key == Key.Multiply || e.Key == Key.Divide || e.Key == Key.Enter)
{
valid = true;
}
}
}
if(valid == true)
{
//Performing functions when a certain key is pressed
switch (e.Key)
{
case Key.NumPad0:
case Key.D0:
NumPressedKeyboard(0);
break;
case Key.NumPad1:
case Key.D1:
NumPressedKeyboard(1);
break;
case Key.NumPad2:
case Key.D2:
NumPressedKeyboard(2);
break;
case Key.NumPad3:
case Key.D3:
NumPressedKeyboard(3);
break;
case Key.NumPad4:
case Key.D4:
NumPressedKeyboard(4);
break;
case Key.NumPad5:
case Key.D5:
NumPressedKeyboard(5);
break;
case Key.NumPad6:
case Key.D6:
NumPressedKeyboard(6);
break;
case Key.NumPad7:
case Key.D7:
NumPressedKeyboard(7);
break;
case Key.NumPad8:
case Key.D8:
NumPressedKeyboard(8);
break;
case Key.NumPad9:
case Key.D9:
NumPressedKeyboard(9);
break;
case Key.Add:
SymbolPressedKeyboard("+");
break;
case Key.Subtract:
SymbolPressedKeyboard("-");
break;
case Key.Divide:
SymbolPressedKeyboard("/");
break;
case Key.Multiply:
SymbolPressedKeyboard("*");
break;
case Key.Enter:
Equals_Equation();
break;
}
}
else if(valid == false)
{
MessageBox.Show("Please enter only numbers.");
//displayText.Text = displayText.Text.Remove(displayText.Text.Length - 1);
}
}
//Display____________________________________________________________________________________________________________________________________________________
//Numbers ___________________________________________________________________________________________________________________________________________________
private void _0_Click(object sender, RoutedEventArgs e)
{
NumPressedButton(0);
}
private void _1_Click(object sender, RoutedEventArgs e)
{
NumPressedButton(1);
}
private void _2_Click(object sender, RoutedEventArgs e)
{
NumPressedButton(2);
}
private void _3_Click(object sender, RoutedEventArgs e)
{
NumPressedButton(3);
}
private void _4_Click(object sender, RoutedEventArgs e)
{
NumPressedButton(4);
}
private void _5_Click(object sender, RoutedEventArgs e)
{
NumPressedButton(5);
}
private void _6_Click(object sender, RoutedEventArgs e)
{
NumPressedButton(6);
}
private void _7_Click(object sender, RoutedEventArgs e)
{
NumPressedButton(7);
}
private void _8_Click(object sender, RoutedEventArgs e)
{
NumPressedButton(8);
}
private void _9_Click(object sender, RoutedEventArgs e)
{
NumPressedButton(9);
}
//Numbers____________________________________________________________________________________________________________________________________________________
//Equations__________________________________________________________________________________________________________________________________________________
private void Divide_Click(object sender, RoutedEventArgs e)
{
SymbolPressedButton("/");
}
private void Multiply_Click(object sender, RoutedEventArgs e)
{
SymbolPressedButton("*");
}
private void Subtract_Click(object sender, RoutedEventArgs e)
{
SymbolPressedButton("-");
}
private void Add__Click(object sender, RoutedEventArgs e)
{
SymbolPressedButton("+");
}
protected void Equals_Equation()
{
if(equations.Count != 0)
{
if(this.displayText.Text.StartsWith("1") || this.displayText.Text.StartsWith("2") || this.displayText.Text.StartsWith("3") || this.displayText.Text.StartsWith("4") || this.displayText.Text.StartsWith("5") || this.displayText.Text.StartsWith("6") || this.displayText.Text.StartsWith("7") || this.displayText.Text.StartsWith("8") || this.displayText.Text.StartsWith("9") || this.displayText.Text.StartsWith("0"))
{
number_List.Add(number_Initial);
this.displayText.AppendText("\n");
//loop that goes through the equations list and does the appropriate calculations
//Does Multiplication and Division first
for (int s = 0; s < equations.Count(); s++)
{
if (equations[s] == "mul")
{
number_List[s] = number_List[s] * number_List[s + 1];
}
else if (equations[s] == "div")
{
number_List[s] = number_List[s] / number_List[s + 1];
}
}
//Then does Addition and Subtraction next
for (int s = 0; s < equations.Count(); s++)
{
if (equations[s] == "add")
{
number_List[0] = number_List[0] + number_List[s + 1];
}
else if (equations[s] == "sub")
{
number_List[0] = number_List[0] - number_List[s + 1];
}
}
//changes the display to show the answer and creates a new line for the user to continue
this.displayText.Text += number_List[0];
number_Initial = number_List[0];
number_List.Clear();
prev_Answer = number_Initial;
//number_List.Add(number_Initial);
number_Initial = 0;
equations.Clear();
this.displayText.AppendText("\n");
this.displayText.PageDown();
displayText.Select(displayText.Text.Length, 0);
}
else if (this.displayText.Text.StartsWith("A"))
{
number_List.Insert(0, prev_Answer);
number_List.Add(number_Initial);
this.displayText.AppendText("\n");
//loop that goes through the equations list and does the appropriate calculations
//Does Multiplication and Division first
for (int s = 0; s < equations.Count(); s++)
{
if (equations[s] == "mul")
{
number_List[s] = number_List[s] * number_List[s + 1];
}
else if (equations[s] == "div")
{
number_List[s] = number_List[s] / number_List[s + 1];
}
}
//Then does Addition and Subtraction next
for (int s = 0; s < equations.Count(); s++)
{
if (equations[s] == "add")
{
number_List[0] = number_List[0] + number_List[s + 1];
}
else if (equations[s] == "sub")
{
number_List[0] = number_List[0] - number_List[s + 1];
}
}
//changes the display to show the answer and creates a new line for the user to continue
this.displayText.Text += number_List[0];
number_Initial = number_List[0];
number_List.Clear();
prev_Answer = number_Initial;
//number_List.Add(number_Initial);
number_Initial = 0;
equations.Clear();
this.displayText.AppendText("\n");
this.displayText.PageDown();
displayText.Select(displayText.Text.Length, 0);
}
}
else
{
}
key_Press = 0;
}
private void Equals_Click(object sender, RoutedEventArgs e)
{
Equals_Equation();
}
//Clears all stored data so the user can start from scratch
private void Clear_Click(object sender, RoutedEventArgs e)
{
number_List.Clear();
number_Initial = 0;
equations.Clear();
this.displayText.Clear();
key_Press = 0;
}
//Equations__________________________________________________________________________________________________________________________________________________
}
}
I havent found much that has helped with only allowing the user to edit the current line. I see a lot with not allowing the user to edit the whole textBox.
I want the user to only be able to edit the current line. Right know I can use the arrow keys or click with my mouse on the previous lines and can edit them and I dont want them to be allowed to do this.
Here is a quick example. Hopefully this gives you enough info.
XAML
<TextBox
AcceptsReturn="True"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto"
PreviewTextInput="TextBox_PreviewTextInput"/>
Code Behind
private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
if (sender is not TextBox box) //semi-redundant safety check
return;
//make sure there is text, also make sure there is a newline in the box
if (string.IsNullOrEmpty(box.Text) || (!box.Text.Contains('\n') && !box.Text.Contains('\r')))
return;
//through some testing I found the \r would be there without a \n so I include both for completeness
var lastReturn = Math.Max(box.Text.LastIndexOf('\r'), box.Text.LastIndexOf('\n'));
if (box.CaretIndex <= lastReturn)
e.Handled = true;
}
This solution can be expanded on, but it mainly prevents the Text from changing whenever Text is entered on any line but the last. I like it as you can also move the Text Caret around to get standard functionality still (highlighting, selection, etc.)
This is a link to the winforms project from my onedrive:
https://1drv.ms/u/s!AmVw2eqP6FhB4h01pNXIMlqdZJk_?e=wSGaRx
using Newtonsoft.Json;
using Ookii.Dialogs.WinForms;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.ProgressBar;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Window;
namespace Image_Crop
{
public partial class Form1 : Form
{
Rectangle rect;
int pixelsCounter = 0;
Color SelectedColor = Color.LightGreen;
List<DrawingRectangle> DrawingRects = new List<DrawingRectangle>();
Bitmap rectImage;
int saveRectanglesCounter = 1;
bool drawBorder = true;
bool clearRectangles = true;
bool saveRectangles = true;
//bool drawnNewRectangle = false; // To think how to work with this flag bool variable
// where to use it how and how to solce this problem !!!!!
string rectangleName;
Dictionary<string, string> FileList = new Dictionary<string, string>();
string selectedPath;
int x, y;
private bool crop = false;
public Form1()
{
InitializeComponent();
textBox1.Text = Properties.Settings.Default.ImageToCropFolder;
textBox2.Text = Properties.Settings.Default.CroppedImagesFolder;
selectedPath = textBox2.Text;
if (textBox1.Text != "")
{
Bitmap bmp = new Bitmap(Image.FromFile(textBox1.Text),
pictureBox2.Width, pictureBox2.Height);
pictureBox2.Image = bmp;
}
checkBoxDrawBorder.Checked = true;
checkBoxClearRectangles.Checked = true;
checkBoxSaveRectangles.Checked = true;
if (selectedPath != "" && selectedPath != null)
{
if (System.IO.File.Exists(Path.Combine(selectedPath, "rectangles.txt")))
{
string g = System.IO.File.ReadAllText(Path.Combine(selectedPath, "rectangles.txt"));
g = g.Remove(0, 32);
FileList = JsonConvert.DeserializeObject<Dictionary<string, string>>(g);
listBox1.DataSource = FileList.Keys.ToList();
label2.Text = listBox1.Items.Count.ToString();
listBox1.SelectedIndex = listBox1.Items.Count - 1;
}
else
{
label2.Text = "0";
}
}
else
{
label2.Text = "0";
}
if ((selectedPath != "" && selectedPath != null) && textBox1.Text != "")
{
crop = true;
}
else
{
crop = false;
}
}
public class DrawingRectangle
{
public Rectangle Rect => new Rectangle(Location, Size);
public Size Size { get; set; }
public Point Location { get; set; }
public Control Owner { get; set; }
public Point StartPosition { get; set; }
public Color DrawingcColor { get; set; } = Color.LightGreen;
public float PenSize { get; set; } = 3f;
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void pictureBox2_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left || crop == false) return;
x = 0;
y = 0;
if (pictureBox2.Image != null && selectedPath != null)
{
if ((x >= 0 && x <= pictureBox2.Image.Size.Width) && (y >= 0 && y <= pictureBox2.Image.Size.Height))
{
DrawingRects.Add(new DrawingRectangle()
{
Location = e.Location,
Size = Size.Empty,
StartPosition = e.Location,
Owner = (Control)sender,
DrawingcColor = SelectedColor
});
}
}
}
private void pictureBox2_MouseMove(object sender, MouseEventArgs e)
{
int X = e.X;
int Y = e.Y;
if (e.Button != MouseButtons.Left || crop == false) return;
if ((X >= 0 && X <= pictureBox2.Width) && (Y >= 0 && Y <= pictureBox2.Height))
{
if (pictureBox2.Image != null && selectedPath != null && DrawingRects.Count > 0)
{
if ((x >= 0 && x <= pictureBox2.Image.Size.Width) && (y >= 0 && y <= pictureBox2.Image.Size.Height))
{
x = e.X;
y = e.Y;
var dr = DrawingRects[DrawingRects.Count - 1];
if (e.Y < dr.StartPosition.Y) { dr.Location = new Point(dr.Rect.Location.X, e.Y); }
if (e.X < dr.StartPosition.X) { dr.Location = new Point(e.X, dr.Rect.Location.Y); }
dr.Size = new Size(Math.Abs(dr.StartPosition.X - e.X), Math.Abs(dr.StartPosition.Y - e.Y));
pictureBox2.Invalidate();
}
}
}
}
int count = 0;
private void pictureBox2_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left || crop == false) return;
if (DrawingRects.Count > 0 && pictureBox2.Image != null && selectedPath != "")
{
if ((x >= 0 && x <= pictureBox2.Image.Size.Width) && (y >= 0 && y <= pictureBox2.Image.Size.Height))
{
var dr = DrawingRects.Last();
if (dr.Rect.Width > 0 && dr.Rect.Height > 0)
{
rectImage = cropAtRect((Bitmap)pictureBox2.Image, dr.Rect);
if (saveRectangles)
{
count++;
rectangleName = GetNextName(Path.Combine(selectedPath, "Rectangle"), ".bmp");
FileList.Add($"{dr.Location}, {dr.Size}", rectangleName);
string json = JsonConvert.SerializeObject(
FileList,
Formatting.Indented
);
using (StreamWriter sw = new StreamWriter(Path.Combine(selectedPath, "rectangles.txt"), false))
{
sw.WriteLine("Total number of rectangles: " + count + Environment.NewLine);
sw.Write(json);
sw.Close();
}
rectImage.Save(rectangleName);
saveRectanglesCounter++;
}
pixelsCounter = rect.Width * rect.Height;
pictureBox1.Invalidate();
listBox1.DataSource = FileList.Keys.ToList();
listBox1.SelectedIndex = listBox1.Items.Count - 1;
pictureBox2.Focus();
Graphics g = Graphics.FromImage(this.pictureBox1.Image);
g.Clear(this.pictureBox1.BackColor);
}
}
else
{
if (clearRectangles)
{
DrawingRects.Clear();
pictureBox2.Invalidate();
}
x = 0;
y = 0;
}
}
}
string GetNextName(string baseName, string extension)
{
int counter = 1;
string nextName = baseName + counter + extension;
while (System.IO.File.Exists(nextName))
{
counter++;
nextName = baseName + counter + extension;
}
return nextName;
}
private void pictureBox2_Paint(object sender, PaintEventArgs e)
{
if (drawBorder)
{
ControlPaint.DrawBorder(e.Graphics, pictureBox2.ClientRectangle, Color.Red, ButtonBorderStyle.Solid);
}
if (pictureBox2.Image != null && selectedPath != null && DrawingRects.Count > 0)
{
DrawShapes(e.Graphics);
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (drawBorder)
{
ControlPaint.DrawBorder(e.Graphics, pictureBox1.ClientRectangle, Color.Red, ButtonBorderStyle.Solid);
}
if (rectImage != null && DrawingRects.Count > 0)
{
var dr = DrawingRects.Last();
e.Graphics.DrawImage(rectImage, dr.Rect);
if (clearRectangles)
{
DrawingRects.Clear();
pictureBox2.Invalidate();
}
}
}
private void DrawShapes(Graphics g)
{
if (DrawingRects.Count == 0) return;
g.SmoothingMode = SmoothingMode.AntiAlias;
foreach (var dr in DrawingRects)
{
if (dr.Rect.Width > 0 && dr.Rect.Height > 0)
{
using (Pen pen = new Pen(dr.DrawingcColor, dr.PenSize))
{
g.DrawRectangle(pen, dr.Rect);
};
}
}
}
public Bitmap cropAtRect(Bitmap b, Rectangle r)
{
Bitmap nb = new Bitmap(r.Width, r.Height);
using (Graphics g = Graphics.FromImage(nb))
{
g.DrawImage(b, -r.X, -r.Y);
return nb;
}
}
private void checkBoxDrawBorder_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxDrawBorder.Checked)
{
drawBorder = true;
}
else
{
drawBorder = false;
}
pictureBox1.Invalidate();
pictureBox2.Invalidate();
}
private void checkBoxClearRectangles_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxClearRectangles.Checked)
{
clearRectangles = true;
}
else
{
clearRectangles = false;
}
pictureBox2.Invalidate();
}
private void checkBoxSaveRectangles_CheckedChanged(object sender, EventArgs e)
{
if(checkBoxSaveRectangles.Checked)
{
saveRectangles = true;
}
else
{
saveRectangles = false;
}
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
var item = ((ListBox)sender).SelectedItem;
var val = FileList[(string)item];
pictureBox1.Image = System.Drawing.Image.FromFile(val);
}
private void button1_Click(object sender, EventArgs e)
{
VistaOpenFileDialog dialog = new VistaOpenFileDialog();
{
dialog.Filter = "Images (*.jpg, *.bmp, *.gif)|*.jpg;*.bmp;*.gif";
};
if (dialog.ShowDialog() == DialogResult.OK)
{
textBox1.Text = dialog.FileName;
Properties.Settings.Default.ImageToCropFolder = dialog.FileName;
Properties.Settings.Default.Save();
Bitmap bmp = new Bitmap(Image.FromFile(dialog.FileName),
pictureBox2.Width, pictureBox2.Height);
pictureBox2.Image = bmp;
if(textBox1.Text != "" && textBox2.Text != "")
{
crop = true;
}
}
}
private void button2_Click(object sender, EventArgs e)
{
VistaFolderBrowserDialog dialog = new VistaFolderBrowserDialog();
if (dialog.ShowDialog() == DialogResult.OK)
{
textBox2.Text = dialog.SelectedPath;
selectedPath = dialog.SelectedPath;
Properties.Settings.Default.CroppedImagesFolder = selectedPath;
Properties.Settings.Default.Save();
if (textBox1.Text != "" && textBox2.Text != "")
{
crop = true;
}
}
}
}
}
The problem is in the mouse up event i can't find the logic with the saving flag saveRectangles.
this is the saving part in the mouse up
if (saveRectangles)
{
count++;
rectangleName = GetNextName(Path.Combine(selectedPath, "Rectangle"), ".bmp");
FileList.Add($"{dr.Location}, {dr.Size}", rectangleName);
string json = JsonConvert.SerializeObject(
FileList,
Formatting.Indented
);
using (StreamWriter sw = new StreamWriter(Path.Combine(selectedPath, "rectangles.txt"), false))
{
sw.WriteLine("Total number of rectangles: " + count + Environment.NewLine);
sw.Write(json);
sw.Close();
}
rectImage.Save(rectangleName);
saveRectanglesCounter++;
}
if the checkBox checkBoxSaveRectangles is not checked when running the application and the folder in textBox1 is empty there are no saved images yet on the hard disk then it will throw exception at the line 201 in the MouseUp event :
Graphics g = Graphics.FromImage(this.pictureBox1.Image);
because the image in pictureBox1 is null.
but in that case i still want it to crop images and add the images information as items to the listBox just not to save it to the hard disk.
i think that rectImage variable is also used in the pictureBox1 paint event.
anyway the goal is to be able to keep cropping even if the saving checkbox not checked and the there are no saved images in the cropped images folder.
I have this simple snake game, my problem is that the tails wont add when it reaches three tails.
namespace Snake
{
public partial class Form1 : Form
{
bool left = false, right = false;
bool top = false, down = false;
PictureBox pic = new PictureBox();
List<PictureBox> tails = new List<PictureBox>();
int score = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
if (((e.KeyChar.ToString() == "a") || (e.KeyChar.ToString() == "A"))&&(right == false))
{
right = false;
top = false;
down = false;
left = true;
}
else if (((e.KeyChar.ToString() == "d") || (e.KeyChar.ToString() == "D"))&& (left == false))
{
top = false;
down = false;
left = false;
right = true;
}
else if (((e.KeyChar.ToString() == "w") || (e.KeyChar.ToString() == "W"))&& (down == false))
{
down = false;
left = false;
right = false;
top = true;
}
else if (((e.KeyChar.ToString() == "s") || (e.KeyChar.ToString() == "S"))&& (top == false))
{
top = false;
left = false;
right = false;
down = true;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
//ticks every 1 sec
if (pic.Location == head.Location)
{
score++;
spawnFood();
tails.Add(addTails());
}
sortLocation();
if (right == true)
{
int r = head.Location.X + head.Height;
head.Location = new Point(r, head.Location.Y);
}
else if(left == true)
{
int l = head.Location.X - head.Height;
head.Location = new Point(l, head.Location.Y);
}
else if (top == true)
{
int t = head.Location.Y - head.Height;
head.Location = new Point(head.Location.X, t);
}
else if (down == true)
{
int d = head.Location.Y + head.Height;
head.Location = new Point(head.Location.X,d);
}
txtScore.Text = score.ToString();
}
private void sortLocation()
{
if (tails.Count == 0)
{
}
else
{
for (int i = 1; i < tails.Count; i++)
{
tails[i].Location = tails[i-1].Location;
}
tails[0].Location = head.Location;
}
}
private PictureBox addTails()
{
PictureBox tail = new PictureBox();
tail.Name = "tail" + score.ToString();
tail.BackColor = Color.Black;
tail.Width = 10;
tail.Height = 10;
this.Controls.Add(tail);
return tail;
}
private void spawnFood()
{
Random rnd = new Random();
int rndLocationX = rnd.Next(10, 50);
int rndLocationY = rnd.Next(10, 50);
pic.BackColor = Color.Red;
pic.Height = 10;
pic.Width = 10;
this.Controls.Add(pic);
if (rndLocationX >= 500)
{
rndLocationX -= 10;
}
if (rndLocationY >= 500)
{
rndLocationY -= 10;
}
pic.Location = new Point(rndLocationX*10,rndLocationY*10);
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Start();
spawnFood();
}
}
}
for (int i = 1; i < tails.Count; i++)
{
tails[i].Location = tails[i+1].Location;
}
tails[0].Location = head.Location;
Are your tails there just stacked so to speak? That's another thought I might be thinking. I changed the tails[i-1] to tails [i+1]. Check if that does the trick.
i want to implement combobox (search anywhere within list, not just first letter). i saw this here but didn't help me.
i using following code
private void cmbMenuName_TextChanged(object sender, EventArgs e)
{
DataTable dt = dtMenuOriginal;//dtMenuOriginal-Orginal data
dt.DefaultView.RowFilter = string.Format("MenuName LIKE '%{0}%'", cmbMenuName.Text);
cmbMenuName.DataSource = dt;
}
this code is only working for first character Press please tell me on which event i will call or any other way to solve this
I use this Link which is useful for auto-complete Textbox control.
My code-
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;
namespace Combo_MiddleText_AutocompleteDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//don't forget to create/initialize our own autocomplete listbox
this.InitializeAutoComplete();
}
private void InitializeAutoComplete()
{
this.SuspendLayout();
//Create a new listbox that will be shown for the auto-complete
//
// _listBox
//
_listBox = new System.Windows.Forms.ListBox();
_listBox.Visible = false;
_listBox.Location = new System.Drawing.Point(0, 0);
_listBox.Name = "_listBox";
_listBox.Size = new System.Drawing.Size(120, 96);
_listBox.TabIndex = 0;
_listBox.MouseDown += new System.Windows.Forms.MouseEventHandler(this._listBox_MouseDown);
//
// AutoCompleteTextBox - attach some events
//
cmbMenuName.KeyDown += new System.Windows.Forms.KeyEventHandler(this.cmbMenuName_KeyDown);
cmbMenuName.KeyUp += new System.Windows.Forms.KeyEventHandler(this.cmbMenuName_KeyUp);
this.ResumeLayout(false);
}
#region Variables
Hashtable hash;
private String[] _values;
private ListBox _listBox;
private bool _isAdded;
private String _formerValue = String.Empty;
#endregion Variables
private void Form1_Load(object sender, EventArgs e)
{
FillGrid(125);// Menu combobox in Quotation Menu Grid
cmbMenuName.DroppedDown = false;
}
public DataTable FillGrid(int QueryNo)
{
try
{
hash = new Hashtable();
DataTable dtReturn = new DataTable();
hash.Add("#QueryNo", QueryNo);
//dtReturn= ClsDefination.FillData()
dtReturn = ClsDefination.FillData("[usp_Transaction_QuotationMaster_Select]", hash);
if ((dtReturn != null && dtReturn.Rows.Count > 0))// || dtReturn2 != null && dtReturn2.Rows.Count > 0)
{
DataRow objRow = dtReturn.Rows[0];
if (QueryNo == 125) // Menu Name
{
cmbMenuName.DataSource = dtReturn;
cmbMenuName.DisplayMember = "MenuName";
cmbMenuName.ValueMember = "MenuID";
cmbMenuName.Text = "";
List<string> list = new List<string>();
for (int i = 0; i < dtReturn.Rows.Count; i++)
{
DataRow dr = dtReturn.Rows[i];
list.Add(dr["MenuName"].ToString());
}
_values = list.ToArray();
}
}
else
{
}
return null;
}
catch (Exception)
{
return null;
throw;
}
}
private void cmbMenuName_KeyDown(object sender, KeyEventArgs e)
{
//react the arrow up and down
switch (e.KeyCode)
{
case Keys.Down:
{
if ((_listBox.Visible) && (_listBox.SelectedIndex < _listBox.Items.Count - 1))
{
_listBox.SelectedIndex++;
}
break;
}
case Keys.Up:
{
if ((_listBox.Visible) && (_listBox.SelectedIndex > 0))
{
_listBox.SelectedIndex--;
}
break;
}
}
}
private void cmbMenuName_KeyUp(object sender, KeyEventArgs e)
{
//whenever a key is pressed, update the listbox content
UpdateListBox();
}
private void _listBox_MouseDown(object sender, MouseEventArgs e)
{
//select the item with the mouse
if (_listBox.Visible)
{
InsertWord((String)_listBox.SelectedItem);
HideListBox();
_formerValue = cmbMenuName.Text;
}
}
private void HideListBox()
{
//Hide the listbox
_listBox.Visible = false;
}
//private void btnShow_Click(object sender, EventArgs e)
//{
// //when this button is clicked,
// //we take the values from the textbox and list them into the second listbox
// lstSelectedValues.Items.Clear();
// List<String> selectedValues = SelectedValues;
// Array.ForEach(selectedValues.ToArray(),
// selectedValue => lstSelectedValues.Items.Add(selectedValue.Trim()));
//}
public List<String> SelectedValues
{
get
{
//return the list of selection
String[] result = cmbMenuName.Text.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
return new List<String>(result);
}
}
private void UpdateListBox()
{
//fill the listbox with the new filtered values
if (cmbMenuName.Text != _formerValue)
{
_formerValue = cmbMenuName.Text;
String word = GetWord();
if (word.Length > 0)
{
//case insensitive search
String[] matches = Array.FindAll(_values, x => (x.ToUpper().Contains(word.ToUpper()) && !SelectedValues.Contains(x)));
if (matches.Length > 0)
{
ShowListBox();
_listBox.Items.Clear();
Array.ForEach(matches, x => _listBox.Items.Add(x));
_listBox.SelectedIndex = 0;
_listBox.Height = 0;
//at least the same width as the textbox
_listBox.Width = cmbMenuName.Width;
// cmbMenuName.Focus();
using (Graphics graphics = _listBox.CreateGraphics())
{
for (int i = 0; i < _listBox.Items.Count; i++)
{
_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 : _listBox.Width;
}
}
}
else
{
HideListBox();
}
}
else
{
HideListBox();
}
}
}
private void ShowListBox()
{
//display the listbox just below the textbox
if (!_isAdded)
{
this.Controls.Add(_listBox);
_listBox.Left = cmbMenuName.Left;
_listBox.Top = cmbMenuName.Top + cmbMenuName.Height;
_isAdded = true;
}
_listBox.Visible = true;
}
private String GetWord()
{
//get the current word (useful when there is more than one on the textbox)
String text = cmbMenuName.Text;
int pos = cmbMenuName.SelectionStart;
int posStart = text.LastIndexOf(';', (pos < 1) ? 0 : pos - 1);
posStart = (posStart == -1) ? 0 : posStart + 1;
int posEnd = text.IndexOf(';', pos);
posEnd = (posEnd == -1) ? text.Length : posEnd;
int length = ((posEnd - posStart) < 0) ? 0 : posEnd - posStart;
return text.Substring(posStart, length);
}
private void InsertWord(String newTag)
{
//add the new selection to the textbox
String text = cmbMenuName.Text;
int pos = cmbMenuName.SelectionStart;
int posStart = text.LastIndexOf(';', (pos < 1) ? 0 : pos - 1);
posStart = (posStart == -1) ? 0 : posStart + 1;
int posEnd = text.IndexOf(';', pos);
String firstPart = text.Substring(0, posStart) + newTag;
String updatedText = firstPart + ((posEnd == -1) ? "" : text.Substring(posEnd, text.Length - posEnd));
cmbMenuName.Text = updatedText;
cmbMenuName.SelectionStart = firstPart.Length;
}
private void cmbMenuName_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if ((e.KeyCode == Keys.Tab) && (_listBox.Visible))
{
InsertWord((String)_listBox.SelectedItem);
HideListBox();
_formerValue = cmbMenuName.Text;
}
}
private void cmbMenuName_KeyDown_1(object sender, KeyEventArgs e)
{
e.Handled = true;
}
you can also refer to this link. It used some event handlers to the toolstripcombobox in c++. Toolstripcombobox is somehow similar to combobox since it is just inherited from combobox and c++ and c# in .net seems to be similar so you won't have much problem understanding the concept of the code.
I have a textbox that does autocompletion like so:
txtName.AutoCompleteMode = AutoCompleteMode.Suggest;
txtName.AutoCompleteSource = AutoCompleteSource.CustomSource;
txtName.AutoCompleteCustomSource = namesCollection;
It works, but only at the beginning of a textbox. I'd like autocomplete to kick in for any word the user is entering, at any position in the textbox.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace TubeUploader
{
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();
KeyDown += this_KeyDown;
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.Tab:
{
if (_listBox.Visible)
{
InsertWord((String)_listBox.SelectedItem);
ResetListBox();
_formerValue = Text;
}
break;
}
case Keys.Down:
{
if ((_listBox.Visible) && (_listBox.SelectedIndex < _listBox.Items.Count - 1))
_listBox.SelectedIndex++;
break;
}
case Keys.Up:
{
if ((_listBox.Visible) && (_listBox.SelectedIndex > 0))
_listBox.SelectedIndex--;
break;
}
}
}
protected override bool IsInputKey(Keys keyData)
{
switch (keyData)
{
case Keys.Tab:
return true;
default:
return base.IsInputKey(keyData);
}
}
private void UpdateListBox()
{
if (Text == _formerValue) return;
_formerValue = Text;
String word = GetWord();
if (_values != null && word.Length > 0)
{
String[] matches = Array.FindAll(_values,
x => (x.StartsWith(word, StringComparison.OrdinalIgnoreCase) && !SelectedValues.Contains(x)));
if (matches.Length > 0)
{
ShowListBox();
_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++)
{
_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 : _listBox.Width;
}
}
}
else
{
ResetListBox();
}
}
else
{
ResetListBox();
}
}
private String GetWord()
{
String text = Text;
int pos = SelectionStart;
int posStart = text.LastIndexOf(' ', (pos < 1) ? 0 : pos - 1);
posStart = (posStart == -1) ? 0 : posStart + 1;
int posEnd = text.IndexOf(' ', pos);
posEnd = (posEnd == -1) ? text.Length : posEnd;
int length = ((posEnd - posStart) < 0) ? 0 : posEnd - posStart;
return text.Substring(posStart, length);
}
private void InsertWord(String newTag)
{
String text = Text;
int pos = SelectionStart;
int posStart = text.LastIndexOf(' ', (pos < 1) ? 0 : pos - 1);
posStart = (posStart == -1) ? 0 : posStart + 1;
int posEnd = text.IndexOf(' ', pos);
String firstPart = text.Substring(0, posStart) + newTag;
String updatedText = firstPart + ((posEnd == -1) ? "" : text.Substring(posEnd, text.Length - posEnd));
Text = updatedText;
SelectionStart = firstPart.Length;
}
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);
}
}
}
}
Sample Usage
using System;
using System.Windows.Forms;
namespace AutoComplete
{
public partial class TestForm : Form
{
private readonly String[] _values = { "one", "two", "three", "tree", "four", "fivee" };
public TestForm()
{
InitializeComponent();
// AutoComplete is our special textbox control on the form
AutoComplete.Values = _values;
}
}
}
I made a few changes to the solution proposed by #PaRiMaL RaJ because the list box was not being displayed when the text box was inside a UserControl that was not tall enough. Basically, instead of adding the list box to the parent of the text box, I added to the form and I calculate the absolute position in the form.
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)
{
Form parentForm = this.FindForm(); // new line added
parentForm.Controls.Add(_listBox); // adds it to the form
Point positionOnForm = parentForm.PointToClient(this.Parent.PointToScreen(this.Location)); // absolute position in the form
_listBox.Left = positionOnForm.X;
_listBox.Top = positionOnForm.Y + 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);
}
}
}
The other solutions didn't work for me in a multiline environment to my needs, so I've added to #Francisco Goldenstein's answer to enable this. What I needed was to autocomplete any 'word' in the TextBox and in any position/line. After minimal testing, this class seems to work well enough for me in a multiline TextBox. Hope it helps someone.
Main changes are in UpdateListBox() and this_KeyDown(), to deal with the 'current' word, i.e. the one just before the caret position, rather than the entire textbox contents.
Change the definition of separators in UpdateListBox() to suit your needs.
using System;
using System.Drawing;
using System.Windows.Forms;
class MultiLineAutoCompleteTextBox : TextBox
{
private ListBox _listBox;
private bool _isAdded;
private String[] _values;
private String _formerValue = String.Empty;
private int _prevBreak;
private int _nextBreak;
private int _wordLen;
public MultiLineAutoCompleteTextBox()
{
InitializeComponent();
ResetListBox();
}
private void InitializeComponent()
{
_listBox = new ListBox();
KeyDown += this_KeyDown;
KeyUp += this_KeyUp;
}
private void ShowListBox()
{
if (!_isAdded)
{
Form parentForm = FindForm();
if (parentForm == null) return;
parentForm.Controls.Add(_listBox);
Point positionOnForm = parentForm.PointToClient(Parent.PointToScreen(Location));
_listBox.Left = positionOnForm.X;
_listBox.Top = positionOnForm.Y + 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:
case Keys.Space:
{
if (_listBox.Visible)
{
Text = Text.Remove(_prevBreak == 0 ? 0 : _prevBreak + 1, _prevBreak == 0 ? _wordLen + 1 : _wordLen);
Text = Text.Insert(_prevBreak == 0 ? 0 : _prevBreak + 1, _listBox.SelectedItem.ToString());
ResetListBox();
_formerValue = Text;
Select(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;
if (Text.Length == 0)
{
_listBox.Visible = false;
return;
}
_formerValue = Text;
var separators = new[] { '|', '[', ']', '\r', '\n', ' ', '\t' };
_prevBreak = Text.LastIndexOfAny(separators, CaretIndex > 0 ? CaretIndex - 1 : 0);
if (_prevBreak < 1) _prevBreak = 0;
_nextBreak = Text.IndexOfAny(separators, _prevBreak + 1);
if (_nextBreak == -1) _nextBreak = CaretIndex;
_wordLen = _nextBreak - _prevBreak - 1;
if (_wordLen < 1) return;
string word = Text.Substring(_prevBreak + 1, _wordLen);
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 : Width; ;
}
}
_listBox.EndUpdate();
}
else
{
ResetListBox();
}
}
else
{
ResetListBox();
}
}
public int CaretIndex => SelectionStart;
public String[] Values
{
get
{
return _values;
}
set
{
_values = value;
}
}
}