How to prevent Enter press from closing menu - winforms

I'm embedding a Colour Picker into a Context Menu using the Windows.Forms.ToolStripControlHost class. The picker displays fine and handles all mouse events properly:
The problem arises when one of the channel sliders is double clicked. This causes the control to add a Windows.Forms.TextBox into the parent control with the same dimensions as the slider so users can enter numeric values. When Enter is pressed while the TextBox has focus, it should assign the value and hide the textbox (which it does), but it also closes the entire menu structure. So, how do I keep the menu alive?
There's an awful lot of code involved but I'll post it if needed.

Somehow, you'll need to eat the Enter key presses before they reach your context menu. Obviously, it's default behavior is to "select" the currently highlighted item when the user presses Enter, just like every other menu control known to man.
You would do that by subclassing the ContextMenuStrip control (if you're not doing so already), and overriding its ProcessCmdKey method. Watch for a keyData value corresponding to Keys.Enter, and when you detect that value, return True to indicate that the character was already processed by the control and prevent it from being passed on for any further processing. Everything else, of course, you'll let the base class process so the behavior of other keys (such as the arrow keys) is unchanged.
For example (I just tested this and it works fine):
public class CrazyContextMenuStrip : ContextMenuStrip
{
protected override bool ProcessCmdKey(ref Message m, Keys keyData)
{
if (keyData == Keys.Enter)
{
// Eat it when the user presses Enter to
// prevent the context menu from closing
return true;
}
// Let the base class handle everything else
return base.ProcessCmdKey(m, keyData);
}
}
And of course, you could add extra checks to the above code so that the Enter key presses are only eaten when your color picker is visible, allowing things to work as expected all the rest of the time,

Related

How do I do something when I press a key on the keyboard?

I want to make a basic game using Windows Forms.
I want to end the turn of a player when I press Space on the keyboard. I've tried using getch(), but it isn't a good solution because the character has to be entered in the debug window, as it isn't constantly polling for a key press.
A basic pseudocode example of what I need is something like this:
if(PlayerPressesSpace)
{
//end turn of player (I've got this bit figured out)
}
In a Windows Forms GUI app, each Form, and every child Control inside of it, has its own KeyDown, KeyUp, and KeyPress events that you can assign individual handlers to as needed.
Or, you can handle all key events within an entire Form instead of on a per-Control basis, by setting the Form's KeyPreview property to true and then using the Form's KeyDown event. See How to handle keyboard input messages in the form (Windows Forms .NET) for more details.
In a WinForms Form, you can override ProcessCmdKey of the Form container, to pre-process Key presses before they're dispatched to the destination. See also the Docs about this.
This allows to intercept keys that are used to activate default action, as the TAB Key, used to move the Focus to the next Control in the TabIndex hierarchy.
You can then perform any action required before the default action is performed, or suppress the Key, returning true when the conditions you have set are met.
It can also be used to generate special actions when combinations of Keys are pressed.
protected:
virtual bool ProcessCmdKey(Message %msg, Keys keyData) override {
if (keyData == Keys::Space) {
/* do something */
// Suppress the Key press, the message is not sent to the destination recipient
return true;
}
// Otherwise, let the message pass through
return Form::ProcessCmdKey(msg, keyData);
}
If you just need to intercept Keys that don't have special uses, as the aforementioned TAB Key, or some other Key that is processed by specific Controls for their internal use - so the Parent may or may not be notified - you can set the KeyPreview property of your Form container to true and handle the KeyDown event, as mentioned in Remy Lebeau answer.
When KeyPreview is set to true, the Form class sets a state, formState[FormStateKeyPreview]; its ProcessKeyPreview override checks this state and behaves as the ProcessCmdKey override in most cases.
I can only think of
if(Console::ReadKey(true).KeyChar == ' ') {
...
}
Console.ReadKey Method Documentation

How to know if the AutoComplete listbox on an TextBox is open?

I have a form with 2 textboxes. The first one has a customsource for its autocompletion set like this :
textBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
textBox1.AutoCompleteCustomSource = GetUserNames();
The GetUserNames() method retuns an AutoCompleteCustomSource and all this works very well.
When this form opens the focus in on the first textbox, the user can type in or choose from the autocomplete and that works well.
Both textboxes have an onKeyDown event and in that event they should do some validations using the values of both textboxes.
I want to keydown to only do its validations when ENTER is pressed when the autocomplete listbox is closed.
Look at it this way:
the user starts typing, a list appears, the user chooses an item from the list and presses enter to confirm his choice, and then he wants to press TAB to go to the next textbox.
But when he presses ENTER after choosing an item in the autocomplete list the keyDown event already fires. At this stage the keydown event should not fire, the ENTER should only confirm the choice from the autocomplete list.
Is there a way to detect in the keydown that ENTER was pressed while the autocomplete list was still open ?
Or is there a way to disable the keydown event while the autocomplete list is open ?
EDIT:
from the comments I tried the answer in this link https://stackoverflow.com/a/40915048/3110834
Unfortunate it does not works in this case but it has teached me that pressing enter on the autocomplete suggestion does 2 things:
close the autocomplete window
fire the keydown event of the textbox
So I need to find a way to stop the keydown event for the textbox to fire when pressing enter on the autocomplete window.
Edit:
Things are far worse than I thought.
When you open the autosuggest box and then click on a suggested item to select it, the keydown event also fires and it has Keys.Enter in its KeyCode ! Since when is a click equal to a keystroke ?
How do I stop this ? Is this a bug ?
I ran into this same problem, I followed the approach in this link https://stackoverflow.com/a/40915048/3110834
if you try to evaluate if there is a list open during the Keydown event, it will always return a false, since the event already closed the list.
instead I observe the PreviewKeyDown event to evaluate if the AutoComplete list is open or not, if it is Open, i unsubscribe from the KeyDown event (which only uses the Enter key on my case) and if the list is close, i re-subscribe to it back again
private void tbxAND_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
if (IsAutoCompleteOpen())
{
tbxAND.KeyDown -= tbxAND_KeyDown;
}
else
{
tbxAND.KeyDown += tbxAND_KeyDown;
}
}
}
notice that IsAutoCompleteOpen() starts the code to enumerate the openlist.
this may not be the full solution for this particular case if you still need to catch other keys, but i wanted to leave the hint in case someone else run into this problem.

