Autocomplete AND preventing new input - combobox - c#

How can I allow the users of my program to type in a value and have it auto-complete, however, I also what to prevent them from entering new data because it would cause the data to be unfindable (unless you had direct access to the database).
Does anyone know how to do this?
The reasoning behind not using just a dropdown style combobox is because entering data by typing it is and then refusing characters that are not part of an option in the list is because it's easier on the user.
If you have used Quickbook's Timer, that is the style of comboboxes I am going for.

Kudos to BFree for the help, but this is the solution I was looking for. The ComboBox is using a DataSet as it's source so it's not a custom source.
protected virtual void comboBoxAutoComplete_KeyPress(object sender, KeyPressEventArgs e) {
if (Char.IsControl(e.KeyChar)) {
//let it go if it's a control char such as escape, tab, backspace, enter...
return;
}
ComboBox box = ((ComboBox)sender);
//must get the selected portion only. Otherwise, we append the e.KeyChar to the AutoSuggested value (i.e. we'd never get anywhere)
string nonSelected = box.Text.Substring(0, box.Text.Length - box.SelectionLength);
string text = nonSelected + e.KeyChar;
bool matched = false;
for (int i = 0; i < box.Items.Count; i++) {
if (((DataRowView)box.Items[i])[box.DisplayMember].ToString().StartsWith(text, true, null)) {
matched = true;
break;
}
}
//toggle the matched bool because if we set handled to true, it precent's input, and we don't want to prevent
//input if it's matched.
e.Handled = !matched;
}

This is my solution, I was having the same problem and modify your code to suit my solution using textbox instead of combobox, also to avoid a negative response after comparing the first string had to deselect the text before comparing again against autocomplet list, in this code is an AutoCompleteStringCollection shiper, I hope this solution will help
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
String text = ((TextBox)sender).Text.Substring(
0, ((TextBox)sender).SelectionStart) + e.KeyChar;
foreach(String s in this.shippers)
if (s.ToUpperInvariant().StartsWith(text.ToUpperInvariant()) ||
e.KeyChar == (char)Keys.Back || e.KeyChar == (char)Keys.Delete)
return;
e.Handled = true;
}

OK, here's what I came up with. Hack? Maybe, but hey, it works. I just filled the combobox with the days of the week (hey, I needed something), and then handle the keypress event. On every key press, I check if that word matches the begining of any word in the AutoCompleteSourceCollection. If it doesn't, I set e.Handled to true, so the key doesn't get registered.
public Form5()
{
InitializeComponent();
foreach (var e in Enum.GetValues(typeof(DayOfWeek)))
{
this.comboBox1.AutoCompleteCustomSource.Add(e.ToString());
}
this.comboBox1.KeyPress += new KeyPressEventHandler(comboBox1_KeyPress);
}
private void comboBox1_KeyPress(object sender, KeyPressEventArgs e)
{
string text = this.comboBox1.Text + e.KeyChar;
e.Handled = !(this.comboBox1.AutoCompleteCustomSource.Cast<string>()
.Any(s => s.ToUpperInvariant().StartsWith(text.ToUpperInvariant()))) && !char.IsControl(e.KeyChar);
}
EDIT: If you're on .Net 3.5 you'll need to reference System.Linq. If you're on .NET 2.0 then use this instead:
private void comboBox1_KeyPress(object sender, KeyPressEventArgs e)
{
string text = this.comboBox1.Text + e.KeyChar;
foreach (string s in this.comboBox1.AutoCompleteCustomSource)
{
if (s.ToUpperInvariant().StartsWith(text.ToUpperInvariant()))
{
return;
}
}
e.Handled = true;
}

I know I'm about six years late but maybe this can help somebody.
private void comboBox1_Leave(object sender, EventArgs e)
{
if (comboBox1.Items.Contains(comboBox1.Text)) { MessageBox.Show("YE"); }
else { MessageBox.Show("NE"); }
OR
if (comboBox1.FindStringExact(comboBox1.Text) > -1) { MessageBox.Show("YE"); }
else { MessageBox.Show("NE"); }
}

Related

C# RichTextBox ReadOnly Event

