disable key event when the focus is on autocompletion box of textbox - c#

In my project there is a Form mainForm in which there are two textBoxes txtUserName and txtPassword and also a button btnLogin.
I have given the following txtUserName properties:
txtUserName Properties
AutoCompleteCustomSource - Collection
--> Administrator
--> Clerk
AutoCompleteMode - Suggest
AutoCompleteSource - CustomSource
btnLogin_Click Event
if (txtUserName.Text.Equals("Administrator") && txtPassword.Text.Equals("123"))
{
//function to access admin features
}
else if (txtUserName.Text.Equals("Clerk") && txtPassword.Text.Equals("123"))
{
//function to access clerk features
}
else
{
MessageBox.Show("Please Enter correct details", "Login Error");
}
I have setted the mainForm keypreview to true and implemented function to keyDown event of mainForm which is shown in the below code:
mainForm_KeyDownEvent
if (e.KeyCode.Equals(Keys.Enter)) //Invokes whenever Enter is pressed
{
btnLogin_Click(sender,e); //login
}
Now my problem is that whenever the focus is on txtUserName and pressing A, dropdown is showing to select "Administrator" (which is defined in collections as I shown in above properties). When I click Enter on keyboard it is showing MessageBox instead of selecting "Administrator". I know that is invoking the keydown event of mainForm. How to disable the keyDown event, when it is on textbox dropdown thing so that I can press enter?
EDIT:
I tried the below code in public form() :(not working)
InitializeComponent();
if (txtUserName.AutoCompleteMode) { /* showing red scribbles */
this.KeyDown -= mainForm_KeyDown;
}

