Preventing Focus-able Controls from stealing Keyboard Input - c#

I'm currently working on a simple game that is drawn on a form by overriding the OnPaint method. The game requires Keyboard input and was working perfectly until I decided to enhance the GUI and add a few Buttons to the form.
The moment I added these Buttons, the form stopped receiving any Keyboard input, no matter how hard I tried the focus was always on the buttons. This behavior can be replicated by placing any Focus-able Control on the form. (ie. TextBox)
I don't need ANY Kayboard interaction with these buttons, I want the user to interact with them with the mouse only.
I've tried the following techniques to try and get around this problem - none of these worked:
1) Normal KeyDown and KeyUp events of the form. (This is the way
I was capturing Keyboard input before placing the buttons.)
2) Overriding the Form's OnKeyDown and OnKeyUp events.
3) Overriding ProcessCmdKey - Works, but cannot differentiate
between KeyUp and KeyDown events, so it is inadequate for me.
I also tried create a MessageFilter for the application, but I couldn't force it to capture only the Keyboard keys that I needed.
I've been looking into this for many hours already and can't find a suitable solution.
Help would be greatly appreciated.

Here is a sample form with a IMessageFilter for the up and down arrow keys, hope this helps:
public partial class MainForm : Form
{
private class MessageFilter : IMessageFilter
{
public MainForm Main { get; set; }
public bool PreFilterMessage(ref Message msg)
{
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
if (msg.Msg == WM_KEYDOWN)
{
var keyData = (Keys)msg.WParam;
if (keyData == Keys.Down || keyData == Keys.Up)
{
return true; // Process keys before return
}
}
else if (msg.Msg == WM_KEYUP)
{
var keyData = (Keys)msg.WParam;
if (keyData == Keys.Down || keyData == Keys.Up)
{
return true; // Process keys before return
}
}
return false;
}
}
public MainForm()
{
this.InitializeComponent();
Application.AddMessageFilter(new MessageFilter { Main = this });
}
}
For a list of possible Windows messages check:
List Of Windows Messages

Set the KeyPreview property of the form to True, and then set event.Handled = True when you handle KeyDown/KeyUp. This will ensure that the form gets a chance to handle events before its children. Because you set the handled property to true, the childen won't see the keyboard events.
More info here: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.keypreview.aspx

Related

How to check whether the program is being used?

I need to close the application (C#) when user doesn't use it - let's say that when there is no Click event on any form of the program (there are about 100 forms). Is there any way to do that without handling Click even on each form of the app. (I have the thread running each minute, where it could be checked)?
Thanks in advance!
You can hook into the application message loop using the Application.AddMessageFilter function. Write a message filter that inspects all mouse click messages and/or keyboard messages, or anything you're interested in.
For instance:
public class DetectActivityMessageFilter : IMessageFilter
{
private const int WM_LBUTTONDOWN = 0x0201;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_LBUTTONDOWN)
{
// The left mouse button was pressed
}
return false;
}
}

How to display only my application forms when pressing Key1 + Key2, just like Alt + Tab does in Windows?