Handling Key Pressed Events + WinForms + Validation + Cancel

I am implementing a WinForms form in a C# project.
My form is a child of a MDI form.
My form contains a user control.
My user control contains some buttons including a validation button and a cancel one.
I want to implement the following logic :
When my form is active and the user presses the enter key then I want the validation button clicked event to be fired automatically.
When my form is active and the user presses the escape key then I want the cancel button clicked event to be fired automatically.
If my validation and my cancel buttons were not included in a user control then I would probably set the AcceptButton and CancelButton properties of my form.
Here is the code I have written in the Load event handler of my user control according to a tip given by Arthur in a comment to my first post :
// Get the container form.
form = this.FindForm();
// Simulate a click on the validation button
// when the ENTER key is pressed from the container form.
form.AcceptButton = this.cmdValider;
// Simulate a click on the cancel button
// when the ESC key is pressed from the container form.
form.CancelButton = this.cmdAnnulerEffacer;
Set the KeyPreview Property of your from true from properties;
Add keyDownEvent to your Form
In keyDownEvent of your Form, include following lines of code
The code
if(e.KeyValue==13)// When Enter Key is Pressed
{
// Last line is performing click. Other lines are making sure
// that user is not writing in a Text box
Control ct = userControl1 as Control;
ContainerControl cc = ct as ContainerControl;
if (!(cc.ActiveControl is TextBox))
validationButton.PerformClick(); // Code line to performClick
}
if(e.KeyValue==27) // When Escape Key is Pressed
{
// Last line is performing click. Other lines are making sure
// that user is not writing in a Text box
Control ct = userControl1 as Control;
ContainerControl cc = ct as ContainerControl;
if (!(cc.ActiveControl is TextBox))
cancelButton.PerformClick(); // Code line to performClick
}
validationButton or cancelButton are the names of your buttons which I am just supposing. You may have different ones. Use Your names instead of these two if you have different.

Control.Leave Triggered by MouseDown, not MouseUp

I have a C# .NET WinForm. In the form, I allow a user to add an item to a ListView by double-clicking in the ListView. This adds a TextBox control to the ListView and places the key cursor in the TextBox so a user can type.
I detect that a user is done with an item in a couple of ways (e.g. pressing Enter, Esc, Tab...), but also when they Leave (TextBox.Leave) the TextBox.
The problem is this set of steps:
User triggers TextBox.Leave by mousing down outside of the TextBox.
I add the new item to the ListView.
I select the the new item in the ListView.
Mouse up occurs and the new item that I just selected, loses focus and is unselected.
What I would like is for TextBox.Leave to be triggered by MouseUp, not MouseDown. How can I accomplish this?
Edit: Cody suggests using the ListView.LabelEdit property. Here are my results trying that:
listView_DoubleClick(...) {
listView.LabelEdit = true;
if(double clicked on existing listViewItem) {
listViewItem.BeginEdit(); //this works as expected
} else {
var newItem = listView.Items.Add("");
newItem.BeginEdit(); //this doesn't work, see below
}
}
The call to newItem.BeginEdit() only works when the user double clicks where the new item will show up. If they double click on any other blank area in the listview the new item is added, but it does not enter edit mode. What's going on here?
Pressing the mouse down on another control is causing that other control to request the focus and so the focus moving causes the TextBox.Leave event to occur. Preventing ever other possible control from requesting the focus is not a very viable option. But luckly you only need to prevent the ListView from using the MouseDown to shift focus. So you need to override the WndProc of your ListView and when the MouseDown windows message occurs and you are currently showing a TextBox you eat the message. In order words you do not allow the base class to process it.

Re-open the DropDown of an AutoCompleteBox in Silverlight when a user presses Enter

If a user types a few characters into an AutoCompleteBox, the DropDown is displayed as expected.
If the user then clicks elsewhere on the page, this removes the focus from the AutoCompleteBox, and the DropDown disappears... also as expected.
However, if a user then returns focus to the AutoCompleteBox, and wants to redisplay the DropDown, filtering on the characters already there, the instinct seems to be to press the Enter key to re-filter/re-display the results. This, however, does not happen.
I've managed to capture the Enter key event (must use KeyUp instead of KeyDown), but I'm not sure how to make the DropDown re-appear at that point. Ideas?
Just assign true to the IsDropDownOpen property:-
myACB.IsDropDownOpen = true;

Resources