You should not be handling the Enter key at all. You can remove your KeyDown handler, and instead use the AcceptButton property of the form to set the button that gets "clicked" when Enter is pressed. This is already supposed to not "click" the button when another control has already handled the Enter key.
That isn't enough for your situation, because standard Windows behaviour is for the Enter key to press the default button. Press Win+R, for example, to get the Run... dialog, start typing C:\Use, press Down to select C:\Users, press Enter, and see what happens.
In order to override that behaviour, you need to make the text box tell the form that it will be handling the Enter key itself, so that the form won't send it to the default button. This can be done by creating a derived class and overriding IsInputKey:
public class MyTextBox : TextBox
{
protected override bool IsInputKey(Keys keyData)
{
return base.IsInputKey(keyData) || ((keyData & ~Keys.Shift) == Keys.Enter && IsDroppedDown);
}
}
However, TextBox implements autocompletion using the SHAutoComplete function, which automatically creates an IAutoComplete object behind the scenes. That object cannot be accessed, and because of that, the IsDroppedDown property that I used in IsInputKey cannot be created. It would be implemented using IAutoCompleteDropDown.GetDropDownStatus, but since the object is inaccessible, you cannot (reliably) determine whether the dropdown list is showing.
You would need to either implement the auto completion without using the built-in AutoComplete* properties, or you would need to always suppress the Enter key (remove the && IsDroppedDown in the above IsInputKey).
Update: here's how to create an IAutoComplete object manually. The strings Administrator and Clerk are hardcoded. The GetDropDownStatus function is used to suppress any default button's handling of Enter when the drop down list is visible. Feedback welcome.
IAutoComplete.cs:
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
[ComImport]
[Guid("00bb2762-6a77-11d0-a535-00c04fd7d062")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[CoClass(typeof(IAutoCompleteClass))]
interface IAutoComplete
{
void Init(HandleRef hwndEdit, IEnumString punkACL, string pwszRegKeyPath, string pwszQuickComplete);
void Enable(bool fEnable);
}
IAutoComplete2.cs:
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
[Guid("EAC04BC0-3791-11d2-BB95-0060977B464C")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAutoComplete2
{
void Init(HandleRef hwndEdit, IEnumString punkACL, string pwszRegKeyPath, string pwszQuickComplete);
void Enable(bool fEnable);
void SetOptions(AutoCompleteOptions dwFlag);
AutoCompleteOptions GetOptions();
};
AutoCompleteOptions.cs:
using System;
[Flags]
enum AutoCompleteOptions : int
{
None = 0x00,
AutoSuggest = 0x01,
AutoAppend = 0x02,
Search = 0x04,
FilterPrefixes = 0x08,
UseTab = 0x10,
UpDownKeyDropsList = 0x20,
RtlReading = 0x40,
WordFilter = 0x80,
NoPrefixFiltering = 0x100,
}
IAutoCompleteDropDown.cs:
using System;
using System.Runtime.InteropServices;
using System.Text;
[Guid("3CD141F4-3C6A-11d2-BCAA-00C04FD929DB")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAutoCompleteDropDown
{
void GetDropDownStatus(out AutoCompleteDropDownFlags dwFlags, out StringBuilder wszString);
void ResetEnumerator();
}
AutoCompleteDropDownFlags.cs:
using System;
[Flags]
enum AutoCompleteDropDownFlags : int
{
None = 0x00,
Visible = 0x01
}
IAutoCompleteClass.cs:
using System;
using System.Runtime.InteropServices;
[ComImport]
[Guid("00BB2763-6A77-11D0-A535-00C04FD7D062")]
class IAutoCompleteClass
{
}
EnumString.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
class EnumString : IEnumString
{
const int E_INVALIDARG = unchecked((int)0x80070057);
const int S_OK = 0;
const int S_FALSE = 1;
int current;
string[] strings;
public EnumString(IEnumerable<string> strings)
{
this.current = 0;
this.strings = strings.ToArray();
}
public void Clone(out IEnumString ppenum)
{
ppenum = new EnumString(strings);
}
public int Next(int celt, string[] rgelt, IntPtr pceltFetched)
{
if (celt < 0)
return E_INVALIDARG;
int num = 0;
while (current < strings.Length && celt != 0)
{
rgelt[num] = strings[current];
current++;
num++;
celt--;
}
if (pceltFetched != IntPtr.Zero)
Marshal.WriteInt32(pceltFetched, num);
if (celt != 0)
return S_FALSE;
return S_OK;
}
public void Reset()
{
current = 0;
}
public int Skip(int celt)
{
if (celt < 0)
return E_INVALIDARG;
if (strings.Length - current > celt)
{
current = strings.Length;
return S_FALSE;
}
current += celt;
return S_OK;
}
}
MyTextBox.cs:
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
public class MyTextBox : TextBox
{
IAutoComplete2 autoComplete;
IAutoCompleteDropDown autoCompleteDropDown;
public bool IsDroppedDown
{
get
{
if (autoCompleteDropDown == null)
return false;
AutoCompleteDropDownFlags dwFlags;
StringBuilder wszString;
autoCompleteDropDown.GetDropDownStatus(out dwFlags, out wszString);
return (dwFlags & AutoCompleteDropDownFlags.Visible) != AutoCompleteDropDownFlags.None;
}
}
protected override void CreateHandle()
{
base.CreateHandle();
autoComplete = (IAutoComplete2)new IAutoComplete();
autoCompleteDropDown = (IAutoCompleteDropDown)autoComplete;
autoComplete.SetOptions(AutoCompleteOptions.AutoSuggest);
autoComplete.Init(new HandleRef(this, this.Handle), new EnumString(new string[] { "Administrator", "Clerk" }), null, null);
}
protected override void DestroyHandle()
{
ReleaseAutoComplete();
base.DestroyHandle();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
ReleaseAutoComplete();
}
base.Dispose(disposing);
}
protected override bool IsInputKey(Keys keyData)
{
return base.IsInputKey(keyData) || ((keyData & ~Keys.Shift) == Keys.Enter && IsDroppedDown);
}
void ReleaseAutoComplete()
{
if (autoComplete != null)
{
Marshal.ReleaseComObject(autoComplete);
autoComplete = null;
autoCompleteDropDown = null;
}
}
}

You need to override the keydown event handler.
protected override void OnKeyDown(KeyEventArgs e)
{
//call original event handler. Remove it if you don't need it at all.
base.OnKeyDown(e);
//Insert your code here....
}

Try this. Hopefully it will not cause any problem on pressing enter while your focus is on txtUsername or else where
If you write a in txtUserName and press enter, Your Admministrator choice will be selected from your autocompletecustomsource using regular expression and focus will go to txtPassword. My regular expression is very flexible you can made it bit restricted as following to match strictly from beginning and also can remove ignore case
Regex rg = new Regex("^" + txtUserName.Text);
private void mainForm_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode.Equals(Keys.Enter))// && !txtUserName.Focus())// && intFlag.Equals(0))
{
if (txtUserName.Text.Length > 0)
{
if (txtUserName.Focused)
{
Regex rg = new Regex(txtUserName.Text, RegexOptions.IgnoreCase);
for (int i = 0; i < txtUserName.AutoCompleteCustomSource.Count; i++)
{
if (rg.IsMatch(txtUserName.AutoCompleteCustomSource[i]))
{
txtUserName.Text = txtUserName.AutoCompleteCustomSource[i];
txtPassword.Focus();
return;
}
}
}
if (txtPassword.Text.Length > 0)
{
btnLogin_Click(null, null); //login
}
else
{
//MessageBox.Show("Please Give a Password");
txtPassword.Focus();
}
}
else
{
//MessageBox.Show("Please Give a username");
txtUserName.Focus();
}
}
//if (txtPassword.ContainsFocus)
//{
// btnLogin_Click(sender, e); //login
//}
//else
//{
// this.txtPassword.Focus();
//}
}