My application has several forms.
What I want to do is to be able to switch between them (and only between them) using a pair of predefined keys (say Keys.A + Keys.B), just like ALT + TAB does for Windows (windows are shown in foreground).
I tried getting the list of forms and then programmatically call Alt + Tab, but, as expected, this allows to switch between all open windows and not only the ones belonging to the application.
Thanks for any help!
You could implement IMessageFilter and add it to your Application, then globally process the messages of your application.
Here is how you do that.
public class MessageFilter : IMessageFilter
{
public bool PreFilterMessage(ref Message m)
{
switch ((WindowsMessage)m.Msg)
{
case WindowsMessage.WM_KEYDOWN:
case WindowsMessage.WM_SYSKEYDOWN:
{
if (((int)m.WParam | ((int)Control.ModifierKeys)) != 0)
{
Keys keyData = (Keys)(int)m.WParam;
var activeForm = Form.ActiveForm;
var forms = Application.OpenForms.Cast<Form>().Where(x => x.Visible).ToArray();
int active = Array.IndexOf(forms, activeForm);
if (keyData == Keys.A)
{
int next = (active + 1)%forms.Length;
forms[next].Activate();//Activate next
}
else if (keyData == Keys.B)
{
int prev = (active - 1) % forms.Length;
forms[prev].Activate();//Activate previous
}
break;
}
}
return false;
}
}
class MainForm : Form
{
protected override void OnLoad(EventArgs e)
{
Application.AddMessageFilter(new MessageFilter());
}
}
You can find WindowsMessage enumeration here.
If you have multiple windows (forms) opened and they have normal caption or belong to MDI, then Ctrl+F6 is a standard shortcut to switch between them.
Otherwise, making a hotkey, to switch between forms is pretty trivial task:
all forms have to have KeyPreview = true and KeyDown | KeyUp event;
all forms instances have to be accessible (one possibility is Application.OpenForms);
when hotkey is pressed, find next / previous window and make it active.

.NET Interpret Enter key as Tab on textbox

This simple problem reoccurs many times for me in .NET with WinForms. I have a textbox and would like to treat the Enter key as the same as the Tab key, as in whenever I press enter, I want lostfocus to be raised, the next control to be Focused, etc. I have noticed many similar questions to this, and I can solve the problem by capturing the KeyDown and when Enter is detected to move the focus, but what I am looking for is a best practice and if not for insight on where the Textbox control detects the Tab key internally and how it does it's thing so I can just add an "|| key == Keys.Enter". This would stop Keydown from being raised and instead move focus to the next control and raise other appropriate messages.
I checked this similar question before
.NET TextBox - Handling the Enter Key but it isn't what I really want to accomplish. What i really want to do is to as early as possible interpret ENTER as TAB in the Textbox control or intercept and change the Enter to a Tab and let the Textbox do it's thing.
Edit: Here's something along the lines of what I'm looking for, but I'm not sure the "safety" of modifying the Windows message like this...
class FancyTextBox : TextBox
{
public bool TreatEnterAsTab { get; set; }
const int WM_KEYDOWN = 0x100;
const int KEY_TAB= 9;
const int KEY_ENTER = 13;
public override bool PreProcessMessage(ref Message msg)
{
if (TreatEnterAsTab && msg.Msg == WM_KEYDOWN && msg.WParam.ToInt32() == KEY_ENTER)
msg.WParam = new IntPtr(KEY_TAB);
return base.PreProcessMessage(ref msg);
}
}
This is fine. Just a few details, you don't want to do it if the text box is multiline or when the Shift, Alt or Ctrl key is down. Turn it on by default, that's why you're using it:
using System;
using System.ComponentModel;
using System.Windows.Forms;
class FancyTextBox : TextBox {
public FancyTextBox() {
TreatEnterAsTab = true;
}
[DefaultValue(true)]
public bool TreatEnterAsTab { get; set; }
public override bool PreProcessMessage(ref Message msg) {
if (TreatEnterAsTab && (!this.Multiline || this.AcceptsReturn) &&
Control.ModifierKeys == Keys.None &&
msg.Msg == WM_KEYDOWN && (Keys)msg.WParam.ToInt32() == Keys.Enter) {
msg.WParam = (IntPtr)Keys.Tab;
}
return base.PreProcessMessage(ref msg);
}
private const int WM_KEYDOWN = 0x100;
}
This may be not what you are looking for, but what I have done in the past is to check for enter in the KeyPress event and use Control.SelectNextControl to use the existing tab sequencing

C#, WinForms: What would prevent KeyDown events from chaining up from focused control up to main Form? Only leaf controls KeyDown works for me

As i understand it, when a keyboard button is pressed it should invoke the KeyDown event for the control which has focus. Then, the KeyDown for the parent control, so on and so forth until it reaches main form. UNLESS - along the chain one of the EventHandlers did:
e.SuppressKeyPress = true;
e.Handled = true;
In my case, KeyDown events never get to the main form.
I have Form -> Panel -> button for example.
Panel doesn't offer a KeyDown Event, but it shouldn't stop it from reaching the main form right?
Right now as a work around I set every single control to call an event handler I wrote. I'm basically trying to prevent Alt-F4 from closing the application and instead minimize it.
[Edit]
If you want to trap Alt-F4 then there's no point trying at the control level as that keystroke is handled by the application - see How to Disable Alt + F4 closing form?
You can use an application message filter:
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.AddMessageFilter(new TestMessageFilter());
Application.Run(new Form1());
}
}
public class TestMessageFilter : IMessageFilter
{
private int WM_SYSKEYDOWN = 0x0104;
private int F4 = 0x73;
public bool PreFilterMessage(ref Message i_Message)
{
Console.WriteLine("Msg: {0} LParam: {1} WParam: {2}", i_Message.Msg, i_Message.LParam, i_Message.WParam);
if (i_Message.Msg == WM_SYSKEYDOWN && i_Message.WParam == (IntPtr)F4)
return (true); // Filter the message
return (false);
} // PreFilterMessage()
} // class TestMessageFilter
}
Try creating an observer to capture your events:
http://ondotnet.com/pub/a/dotnet/2002/04/15/events.html

Capture Keystrokes

I need to capture keystokes from a wedge device and prevent any controls or the form from receiving them. I also need to be able to know the value (char). I have tried overriding the ProcessCmdKey(Keys) and ProcessDialogChar(char) events. In the ProcessCmd event I can suppress the keystroke if it's one I want, but I the character isn't a parameter of the event. In the ProcessDialogChar event the character is there, but I cannot prevent the form or control with the focus from receiving the key. Suggestions?
You'll want to add this during Form load:
Application.AddMessageFilter(this);
Add this constant:
private const int WM_KEYDOWN = 0x100;
And this method:
public bool PreFilterMessage(ref Message m)
{
Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode;
bool retVal = false;
if (m.Msg == WM_KEYDOWN)
{
// Handle the keypress
retVal = true;
}
return retVal;
}
By returning true, your form and control(s) will never see the key press.
It was not how I wanted to do it, but because I needed to fix this and move on I put a hidden textbox on the form and as soon as I see the character that signals the possible start of the string of data I want to capture I set focus to that text box and respond to the TextChanged event. If I haven't seen the ending character before I timer expires I clear the textbox and start again. Kludge, but it works and got me to the next task.

Categories