How to override natural navigation in c# winforms - c#

Domain understanding
I'm making an application for dentists and at one point I have to show the teeth of the patient and allow the dentist to register information about several points around each tooth.
To make the application dynamic I create the teeth depending on the patient (some have more or fewer teeth).
So the mouth has a number of teeth and depending on the settings each tooth has a number of measurement points.
The dentists register the points in different orders depending on preference (set in settings). Some register all points on a tooth and then move to the next, others register all points along the front of all teeth and then all the points along the back of the teeth.
The issue
As I create each tooth and add the measurement points to it, the natural tab order is: all the points on one tooth, move to next tooth [repeat].
I need to control this in a different way.
I need to be able to navigate (tab) to a control in another usercontrol and then return later for the rest of the points.
Both tab and the arrow keys cause navigation, so I have to deal with both (well all navigation, but these are the ones I've discovered to far)
Trial and error
I tried to override OnKeyUp/Down/Press and handle the events for the key press, but to no avail.
In stead I get a double effect; both my navigation AND the natural navigation.
What is the smartest and easiest way to be in complete control of the navigation?

For Tab there is a ContainerControl function called ProcessTabKey. This is where the natural navigation for Tab happens. If you override this you can provide our own navigation: (or just return true and do it somewhere else)
protected override bool ProcessTabKey(bool forward)
{
return forward ? this.NextPoint() : this.PreviousPoint();
}
As for the arrow-navigation, I've found that to be a little trickier. There are a number of overridable functions that register key presses.
I think the best one is ProcessCmdKey. This function must return "true if the character was processed by the control; otherwise, false":
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Right)
{
this.NextField();
return true;
}
if (keyData == Keys.Left)
{
this.PreviousField();
return true;
}
if (keyData == Keys.Down) return true;
if (keyData == Keys.Up) return true;
return base.ProcessCmdKey(ref msg, keyData);
}
This will cause my own navigation to happen and let the framework know 'I already dealt with this' (or I could just return true and handle it somewhere else). Any other 'command' keys can be handled the same (like Tab), however this does not handle the modifiers!
Both Tab and the Arrow keys navigate if Shift is pressed. If I want to handle all my navigation here it should look something like this:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
switch (keyData)
{
case Keys.Right:
case Keys.Right|Keys.Shift:
case Keys.Enter:
// move to next field
this.NextField();
return true;
case Keys.Left:
case Keys.Left|Keys.Shift:
// move to previous field
this.PreviousField();
return true;
case Keys.Down:
case Keys.Down|Keys.Shift:
case Keys.Up:
case Keys.Up|Keys.Shift:
// disable native navigation
return true;
case Keys.Tab:
// move to next point
return this.NextPoint();
case Keys.Tab|Keys.Shift:
// move to previous point
return this.PreviousPoint();
}
return base.ProcessCmdKey(ref msg, keyData);
}
Handling Tab in ProcessCmdKey negates the need to override ProcessTabKey .

Related

Child class of Systems.Windows.Forms.TextBox will not let me edit text

In Visual Studio 2017, I have created a class to inherit System.Windows.Forms.TextBox in its own file under the same namespace for the project. I am only overriding one method ProcessCmdKey() so that I can use ctrl+a keyboard shortcut to select all text in the text box that is created. Multiline textboxes for some reason only does not allow ctrl+a shortcut for whatever reason. When I build the app and run it i am not able to type at all in my child textbox. Here is the class:
namespace Amatechtel_Notetaker
{
public class ITextBox : System.Windows.Forms.TextBox
{
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
switch (keyData)
{
case Keys.Control | Keys.C:
Copy();
break;
case Keys.Control | Keys.V:
Paste();
break;
case Keys.Control | Keys.X:
Cut();
break;
case Keys.Control | Keys.A:
SelectLine();
break;
}
return true;
}
//selects the current line of text for the formatting methods and allows
//for formatting shortcuts (e.i. ctrl+c, ctrl+a, ect) to be used.
internal void SelectLine()
{
SendKeys.Send("{HOME}");
SendKeys.Send("+{END}");
}
}
}
In the Form1.Designer class I am creating an object (field) of ITextBox called notesTextBox which is being used for the main text box for the app. SelectLine() is being used to select the current line the user is editing for a copy button that i have added and have a later implementation for. When I build the project i am not able to type freely in the notesTextBox. If I build the project the same way but using Windows.System.Forms.TextBox object i am able to type freely in the text box. Ctrl+C, V, and X all work in ITextBox but that behavior inherited from the parent. I feel like i am making a simple mistake but anyu help would be great. Please let me know any additional information on the project i need to include
Add default: return false; to your switch. Your current code eats all keypresses.