Actually, you have two issues.
First off, set the AutoCompleteMode property of txtUserName to "SuggestAppend" instead of simply "Suggest." This way, if the user types the first letter or two, the correct entry will automatically be appended to the txtUSerName.Text.
Next, modify your Form code as follows:
void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode.Equals(Keys.Enter)) //Invokes whenever Enter is pressed
{
if (txtPassword.ContainsFocus)
{
btnLogin_Click(sender, e); //login
}
else
{
this.txtPassword.Focus();
}
}
}
private void btnLogin_Click(object sender, EventArgs e)
{
if (txtUserName.Text.Equals("Administrator") && txtPassword.Text.Equals("123"))
{
MessageBox.Show("Administrator");
}
else if (txtUserName.Text.Equals("Clerk") && txtPassword.Text.Equals("123"))
{
MessageBox.Show("Clerk");
}
else
{
MessageBox.Show("Please Enter correct details", "Login Error");
}
}
In the above, the Key Down event handling code tests to see if the password text box has the focus (meaning, the user has presumable entered a user name already, and a password, and is ready to submit the data). If so, the btnLogin_Click event is called. Otherwise, (meaning, txtUserName probably has the focus) control is passed to txtPassword to continue data entry.
UPDATE: re - Your comment:
Simply kill the logic in the Key Down Event handler like so:
Revised Event Handling Code:
void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode.Equals(Keys.Enter)) //Invokes whenever Enter is pressed
{
btnLogin_Click(sender, e); //login
}
}
Note, another minor improvement (considering the overall structure of your code) would be to use a combo box for selection of UserName, and set the autocomplete source to "ListItems" then enter your options the same as with the text box. This requires the user to select from the pre-defined list. This still has scaleability issues similar to the previous, but eliminates an unnecessary step for the user if they simply make a typo while entering User Name data.
Remember that users tend to dislike unnecessary interruption by pop-up messages. Allow them to select the appropriate "user name" from a drop down, type in the proper password, and move on.
There are some better ways to do all of this, but this should tune up what you have into working order.
On a final note, let me observe that eventually you will probably want to find a more robust way of performing this sort of validation. Any time you need to add users (which, in your code, appear to be defined more as "groups" you will need to add to your conditional event handling tree.
You might check into persisting usernames and passwords in an encrypted file or database, and load them into a dictionary or something at run time. Then perform a key/value lookup on user/Password.
Or something.
Anyway, hope that helps.
UPDATE 2: The complete code all in one shot. This should behave the way you are asking:
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;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.KeyDown +=new KeyEventHandler(Form1_KeyDown);
}
void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode.Equals(Keys.Enter)) //Invokes whenever Enter is pressed
{
btnLogin_Click(sender, e); //login
}
}
private void btnLogin_Click(object sender, EventArgs e)
{
if (txtUserName.Text.Equals("Administrator") && txtPassword.Text.Equals("123"))
{
MessageBox.Show("Administrator");
}
else if (txtUserName.Text.Equals("Clerk") && txtPassword.Text.Equals("123"))
{
MessageBox.Show("Clerk");
}
else
{
MessageBox.Show("Please Enter correct details", "Login Error");
}
}
}
}