I have a read only rich text box and an editable text box. The text from the read only is from the editable. They can't be viewed at the same time. When the user presses a key it hides the read only and then selects that position in the editable.
I would like it to enter the key that was pressed into the editable without playing the error "ding"
I'm thinking that an override of the read only error function would be ideal but i'm not sure what that is.
private void EditCode(object sender, KeyPressEventArgs e)
{
int cursor = txtReadOnly.SelectionStart;
tabText.SelectedIndex = 0;
ToggleView(new object(), new EventArgs());
txtEdit.SelectionStart = cursor;
txtEdit.Text.Insert(cursor, e.KeyChar.ToString());
}
Answer:
private void EditCode(object sender, KeyPressEventArgs e)
{
e.Handled = true;
int cursor = txtCleanCode.SelectionStart;
tabText.SelectedIndex = 0;
ToggleView(new object(), new EventArgs());
txtCode.Text = txtCode.Text.Insert(cursor, e.KeyChar.ToString());
txtCode.SelectionStart = cursor + 1;
}
I'll have to make it check if it's a non-control char but that's another deal. Thanks everyone!
One idea is to make the rich text box editable but canceling out all keys:
private void richtextBox1_KeyDown(object sender, KeyEventArgs e)
{
// Stop the character from being entered into the control
e.Handled = true;
// add any other code here
}
Here is one way: Check for <Enter> so the user can still use the navigation keys:
private void txtReadOnly_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
e.Handled = true; // no ding for normal keys in the read-only!
txtEdit.SelectionStart = txtReadOnly.SelectionStart;
txtEdit.SelectionLength = txtReadOnly.SelectionLength;
}
}
No need to fiddle with a cursor. Make sure to have:
txtEdit.HideSelection = false;
and maybe also
txtReadOnly.HideSelection = false;
Obviously to keep the two synchronized :
private void txtEdit_TextChanged(object sender, EventArgs e)
{
txtReadOnly.Text = txtEdit.Text;
}
You will need o decide on some way fo r the user to return from editing to viewing. Escape should be reserved for aborting the edit! Maybe Control-Enter?

Why are numeric keystrokes appearing twice?

