I have four text boxes in my winform, and I've already ordered their tab index sequentially. Also, I set their TabStop properties as true. But when I press Tab key while in filling my first textbox, it does not move to next one. For that, I even added the following part for each of them in the code:
firstTextbox.KeyDown += (sender, args) =>
{
if(args.KeyCode == Keys.Tab)
{
firstTextbox.SelectNextControl(this, true, true, true, true);
}
}
But this didn't help either. Any suggestion?
Responding to tab buttons is handled by the message loop. If you're running the form as modal, or are calling
Application.Run(myForm);
then you've got a message loop. If you're only doing
Form myForm = new Form();
myForm.Visible = true;
Then you do not have a message loop and therefore tab navigation won't work. (If you are not running the form modally, then try using the ShowDialog() method to show the form, and see if tabbing works in this case.)
If this is your issue, then this MSDN article below suggests that you either
Run each Form in its own thread (so that you can call Application.Run(myForm) on it). Running each form in its own thread is a huge can of worms and should not be undertaken by the faint of heart (especially if you have preexisting or poorly designed forms).
Show the dialog modally.
I've had some success with a horrible hack of setting KeyPreview on the form to true and then listening for System.Windows.Forms.Keys.Tab within the Form.OnKeyDown method (and setting KeyEventArgs.SuppressKeyPress and KeyEventArgs.Handled to true if I do handle it). (Also remember to check the KeyEventArgs.Modifiers property to see if the user is doing a [Shift]+[Tab].) The downside of this is that you rob the Control that has a focus on the opportunity to respond to the keypress since Form.KeyPreview causes the Form to get a chance to handle the key before the Control gets a chance. (I'm also not sure what would happen if you implemented this logic and had the message loop going.)
As far as what you should do when you detect the Tab Key, check out the Control.SelectNextControl function. I've been calling it like...
e.SuppressKeyPress = SelectNextControl(
ActiveControl, //Current Control
true, //Move Forward? (You want false for a Shift-Tab to go backward.)
true, //Only stop at controls where Control.TabStop == true?
true, //Consider controls nested within other controls?
true); //Wrap to the beginning of the form if you reach the end?
A person who is smarter than me might be able to set up a shared message loop or something else, but this is all I've figured out so far.
The only way I was able to fire textBox1_KeyDown with the Tab key was by overriding IsInputKey method.
class TabTextBox : TextBox
{
protected override bool IsInputKey(Keys keyData)
{
if (keyData == Keys.Tab)
{
return true;
}
else
{
return base.IsInputKey(keyData);
}
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyData == Keys.Tab)
{
this.SelectedText = " ";
}
else
{
base.OnKeyDown(e);
}
}
and then modify InitializeComponent():
private void InitializeComponent()
{
...
this.textBox1 = new TabTextBox();
...
}
Forms.Control.isInputKey
Every control in windows forms application have property
this.button1.TabIndex = 3;
you can use it to select next control if you order it correctly.
If your textboxes are multiline you need to set the AcceptsTab property to false.
Perhaps your event handler is not being registered. Make sure textboxes are not ReadOnly=true, TabStop=false. Try this
public Form1()
{
InitializeComponent();
this.textBox1.KeyDown += new System.Windows.Forms.KeyEventHandler(this.textBox1_KeyDown);
}
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Tab)
{
textBox1.SelectNextControl(sender as Control, true, true, true, true);
}
}
I ran into a similar problem with tab order completely ignored. After two hours of digging I finally found my dumb mistake: "If mybutton.Focus Then ..." when I meant "If myButton.Focused Then...". Oops. Figured I'd share in case anyone else has this experience too.
Related
I need show droppeddown combobox after start program.
I need in dropdown style only, not simple style.
This is simple fragment of my program:
private void Form1_Shown(object sender, EventArgs e)
{
CB1.Items.Add("1");
CB1.DropDownStyle = ComboBoxStyle.DropDown;
CB1.DroppedDown = true;
}
But I found the watch sign as cursor till I click on Form in any place.
I guessed that my Form have not fully active state and wait for something.
When I click Form (or combobox or any control) by LBM, it activated fully and all works fine.
Of course the combobox is dropup then, so I need click combobox twice.
Đ•ell me please what is correct initialization of such style combobox without "Cursor = Cursors.Default;"
You can simply wait until cursor is the default:
while (Cursor.Current != Cursors.Default)
{
Application.DoEvents();
}
CB1.Items.Add("1");
CB1.DropDownStyle = ComboBoxStyle.DropDown;
CB1.DroppedDown = true;
Application.DoEvents simply process messages from the window queue, so you can process message until you get that cursor is the default. In that moment, you can drop down your control without problem.
If you prefer, create a extension method for the Form:
public static class FormExtends
{
public static void WaitToDefaultCursor(this Form form)
{
while (Cursor.Current != Cursors.Default)
{
Application.DoEvents();
}
}
}
And use it:
this.WaitToDefaultCursor();
CB1.Items.Add("1");
CB1.DropDownStyle = ComboBoxStyle.DropDown;
CB1.DroppedDown = true;
NOTE: I use Cursor.Default but not to change the cursor. The form is processing messages and it's difficult to select a good moment to drop down the control.
I have a Leave Event for a TextEditor in which I perform a validation that an entry is required and display an error message.
Before I perform the validation, I check if the form is disposing, or the Cancel button was clicked. In that case I exit the leave event.
But if the user clicks the X button, these two checks do not capture that and the error message is displayed. I do not want the error message to display if the user clicks the X button. How can I achieve that?
private void TitleTextEditor_Leave(object sender, EventArgs e)
{
UltraTextEditor _currentControl = sender as UltraTextEditor;
if (this.CancelUButton.Focused || this.Disposing)
{
return;
}
if (_currentControl.Text.IsNullOrStringEmpty())
{
MessageBox.Show("Title is required.");
}
}
This is a cruddy problem if you want to suppress the validation error message you display. The only decent way to get ahead of it is by detecting the WM_CLOSE message before the Winforms code sees it and generates the Validating event on the control with the focus.
Paste this code to solve your problem:
protected override void WndProc(ref Message m) {
// Prevent validation on close:
if (m.Msg == 0x10) this.AutoValidate = AutoValidate.Disable;
base.WndProc(ref m);
}
Do consider that you are yelling too loud. The ErrorProvider component is a very decent way to display validation errors and be subtle about it. And nothing drastic goes wrong when the form validates itself on closure, you only have to do this:
private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
e.Cancel = false;
}
In the FormClosingEventArgs you have a CloseReason property, you can probably use it.
How to find out if a control's Form has closed or not. I have listened to the VisibleChanged event in order to divine the Form because ParentChanged can happen before the control is added to a Form (e.g. if it is in a Panel). You might also want to unsubscribe from VisibleChanged events after the first one.
//put this at class level
bool _parentClosed;
//put this in controls constructor
//when control first becomes visible
this.VisibleChanged += (s1, a1) =>
{
//find parent Form (not the same as Parent)
Form form = this.FindForm();
//If we are on a Form
if (form != null)
//subscribe to it's closing event
form.Closing += (s2, a2) => { _parentClosed = true; };
else
throw new Exception("How did we become visible without being on a Form?");
};
I would like to ask the user when he/she clicks the closebutton: save the file, discard the changes, or go back IF the RichTextBox content changed. Like Windows Notepad or any other text editor does. How could I do that?
You need to read about events and how they work. In this case, you are interested in the TextChanged event of the RichTextBox and the FormClosing event of the form.
TextChanged Event : MSDN
The TextChanged event is raised whenever the contents of the textbox are modified. One way to track changes in the textbox is simply to use a boolean value. Be sure to set it to false when you are loading data into the textbox. Then, when the user changes the text the TextChanged event will fire and you can set the _textChanged (in the example below) value to true.
Similarly, making use of the FormClosing event allows you to react to a user attempting to close the form.
FormClosing Event : MSDN
This event passes a FormClosingEventArgs value e that allows you to cancel the closing of the form (in this case if the user selects to cancel when prompted about the text having been changed). It also allows you to perform any other action before the form is closed.
To create the message dialog you can use an appropriate overload of MessageBox.Show - this function returns a DialogResult indicating which of the buttons the user clicked. This allows you to take different actions depending on the selection the user made.
MessageBox.Show : MSDN
If you don't know how to connect these events, then I suggest you read through some of the basic documentation and examples. This is pretty elementary stuff that you will need to understand to get much of anything done in C#. The examples here are winforms since you have not indicated otherwise.
This is a trivial example :
public partial class Form1 : Form
{
private bool _textChanged;
public Form1()
{
InitializeComponent();
// load data to richtextbox, then ....
_textChanged = false;
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
_textChanged = true;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (_textChanged)
{
DialogResult rslt = MessageBox.Show("save changes?", "some caption",
MessageBoxButtons.YesNoCancel);
if (rslt == DialogResult.Yes)
{
// save changes and exit
}
else if (rslt == DialogResult.Cancel)
{
e.Cancel = true;
// cancel close, return to form
}
// else do not save and continue closing form
}
}
}
I want to move a button using a function which will be activated through a keyboard button, but I can't seem to make it accept my input. If I try to run the function through a button press, it works fine, so I know the function is not to blame. What am I doing wrong that it's not accepting my keyboard input?
private void MoveLeft()
{
_y = btnBot.Location.Y;
_x = btnBot.Location.X;
btnBot.Location = new System.Drawing.Point(_x - 10,_y);
}
void MoveLeft_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.A)
{
MoveLeft();
}
}
To ensure that your form key handle event is triggered, even if another control has focus, make sure the KeyPreview property is set to true.
In your main form, add the following line, or set it during design time.
this.KeyPreview = true;
If I do not create an "Edit->Copy" menu item and assign it the shortcut keys "CTRL+C", then I can select a control (RichTextBox, DataGridView, etc..) and hit "CTRL+C" and the control itself will handle the copy. I can copy text out, and paste it into notepad, etc..
Now throughout my whole form, I have a lot of controls. But I have a custom control that I want to make clear that I handle Copy functionality for. So I added the ShortcutKey CTRL+C to Edit->Copy, and by default it is set to Enabled.
Now, I have to implement an event handler for the 'click' event on that menu item. If I explicitly put in code to handle the copy, then it works:
public void menuEditCopy_Click(object sender, EventArgs e)
{
myCustomControl.Copy();
}
However, now Copy does not work on any other type of control. My first inclination was to find out the type of control that has focus, and implement a limited set of copy code for each of them:
public void menuEditCopy_Click(object sender, EventArgs e)
{
if (this.ActiveControl is MyCustomControl)
{
((MyCustomControl)this.ActiveControl).Copy();
}
else if (this.ActiveControl is RichTextBox)
{
((RichTextBox)this.ActiveControl).Copy();
}
}
etc...
However, my controls are added to a SplitContainer, and debugging shows that this.ActiveControl is set to the splitcontainer instance, not the control, even though I know that control is selected.
So my last thought is to literally check if every control has focus:
public void menuEditCopy_Click(object sender, EventArgs e)
{
if (myCustomControl.Focused)
{
myCustomControl.Copy();
}
else if (richTextBox1.Focused)
{
richTextBox1.Copy();
}
}
I would like to avoid this if possible, it is a lot of controls, and if I add a new control, I would need to update it. Is there a better way of doing this?
Thanks
A SplitContainer implements ContainerControl, so you could check for either one and look for it's ActiveControl instead. ContainerControl is the base class, so I would go for that - you might catch another type of container as well:
private void DoCopy(Control control)
{
if(control is ContainerControl)
DoCopy(control.SelectedControl);
else if(control is MyCustomControl)
((MyCustomControl)control).Copy();
else if(control is RichTextBox)
((RichTextBox)control).Copy();
else
throw new NotSupportedException("The selected control can't copy!");
}
void menuEditCopy_Click(object sender, EventArgs e)
{
DoCopy(this.ActiveControl);
}
You could try settting the KeyPreview property of your form to true. Then you could set up a handler for the form's KeyDown event which would look like the following:
private void Form_KeyDown(object sender, KeyEventArgs e)
{
if(e.Modifiers == Keys.Control && e.KeyCode == Keys.C)
{
if (ActiveControl.GetType() == typeof(MyCustomControl))
{
((MyCustomControl)ActiveControl).Copy();
e.Handled = true;
}
}
}
Here you are specifying that you have handled the Ctrl-C event by setting the event args Handled property to true. Else, if you leave it as false, the Ctrl-C key press will be handled as per usual by each individual control.
Because we have set the KeyPreview to true the form's handler gets to see each key press before any other control that it contains and can decide to deal with the key press itself or else allow it to be handled in the same way as if the form had never previewed it.
I think as well it would be necessary to remove the short-cut key from your menu item (although you could still manually put the text "Ctrl+C" next to your menu item name) for this to work, otherwise your menu item will hijack the key stroke.