Related

TextBox Validation in Visual Studio C#

I am fairly new to Visual Studio and C# in general but basically I need to check that the contents of the textbox are valid before proceeding to add the contents to a list with a button.
I am using the following objects:
A TexBox to enter the value
A Validating event linked to the TextBox to validate the data.
A Button to take action
A Click event associated to the button.
The problem is that I cannot check if the values in the box are valid or not and prevent the click event in the button to happen. In other words if the contents are not valid then do not take action.
This is my code.
public partial class mainForm : Form
{
public mainForm()
{
InitializeComponent();
}
private void addButton_Click(object sender, EventArgs e)
{
// I need to check if the content is valid before adding it to the form
ListViewItem item = new ListViewItem(this.nameTextBox.Text);
this.listView1.Items.Add(item);
}
private void nameTextBox_Validating(object sender, CancelEventArgs e)
{
int maxCharacters = 15;
String err = "";
String contents = this.nameTextBox.Text;
if (contents.Length == 0)
{
err = "I am sorry but the name cannot be empty";
e.Cancel = true;
}
else if (!contents.Replace(" ", "").Equals(contents, StringComparison.OrdinalIgnoreCase))
{
err = "I am sorry but the name cannot contain spaces";
e.Cancel = true;
}
else if (contents.Length > 15)
{
err = "I am sorry, but the name cannot have more than " + maxCharacters + " characters";
e.Cancel = true;
}
this.mainFormErrorProvider.SetError(this.nameTextBox, err);
}
}
You are confused about when the "name" text boxes' validation method is called.
See here
When you change the focus by using the keyboard (TAB, SHIFT+TAB, and so on), by calling the Selector SelectNextControl methods, or by setting the ContainerControl.ActiveControl property to the current form, focus events occur in the following order...
So clicking the button has nothing to do with the validation of the text box.
What you need to do is put the validation logic in a separate method, and then call it from both events.
Also, since you're new to C# here are some pointers.
Namespaces, Classes, methods, and properties are supposed to be Pascal Case.
Instead of using a long winded work around like this
!contents.Replace(" ", "").Equals(nameText, StringComparison.OrdinalIgnoreCase)
You could simply use
contents.Contains(" ")
There are tons of useful methods just like that, so in the future you should do more research on what you need before implementing something yourself, especially if it seems like a commonly used technique.
Also, you want to avoid if/else's as much as possible in favour of returning early.
Here's what your class might look with better practice in mind
const int NAME_MAX_CHARACTERS = 15;
public mainForm()
{
InitializeComponent();
}
private void addButton_Click(object sender, EventArgs e)
{
if(!Validate())
{
return;
}
// I need to check if the content is valid before adding it to the form
ListViewItem item = new ListViewItem(this.nameTextBox.Text);
this.listView1.Items.Add(item);
}
private void nameTextBox_Validating(object sender, CancelEventArgs e)
{
e.Cancel = !Validate();
}
private bool Validate()
{
string nameText = nameTextBox.Text;
if(String.IsNullOrEmpty(nameText))
{
this.mainFormErrorProvider.SetError(this.nameTextBox, "I am sorry but the name cannot be empty");
return false;
}
if(nameText.Contains(" "))
{
this.mainFormErrorProvider.SetError(this.nameTextBox, "I am sorry but the name cannot contain spaces");
return false;
}
if (nameText.Length > 15)
{
this.mainFormErrorProvider.SetError(this.nameTextBox, "I am sorry, but the name cannot have more than " + NAME_MAX_CHARACTERS + " characters");
return false;
}
return true;
}