How to Disable Shortkeys on ComboBox In C# using Windows Application?

In Main Form i have Short Cut Keys(ctrl + S ) for saving the data in DataBase. For combobox (ctrl + Alphabets) is the default Operation. How can I disable shortcut keys for Combobox?
UPDATE:
On keyPress event we can Do this
if (ModifierKeys == Keys.Control)
{
e.Handled = true;
}
You can try overriding ProcessCmdKey method of the Main Form:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
const int WM_KEYDOWN = 0x100;
if (keyData == (Keys.Control | Keys.S)) {
if (msg.Msg == WM_KEYDOWN)
MySaveDataToDatabase(); // <- Do your save command
return true; // <- Stop processing the WM_KeyDown message for Ctrl + S (and shortcut as well)
}
// All other key messages process as usual
return base.ProcessCmdKey(ref msg, keyData);
}
I get the feeling that your problem is actually that the combobox is getting keypresses before your form. Be sure to set the Form's KeyPreview property to true if you want to handle keypresses before your controls.
More information about the KeyPreview property:
http://msdn.microsoft.com/en-us/library/system.windows.forms.form.keypreview(v=vs.100).aspx
If you are getting issues like overriding the F4 key (which in a combobox shows all options available by default), note the bit about setting the KeyPressEventArgs.Handled property in the form's KeyPress event handler to true.
For most hot keys you can override the PreviewKeyDown event specify what to allow or not, eg:
void comboBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
var keysToIgnore = new []{
Keys.S,
Keys.X,
Keys.F4,
Keys.Space,
}.ToList();
if (keysToIgnore.Contains(e.KeyCode)) {
if(e.Modifiers == Keys.Alt) ; // Do stuff (or don't) here
}
}
There's all sorts of ways you can structure your logic depending on what your actual use case is but hopefully you get the idea.
You might find some hot key combinations aren't possible to disable this way, in which case you'll need to use the Win32API: UnregisterHotKey. More info here:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646327%28v=vs.85%29.aspx
The main advantage of using the PreviewKeyDown event is you can also easily replace behaviour instead of just disabling it. The main advantage of using UnregisterHotKey is it operates at a lower level and is a far more robust and reliable way of disabling hot keys.
EDIT:
If you need to get a contiguous range of values you can try something like:
int keyCode = (int) (e.KeyCode);
if(keyCode >= (int)(Keys.A) && keyCode <= (int)(Keys.Z)) { /* do stuff */ }
but again, an ideal solution really depends on the specifics of your use case. There's no generic 'right answer'.

inheritance and event handling for non-instantiated object C#

I'm having a little trouble figuring out how to create an inherited class that extends a windows form control to always have an event handler that will handle a keypress event for each instance of that object.
I'm probably explaining this poorly. Essentially I want to extend the DatagridView class in windows forms to always have a keyPress event handler present for any instantiated object of my extended DatagridView class.
I was wondering if it's possible to have an event handler that listens for key presses and handles them with code similar to what I have written below:
private void dgvObject_KeyPress(object sender, KeyPressEventArgs e)
{
if (Char.IsLetterOrDigit(e.KeyChar))
{
//start the loop at the currently selected row in the datagridview
for (int i = dgvObject.SelectedRows[0].Index; i < dgvObject.Rows.Count; i++)
{
//will only evaluate to true when the current index has iterated above above the
//selected rows index number AND the key press event argument matches the first character of the current row
// character of the
if (i > dgvObject.SelectedRows[0].Index && dgvObject.Rows[i].Cells[1].FormattedValue
.ToString().StartsWith(e.KeyChar.ToString(), true, CultureInfo.InvariantCulture))
{
//selects current iteration as the selected row
dgvObject.Rows[i].Selected = true;
//scrolls datagridview to selected row
dgvObject.FirstDisplayedScrollingRowIndex = dgvObject.SelectedRows[0].Index;
//break out of loop as I want to select the first result that matches
break;
}
}
}
}
The code above simply selects the next row that begins with the character of whatever the keypress event has in its event argument when fired. The reason I was wondering if I could have this as an inherited handler that is always present. I figured it'd be better than explicitly creating hundreds of handlers in my windows form for each individual DatagridView object. If my thinking is wrong please feel free to correct me! Anyway thanks for any input.
I've been programming in C# for about 5 months now, still learning as I go =)
Yes, in your inherited class just override OnKeyPress, and you should remember to call base.OnKeyPress afterwards:
protected override OnKeyPress(KeyPressEventArgs e)
{
.. all your code
base.OnKeyPress(e); // to ensure external event handlers are called
}
You can catch all key presses and even combinations by overriding ProcessCmdKey:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Control | Keys.F))
{
//your code here
}
return base.ProcessCmdKey(ref msg, keyData);
}