I'm new to C#. Using the code below, whenever I press a number key on my keyboard, it will display twice in the textbox. When I press "1" on the keyboard it will display
"11", and when I press "2" it will display "22". Why is this?
private void Window_TextInput(object sender, TextCompositionEventArgs e)
{
if(!isNumeric(e.Text))
{
string display = string.Empty;
display += e.Text;
displayNum(display);
}
else
{
String inputOperator = string.Empty;
inputOperator += e.Text;
if (inputOperator.Equals("+"))
{
ApplySign(sign.addition, "+");
}
}
}
private bool isNumeric(string str)
{
System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("[^0-9]");
return reg.IsMatch(str);
}
private void window_keyUp(object sender, KeyEventArgs e)
{
if (e.Key >= Key.D0 && e.Key <= Key.D9)
{
int num = e.Key - Key.D0;
outputText2.Text += num;
}
}
private void BtnNum_Click(object sender, RoutedEventArgs e)
{
Button num = ((Button)sender);
displayNum(num.Content.ToString());
}
private void displayNum(String n)
{
if (operator1 == 0 && double.Parse(n) == 0)
{
}
else
{
if (operator1 == 0)
{
outputText2.Clear();
}
outputText2.Text += n;
operator1 = double.Parse(outputText2.Text);
outputText2.Text = Convert.ToString(operator1);
}
}
You have two events that are handeling the Keyboard events. Although not really sure what the displayNum() method is doing
I am assuming the Window_TextInput event is the event you wish to primarily handle the event.
Try adding
e.Handled = true;
In the Window_TextInput method. If that doesn't solve the problem can you post the displayNum() method?
EDIT:
After further review of the code and trying the same I do not see the relevance for the window_keyUp method as your Window_TextInput handles the input characters and has more applicable logic for handling the TextInput changes.
After I removed the window_keyUp event method the output appeared as expected (although commented out the ApplySign() method.
You've subscribed to two window-level text-related events - TextInput and KeyUp - and both of them end up appending input to the TextBox.
window_keyUp appends numbers to the TextBox
It looks like Window_TextInput is supposed to append non-numeric characters, but your RegEx is incorrect ([^0-9] matches anything that is not numeric, so IsNumeric returns True if the input is not a number)
The effect is that every numeric key press shows up twice.

Permanent prefix in a TextBox

I am trying to have a permanent prefix input in the textbox. In my case, I want to have the following prefix:
DOMAIN\
So that users can only have to type their username after the domain prefix. It's not something I have to do, or pursue but my question is more out of curiosity.
I was trying to come up with some logic to do this on the TextChangedEvent however, this means I need to know which characters have been deleted where and then pre-append DOMAIN\ to whatever their input is - I can't work out the logic for this so I can't post what I have tried apart from where I got to.
public void TextBox1_TextChanged(object sender, EventArgs e)
{
if(!TextBox1.Text.Contains(#"DOMAIN\")
{
//Handle putting Domain in here along with the text that would be determined as the username
}
}
I've looked on the internet and can't find anything, How do I have text in a winforms textbox be prefixed with unchangable text? was trying to do a similar thing but the answers don't really help.
Any ideas on how I can keep the prefix DOMAIN\ in a TextBox?
Using the KISS principle is indicated here. Trying to catch key presses just won't do anything when the user uses Ctrl+V or the context menu's Cut and Paste commands. Simply restore the text when anything happened that fudged the prefix:
private const string textPrefix = #"DOMAIN\";
private void textBox1_TextChanged(object sender, EventArgs e) {
if (!textBox1.Text.StartsWith(textPrefix)) {
textBox1.Text = textPrefix;
textBox1.SelectionStart = textBox1.Text.Length;
}
}
And help the user avoid editing the prefix by accident:
private void textBox1_Enter(object sender, EventArgs e) {
textBox1.SelectionStart = textBox1.Text.Length;
}
Why not see in the event args of the textChanged what the value was before and the new value and if the Domain\ is not there in the new value, then keep the old one.
Or, why not just show the Domain\ as a label in front of the TextBox and just prepend it in code behind so that the final text is something like Domain\<username>.
Yeah I sort of solved this once I asked the question... I won't delete the question incase anybody else has the same question in the future, because I couldn't find a suitable answer. I set the Text to Domain\ and then used the KeyPress event.
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = (textBox1.GetCharIndexFromPosition(Cursor.Position) < 7);
}
I tend to keep working once I ask, instead of letting people do all the work for me :)
How about a reusable custom TextBox control. There are comments in the code
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
textBox1.Prefix = #"DOMAIN\";
}
}
class PrefixedTextBox : TextBox
{
private string _prefix = String.Empty;
public string Prefix
{
get { return _prefix; }
set
{
_prefix = value;
Text = value;
}
}
protected override void OnKeyDown(KeyEventArgs e)
{
// Don't allow Backspace and Delete if the only text is Prefix
if (Text == Prefix && (e.KeyCode == Keys.Back || e.KeyCode == Keys.Delete))
e.Handled = true;
// If home key is pressed set cursor just after the prefix
if (e.KeyCode == Keys.Home)
{
e.Handled = true;
SelectionStart = Prefix.Length;
}
// Don't allow cursor to be moved inside Prefix
if (SelectionStart <= Prefix.Length && (e.KeyCode == Keys.Left || e.KeyCode == Keys.Up))
e.Handled = true;
base.OnKeyDown(e);
}
protected override void OnClick(EventArgs e)
{
EnsureCursorPosition();
base.OnClick(e);
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
EnsureCursorPosition();
// this was checked OnKeyDown. This prevents deleting and writing back behaviour
if (Text == Prefix && e.KeyChar == '\b')
e.Handled = true;
base.OnKeyPress(e);
}
protected override void OnKeyUp(KeyEventArgs e)
{
// Yet, some how an invalid text is entered fix it by just displaying the Prefix
if (!Text.StartsWith(Prefix))
Text = Prefix;
base.OnKeyUp(e);
}
private void EnsureCursorPosition()
{
// Never allow cursor position before Prefix
if (SelectionStart < Prefix.Length)
SelectionStart = Text.Length;
}
}

How to stop the first character in a text box from being '.'?