C# Guess A Number Game - How to make it work with Radio Buttons

Clearing the air now: yes, this is classwork. Don't want to be spoonfed an answer, just want to get some help understanding what I'm doing wrong and what I need to be doing instead.
First of all, for the sake of being clear, here is the prompt from the book:
Create a project named GuessANumber with a Form that contains a guessing game with five RadioButtons numbered 1 through 5. Randomly choose one of the RadioButtons as the winning button. When the user clicks a RadioButton, display a message indicating whether the user is right.
And here is my best attempt at making it to these specs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace GuessANumber
{
public partial class Form1 : Form
{
Random ran = new Random();
public Form1()
{
InitializeComponent();
int randomNumber;
randomNumber = ran.Next(0, 4);
int correctAnswer = randomNumber;
SelectButton(correctAnswer);
}
private void SelectButton(int correctAnswer)
{
bool guessedCorrectly;
if (radioButton1.Checked && (Convert.ToInt32(radioButton1.Text) == correctAnswer))
{
guessedCorrectly = true;
}
else if (radioButton2.Checked && (Convert.ToInt32(radioButton2.Text) == correctAnswer))
{
guessedCorrectly = true;
}
else if (radioButton3.Checked && (Convert.ToInt32(radioButton3.Text) == correctAnswer))
{
guessedCorrectly = true;
}
else if (radioButton4.Checked && (Convert.ToInt32(radioButton4.Text) == correctAnswer))
{
guessedCorrectly = true;
}
else if (radioButton5.Checked && (Convert.ToInt32(radioButton5.Text) == correctAnswer))
{
guessedCorrectly = true;
}
else
{
guessedCorrectly = false;
}
if(guessedCorrectly == true)
{
winLabel.Visible = true;
}
else
{
loseLabel.Visible = true;
}
}
private void hintLabel_MouseHover(object sender, EventArgs e)
{
hintLabel.ForeColor = Color.Yellow;
}
private void hintLabel_MouseLeave(object sender, EventArgs e)
{
hintLabel.ForeColor = Color.Black;
}
}
}
My question, I guess, is this: how do I make this work as specified? I've gotten it to where I can make the labels for the win and lose conditions appear, so I know the part where I check for the win condition is working right, it's just that, being radio buttons, it defaults to 1 when the application starts. Is there a way I can make it so the radio buttons don't have a default?
EDIT: Mike's answer got me where I needed to be. Thanks to everyone!
You need wait until the user has selected one of the radio buttons, and then clicked some "OK" pushbutton.
So, begin by adding an "OK" pushbutton to your form, and adding a "buttonClicked" event handler for that pushbutton.
Once the user has clicked the "OK" pushbutton, you are supposed to check which radio button is selected, and then tell the user whether they made the correct choice or not.
By default, none of the radio buttons is selected after InitializeComponent(). (And if by any chance one of them is selected it is rather simple to make them all unselected.)
I would, personally, make an array that holds all 5 radio buttons.
RadioButton[] _buttons = { radioButton1, radioButton2, ..., radioButton5 };
Then I'd create a common event handler for the Checked event on all 5 radio buttons. It would look something like this:
void RadioButton_Checked(object sender, EventArgs e)
{
if (_buttons[ran.Next(5)].Checked)
// You win!
else
// You lose!
}

messagebox always pop up when clicking combo box