Picking up keystrokes Ctrl-Alt-Q

What's the 'correct/best' way to pick up a keystroke combination? The keys in question are Ctrl+Alt+Q, I want the user to press them all at the same time, at which point I am going to open a window. I'm currently doing it by have an array and then catching each keystroke individually, but my results are inconsistent, especially on a particular make of Dell laptop/windows 7 combination, but that's another story.
So after spending five minutes with google, this is just a rough version after looking at the msdn, but as I stated earlier is this version (untested) the correct/best way of doing it?
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
const int WM_KEYDOWN = 0x100;
const int WM_SYSKEYDOWN = 0x104;
if ((msg.Msg == WM_KEYDOWN) || (msg.Msg == WM_SYSKEYDOWN))
{
switch(keyData)
{
case Keys.Control | Keys.Alt | Keys.Q:
this.Parent.Text="<CTRL> + Alt + Q Captured";
break;
}
}
return base.ProcessCmdKey(ref msg,keyData);
}
Ignore msg.Msg, only look at keyData. And return true without calling base if you use the keystroke. Which simplifies it to:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == (Keys.Control | Keys.Alt | Keys.Q)) {
this.Parent.Text="<CTRL> + Alt + Q Captured";
return true;
}
return base.ProcessCmdKey(ref msg,keyData);
}
This should probably be an override of the form's method so you don't depend on the control having the focus. You'd use this.Text instead.
Yes, that's how I would do it if I wish to listen for key events globally.
But if you are interested in keyboard inputs ONLY when your program is the focused window (in the front on Windows), you might want to use the KeyDown event:
private void textBox1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
// your code goes here
}
According to the documentation for System.Windows.Forms.Keys, you cannot use a bitwise OR to check for key combinations. The documentation states: "For finer control, use the Win32 API functions GetKeyState, GetAsyncKeyState, or GetKeyboardState defined in user32.dll...". But it appears that Shift, Alt and Control can be used as bitwise flags.
For those who wants open a windows or make visible/invisible with the combination of three keys (with two modifiers) by the light of #Hans Passant i found finally an answer to my problem. I was beware of | operand, putting instead && operand which was not working. It can be assigned to WFA properties to Keydown
private void ShowHidden_Click(object sender, KeyEventArgs e)
{
if (e.KeyData == (Keys.Control | Keys.Alt | Keys.M) )
{
OldBisProdId.Visible = true;
OldGanProdId.Visible = true;
}
if (e.KeyData == (Keys.Control | Keys.Alt | Keys.C))
{
OldBisProdId.Visible = false;
OldGanProdId.Visible = false;
}
}

Multiple key presses doing different events in C#

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.W)
player1.moveUp();
if (e.KeyCode == Keys.NumPad8)
player2.moveUp();
}
In the above code the moveUp methods basically just increment a value. I want it so both keys can be pressed (or held down)at the same time and both events will trigger.
Thanks,
Nevik
Get the state of the keyboard and check for the status of the keys that you want.
Events are not the best way to go in gaming. You need faster response.
[DllImport("user32.dll")]
public static extern int GetKeyboardState(byte [] lpKeyState);
...
byte[] bCharData = new byte[256];
GetKeyboardState(bCharData);
Another way, taken from here,
[DllImport("user32.dll")]
static extern short GetKeyState(VirtualKeyStates nVirtKey);
...
public static bool IsKeyPressed(VirtualKeyStates testKey)
{
bool keyPressed = false;
short result= GetKeyState(testKey);
switch (result)
{
case 0:
// Not pressed and not toggled on.
keyPressed = false;
break;
case 1:
// Not pressed, but toggled on
keyPressed = false;
break;
default:
// Pressed (and may be toggled on)
keyPressed = true;
break;
}
return keyPressed;
}
More links.
Basically, these are already available on net. Try searching before asking. It will be faster :)
Let's assume you have a "game loop" that updates the object you're moving with the keyboard. The KeyDown event should change the object state to "moving upwards". And your loop then gives it new positions each time it runs.
The KeyUp event should change the state back to "idle". Iff the state is still "moving upwards".
You now no longer depend on a keystroke repeating to keep the object moving. And will have no trouble with the player pressing multiple keys at the same time.

Categories

Resources