This is the code I currently have:
private void textBox_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar) && e.KeyChar != '.';
if (e.KeyChar == '.' && (sender as TextBox).Text.IndexOf('.') > -1) e.Handled = true;
}
KeyPress isn't good enough to do this kind of validation. A simple way to bypass it is to paste text into the text box with Ctrl+V. Or the context menu, no key event at all.
In this specific case, the TextChanged event will get the job done:
private void textBox_TextChanged(object sender, EventArgs e) {
var box = (TextBox)sender;
if (box.Text.StartsWith(".")) box.Text = "";
}
But there's a lot more to validating numeric values. You also need to reject stuff like 1.1.1 or 1.-2 etcetera. Use the Validating event instead. Drop an ErrorProvider on the form and implement the event like this:
private void textBox_Validating(object sender, CancelEventArgs e) {
var box = (TextBox)sender;
decimal value;
if (decimal.TryParse(box.Text, out value)) errorProvider1.SetError(box, "");
else {
e.Cancel = true;
box.SelectAll();
errorProvider1.SetError(box, "Invalid number");
}
}
You probably want to use the TextChanged event, since the user could paste in values. For the best experience given the requirements, I'd suggest simply removing any leading . characters.
void textBox1_TextChanged(object sender, EventArgs e)
{
if (textBox1.Text.StartsWith("."))
{
textBox1.Text = new string(textBox1.Text.SkipWhile(c => c == '.').ToArray());
}
}
This does not address a requirement to use only digits -- wasn't clear in the question if that is the case.
This works for copy and pasting too.
private void textBox1_KeyUp(object sender, KeyEventArgs e)
{
int decimalCount=0;
string rebuildText="";
for(int i=0; i<textBox1.Text.Length; i++)
{
if (textBox1.Text[i] == '.')
{
if (i == 0) break;
if (decimalCount == 0)
rebuildText += textBox1.Text[i];
decimalCount++;
}
else if ("0123456789".Contains(textBox1.Text[i]))
rebuildText += textBox1.Text[i];
}
textBox1.Text = rebuildText;
textBox1.SelectionStart = textBox1.Text.Length;
}
You can try this:
private void TextBox_TextChanged(object sender, EventArgs e)
{
TextBox.Text = TextBox.Text.TrimStart('.');
}

How to move the textbox caret to the right

I would like to change all the characters entered into a textbox to upper case. The code will add the character, but how do I move the caret to the right?
private void textBox3_KeyPress(object sender, KeyPressEventArgs e)
{
textBox3.Text += e.KeyChar.ToString().ToUpper();
e.Handled = true;
}
Set the CharacterCasing property of the TextBox to Upper; then you don't need to process it manually.
Note that textBox3.Text += e.KeyChar.ToString().ToUpper(); will append the new character to the end of the string even if the input caret is in the middle of the string (which most users will find highly confusing). For the same reason we cannot assume that the input caret should appear at the end of the string after inputting the character.
If you would still really want to do this in code, something like this should work:
// needed for backspace and such to work
if (char.IsControl(e.KeyChar))
{
return;
}
int selStart = textBox3.SelectionStart;
string before = textBox3.Text.Substring(0, selStart);
string after = textBox3.Text.Substring(before.Length);
textBox3.Text = string.Concat(before, e.KeyChar.ToString().ToUpper(), after);
textBox3.SelectionStart = before.Length + 1;
e.Handled = true;
tbNumber.SelectionStart = tbNumber.Text.ToCharArray().Length;
tbNumber.SelectionLength = 0;
private void txtID_TextChanged(object sender, EventArgs e)
{
txtID.Text = txtID.Text.ToUpper();
txtID.SelectionStart = txtID.Text.Length;
}
This will preserve the location of the insertion point (but persionally I'd go with the answer given by Fredrik Mörk)
private void textBox3_KeyPress(object sender, KeyPressEventArgs e)
{
int selStart = textBox3.SelectionStart;
textBox3.Text += e.KeyChar.ToString().ToUpper();
textBox3.SelectionStart = selStart;
e.Handled = true;
}
SelectionStart might actually be called SelStart, I don't have a compiler handy at the moment.
If you have to do this manually, you can use
private void textBox3_KeyPress(object sender, KeyPressEventArgs e)
{
textBox3.Text += e.KeyChar.ToString().ToUpper();
textBox3.SelectionStart = textBox3.Text.Length;
e.Handled = true;
}
But the preceding code inserts the new character at the end of the text. If you want to insert it at where the cursor is:
private void textBox3_KeyPress(object sender, KeyPressEventArgs e)
{
int selStart = textBox3.SelectionStart;
textBox3.Text = textBox3.Text.Insert(selStart,e.KeyChar.ToString().ToUpper());
textBox3.SelectionStart = selStart + 1;
e.Handled = true;
}
This code inserts the new character at the cursor position and moves the cursor to the left of the newly inserted character.
But i still think setting CharacterCasing is better.
Another method is to just change the value of the KeyChar itself:
private void textBox1_KeyPress(object sender, KeyPressEventArgs e) {
if ((int)e.KeyChar >= 97 && (int)e.KeyChar <= 122) {
e.KeyChar = (char)((int)e.KeyChar & 0xDF);
}
}
Although, using the CharacterCasing property is the easiest solution.

Categories