Good day, anyone can help me with this problem... I have a combo box and a textbox. the textbox(txtFruitNo) will check the length of text under Leave event. It is ok. But if I click on the combo box while txtFruitNo is not yet completed. It needs me to complete first the length of txtFruitNo then only I can click the combo box.
I do not want to show the messagebox if I click on the combo box even if the length of the txtFruitNo is not yet completed.
Thanks
private void cmbFruitSelection_SelectedIndexChanged(object sender, EventArgs e)
{
DateTime thetime = DateTime.Now;
String varApple = "App-Red";
String varBanana = "Ban-Yellow";
if (cmbFruitSelection.SelectedItem.ToString() == "Apple")
{
txtFruitNo.Text = varApple.ToString() + thetime.ToString("yyyy");
txtFruitNo.SelectionStart = txtFruitNo.Text.Length;
txtFruitNo.MaxLength = 18;
}
else if (cmbFruitSelection.SelectedItem.ToString() == "Banana")
{
txtFruitNo.Text = varBanana.ToString() + thetime.ToString("yyyy");
txtFruitNo.SelectionStart = txtFruitNo.Text.Length;
txtFruitNo.MaxLength = 17;
}
}
private void txtFruitNo_Leave(object sender, EventArgs e)
{
if (txtFruitNo.TextLength != txtFruitNo.MaxLength)
{
MessageBox.Show("Your fruit number is too short. Please check.");
txtFruitNo.Focus();
}
else
{
// Do something here
}
}
At what point is it important for continuation of the program that the "Fruit Number" is within parameters. If it is not at the time of leaving focus try moving it to a different control for example the "OK" button could run the parameter check and if valid continue if not flag mesage box and return to the textbox
Since your requirement is to only to do the validation and prompt the message box once the user has selected a value from the combo, please do the following;
Introduce a form variable
private bool isComboClicked = false;
Add the below line to cmbFruitSelection_SelectedIndexChanged
isComboClicked = true;
Adding the above line at the beginning of the above event would prompt the length validation message on selection of value from the combo. If you want to prompt message for specific value on the combo move it within the if statements if (comboBox1.SelectedItem.ToString() == "Apple") etc.
Now in txtFruitNo_Leave event enclose the code within the below if condition.
if (isComboClicked)
{
// Your Code
if (txtFruitNo.TextLength != txtFruitNo.MaxLength)
{
MessageBox.Show("Your fruit number is too short. Please check.");
txtFruitNo.Focus();
}
else
{
// Do something here
}
}
As I understand:
You have "validation" on TextBox in Leave eventhandler, which show error message if validation fails.
But if TextBox.Leave event was raised by selecting ComboBox control, then validation must be suppressed.
Create Panel and put there only txtFruitNo and cmbFruitSelection controls.
// Validation function
private bool IsTextBoxValid()
{
return this.txtFruitNo.Length == this.txtFruitNo.maxlength;
}
Then create and hook up Validating eventhandler for Panel where you will validate txtFruitNo
private void Panel_Validating(Object sender, CancelEventArgs e)
{
if(this.IsTextBoxValid() == false)
{
e.Cancel = true;
MessageBox.Show("Your fruit number is too short. Please check.") ;
}
}
Validating will be raised only when focus move outside of the panel.
Using Validating event will prevent changing focus to outside controls automatically if e.Cancel = true
In that case combobox cmbFruitSelection can be focused and user can complete txtFruitNo text by selecting valid value from ComboBox.
I think using of ErrorProvider control will be more friendly for the user, then MessageBox.
Add ErrorProvider control in the Form through designer and add few lines in the code
private void Panel_Validating(Object sender, CancelEventArgs e)
{
if(this.IsTextBoxValid() == false)
{
e.Cancel = true;
this.ErrorProvider1.SetError(txtFruitNo,
"Your fruit number is too short. Please check.");
}
else
{
this.ErrorProvider1.Clear();
}
}
And clear error after valid value was used from ComboBox
private void cmbFruitSelection_SelectedIndexChanged(object sender, EventArgs e)
{
DateTime thetime = DateTime.Now;
String varApple = "App-Red";
String varBanana = "Ban-Yellow";
if (cmbFruitSelection.SelectedItem.ToString() == "Apple")
{
txtFruitNo.Text = varApple.ToString() + thetime.ToString("yyyy");
txtFruitNo.SelectionStart = txtFruitNo.Text.Length;
txtFruitNo.MaxLength = 18;
//Clear error
this.ErrorProvider1.Clear();
}
else if (cmbFruitSelection.SelectedItem.ToString() == "Banana")
{
txtFruitNo.Text = varBanana.ToString() + thetime.ToString("yyyy");
txtFruitNo.SelectionStart = txtFruitNo.Text.Length;
txtFruitNo.MaxLength = 17;
//Clear error
this.ErrorProvider1.Clear();
}
}

How to enable a WinForm button in time to receive focus by tabbing

Visual Studio 2010, C#
I have a ComboBox with a DropDown, AutoComplete set to SuggestAppend and the AutoCompleteSource is from the ListItems. The user keys data into it until the have the correct entry. Utill the data matches one of the list items, a button next to the combobox is disabled.
If the user hits the tab key the autocomplete feature accepts current suggestion. It also moves on to the next control in tab sequence that is enabled. Of course since I want it to go to the disbabled button I need to enable it as soon as I validate the entry.
The problem is that none of the events I've tried, PreviewKeyDown, LostFocus, SelectedIndexChanged allow me to enable the button in time for it to be proccessed and receive the focus. It always goes to the next button in tab order which is always enabled.
I am about ready to leave the button enabled and have it give an error if pressed too soon but I don't want to do it that way. I also don't want to get into have special mode flags to keep track of when these controls receive focus. Validation seems to be a normal thing, but I'm stuck.
If the SelectedIndexChanged worked when the user made a match this would be easy. It doesn't fire when the box clears nor when a typed match is found.
You could create your own ComboBox class to encapsulate this behavior. Something like this:
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.myComboBox1.TheButton = this.button1;
this.myComboBox1.Items.AddRange( new string[] {
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
} );
button1.Enabled = false;
}
}
public class MyComboBox : ComboBox
{
public Control TheButton { get; set; }
public MyComboBox()
{
}
bool IsValidItemSelected
{
get { return null != this.SelectedItem; }
}
protected override void OnValidated( EventArgs e )
{
if ( null != TheButton )
{
TheButton.Enabled = this.IsValidItemSelected;
TheButton.Focus();
}
base.OnValidated( e );
}
protected override void OnTextChanged( EventArgs e )
{
if ( null != TheButton )
{
TheButton.Enabled = this.IsValidItemSelected;
}
base.OnTextChanged( e );
}
}
}
try this :
key_press event :
if (e.KeyData == Keys.Enter)
{
button2.Enabled = true;
button2.Focus();
}
Instead of the event hanlders you mentioned, (LostFocus, SelectedIndexChanged and PreviewKeyDown) use the "Validated" event of your combobox to set the enabled state of the button.
You may need to also manually focus on the button to force the focus to move to it though.
e.g.
private void comboBox1_Validated(object sender, EventArgs e)
{
button1.Enabled = true;
button1.Focus();
}
With some thought to the other answers here I came up with a partial senario that works without using AutoComplete. A side effect is that the PreviewKeyDown event is called a second time and therefore validation is called twice. I wonder why... maybe I should ask another question.
private void comboBox1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) {
if (e.KeyData == Keys.Tab) {
if (ValidationRoutine()) {
e.IsInputKey = true; //If Validated, signals KeyDown to examine this key
} //Side effect - This event is called twice when IsInputKey is set to true
}
}
private void comboBox1_KeyDown(object sender, KeyEventArgs e) {
if (e.KeyData == Keys.Tab) {
e.SuppressKeyPress = true; //Stops further processing of the TAB key
btnEdit.Enabled = true;
btnEdit.Focus();
}
}
Once you turn on AutoCompleteMode with any setting other than None, the KeyDown event doesn't fire for Tab anymore the key is silently eaten.

C# how to trigg a key event in a tabcontrol specific tab?

I have an tabControl1 in my form with three TabPages named TabPage1, TabPage2 and TabPage3.
When TabPage 2 has focus I need to raise an key event (arrow keys for navigation).
This event should not be raised in the other TabPages.
Anybody know how?
On Selected event handler you can cast the sender to the proper control and check for it's name. If the event is generated from TabPage2 you can fire the key event.
Something like this
private void TabPage_Selected(object sender, EventArgs e)
{
TabPage source = sender as TabPage;
if(source.Name.equals("TabPage2"))
//Do whatever...
}
You'll need to derive your own control from TabControl so that you can intercept the arrow keys and generate an event. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto your form.
using System;
using System.Windows.Forms;
class MyTabControl : TabControl {
public event EventHandler<KeyEventArgs> ArrowKeys;
protected void OnArrowKeys(KeyEventArgs e) {
EventHandler<KeyEventArgs> handler = ArrowKeys;
if (handler != null) handler(this, e);
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (keyData == Keys.Up || keyData == Keys.Down || keyData == Keys.Left || keyData == Keys.Right) {
var e = new KeyEventArgs(keyData);
OnArrowKeys(e);
if (e.Handled) return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
Sample usage in a form:
private void myTabControl1_ArrowKeys(object sender, KeyEventArgs e) {
if (myTabControl1.SelectedIndex == 1) {
// Do something with e.KeyData
//...
e.Handled = true;
}
}
protected override bool ProcessCmdKey(ref Message m, Keys keyData)
{
bool blnProcess = false;
if (keyData == Keys.Left)
{
blnProcess = true;
MessageBox.Show("Key left");
if (myTabControl1.SelectedIndex == 1)
MessageBox.Show("inside");
}
}
This code seems to work
So when I have selected the tabPage2 a Messagebox tells me "inside" when i press left arrow key.
Probalby not the correct thing to do thing but atleast it works for now...
I did this in VB .NET, I can post it in C# if you really need it but this should show you how to handle the catching of the event.
Public Class Form1
'give a variable as a TabPage here so we know which one is selected(in focus)
Dim selectedPage As TabPage = TabPage1
'If a key is pressed when the tab control has focus, it checks to see if it is the right tab page
'and then show a message box(for demonstration)
Private Sub TabControl1_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles TabControl1.KeyDown
'The IF Not is to basically catch any odd happening that might occur if a key stroke gets passed with
'no selected tab page
If Not selectedPage Is Nothing Then
'If the tabpage is TabPage2
If selectedPage.Name = "TabPage2" Then
MessageBox.Show("Key Pressed")
End If
End If
End Sub
'This handles the actual chaning of the tab pages
Private Sub TabControl1_Selected(ByVal sender As System.Object, ByVal e As System.Windows.Forms.TabControlEventArgs) Handles TabControl1.Selected
selectedPage = TabControl1.SelectedTab
End Sub
And now you just need to use the actual key presses.
Michael Sarchet
private void Main_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.C) // I used the C Key
{
if (tabControl.SelectedIndex == 0) // control is only in tab 1 (index 0) endabled
{
// Your Code
}
}
}
A C# version of msarchet's answer:
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualBasic;
public class Form1
{
// give a variable as a TabPage here so we know which one is selected(in focus)
private TabPage selectedPage = TabPage1;
// If a key is pressed when the tab control has focus, it checks to see if it is the right tab page
// and then show a message box(for demonstration)
private void TabControl1_KeyDown(System.Object sender, System.Windows.Forms.KeyEventArgs e)
{
// The IF Not is to basically catch any odd happening that might occur if a key stroke gets passed with
// no selected tab page
if (!selectedPage == null)
{
// If the tabpage is TabPage2
if (selectedPage.Name == "TabPage2")
MessageBox.Show("Key Pressed");
}
}
// This handles the actual chaning of the tab pages
private void TabControl1_Selected(System.Object sender, System.Windows.Forms.TabControlEventArgs e)
{
selectedPage = TabControl1.SelectedTab;
}
}
I hope this helps you :)

Categories

Resources