Hold Arrow Key Down Event with ProcessCmdKey - c#

I need to perform a certain method as long as an arrow key is being held down. Now, for regular arrow key presses in Visual studio 2012, using OnKeyDown did not work, so I needed to use ProcessCmdKey, which works like a charm. Is there a way to use ProcessCmdKey to detect when an arrow has been released / being held down?
I've taken both Markus' and Hans' advice, and combined the two ideas. I used the ProcessKeyPreview method, but I am still having some issues. When I hold down any arrow key, this method will not detect that WM_KEYDOWN has happened... but as soon as I release my finger from the key, it actually DOES notice that WM_KEYUP has happened.
The interesting this is that when I hold down any other key (i.e. letter 'S'), it properly recognizes when it has been pressed and released. I've posted a fragment of my code below:
const int WM_KEYUP = 0x0101;
const int WM_KEYDOWN = 0x0100;
protected override bool ProcessKeyPreview(ref Message m)
{
int msgVal = m.WParam.ToInt32();
if (m.Msg == WM_KEYDOWN)
{
switch ((Keys)msgVal) {
case Keys.Down:
Console.WriteLine("down pressed"); //not detected
break;
case Keys.S:
Console.WriteLine("S pressed!"); //detected
break;
}
}
if (m.Msg == WM_KEYUP)
{
switch ((Keys)msgVal)
{
case Keys.Down:
Console.WriteLine("down released"); //detected
break;
case Keys.S:
Console.WriteLine("s released!"); //detected
break;
}
}
return base.ProcessKeyPreview(ref m);
}

You cannot see the KeyUp event with ProcessCmdKey(), it was made to only handle KeyDown events. You'll need to tackle this at a much lower level if the form contains controls. The trick is to intercept the message before Winforms sends it through the normal key-handling and WndProc chain. That requires implementing the IMessageFilter interface. Like this:
public partial class Form1 : Form, IMessageFilter { // NOTE: added IMessageFilter
public Form1() {
InitializeComponent();
Application.AddMessageFilter(this);
}
protected override void OnFormClosed(FormClosedEventArgs e) {
Application.RemoveMessageFilter(this);
base.OnFormClosed(e);
}
bool IMessageFilter.PreFilterMessage(ref Message m) {
// Trap WM_KEYUP/DOWN for Keys.Down key
if ((m.Msg == 0x100 || m.Msg == 0x101) && (Keys)m.WParam.ToInt32() == Keys.Down) {
bool repeat = (m.LParam.ToInt32() & (1 << 30)) != 0;
bool down = m.Msg == 0x100;
// But only for this form
Form form = null;
var ctl = Control.FromHandle(m.HWnd);
if (ctl != null) form = ctl.FindForm();
if (form == this) {
OnCursorDown(down, repeat & down);
return true;
}
}
return false;
}
private void OnCursorDown(bool pressed, bool repeat) {
// etc..
}
}

You can overload ProcessKeyPreview instead, which will let you see the WM_KEYDOWN message as well as the WM_KEYUP message. Be sure to turn on KeyPreview for the form.
WM_KEYDOWN:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646280(v=vs.85).aspx
WM_KEYUP:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646281(v=vs.85).aspx
Example:
public partial class TestForm : Form
{
public TestForm()
{
InitializeComponent();
this.KeyPreview = true;
}
const int WM_KEYUP = 0x0101;
const int WM_KEYDOWN = 0x0100;
protected override bool ProcessKeyPreview(ref Message m)
{
switch (m.Msg)
{
case WM_KEYDOWN:
Console.WriteLine(m.LParam);
break;
case WM_KEYUP:
Console.WriteLine(m.LParam);
break;
}
return base.ProcessKeyPreview(ref m);
}
}

Related

How to focus arrow keys on ListView while writing in a TextBox?

I have a simple Windows Form containing a TextBox and a ListView.
I want to allow the user to write text into the TextBox while focusing on the ListView (typing text into the TextBox while controlling the ListView with the keyboard arrow keys).
How can I do this?
Implement IMessageFilter for your Form and change the intended recipient when appropriate:
public partial class form1 : Form, IMessageFilter
{
private NativeWindow nw = null;
public form1()
{
InitializeComponent();
nw = new NativeWindow();
nw.AssignHandle(this.listView1.Handle);
Application.AddMessageFilter(this);
// so you can see the selection moving when the arrow keys are pressed in the TextBox
this.listView1.HideSelection = false;
this.listView1.Items[0].Selected = true;
}
private const int WM_KEYDOWN = 0x100;
private const int WM_KEYUP = 0x101;
public bool PreFilterMessage(ref Message m)
{
if (m.HWnd.Equals(this.textBox1.Handle))
{
switch (m.Msg)
{
case WM_KEYDOWN:
case WM_KEYUP:
switch ((Keys)m.WParam)
{
case Keys.Up:
case Keys.Down:
case Keys.Right:
case Keys.Left:
m.HWnd = this.listView1.Handle; // change the handle to the ListView
nw.DefWndProc(ref m); // send the message to the ListView
return true; // suppress handling by the TextBox
break;
}
break;
}
}
return false;
}
}

Handling release key in custom panel control

I have custom control derived from Panel and I need to handle selecting with mouse. I found that for panel I must override ProcessCmdKey and it is working for pressing keys but what if I want to handle when control key is release? Thanks
Perhaps this can help you:
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
protected override bool ProcessKeyPreview(ref Message m)
{
if (m.Msg == WM_KEYDOWN && (Keys)m.WParam == Keys.ControlKey)
{
//Do something
}
else if (m.Msg == WM_KEYUP && (Keys)m.WParam == Keys.ControlKey)
{
//Do something
}
return base.ProcessKeyPreview(ref m);
}
And you could take a look at this (If you haven't already): http://support.microsoft.com/kb/320584

Is it possible to make a C# form non movable [duplicate]

How would i go about stopping a form from being moved. I have the form border style set as FixedSingle and would like to keep it this way because it looks good in vista :)
Take a look at this link. You might be interested in option #3. It will require you to wrap some native code, but should work. There's also a comment at the bottom of the link that shows an easier way to do it. Taken from the comment (can't take credit for it, but I'll save you some searching):
protected override void WndProc(ref Message message)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_MOVE = 0xF010;
switch(message.Msg)
{
case WM_SYSCOMMAND:
int command = message.WParam.ToInt32() & 0xfff0;
if (command == SC_MOVE)
return;
break;
}
base.WndProc(ref message);
}
You can set the FormBorderStyle property of the Form to None
this.FormBorderStyle=System.Windows.Forms.FormBorderStyle.None
I found this to stop the form from moving (its in c#)
protected override void WndProc(ref Message m)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_MOVE = 0xF010;
switch (m.Msg)
{
case WM_SYSCOMMAND:
int command = m.WParam.ToInt32() & 0xfff0;
if (command == SC_MOVE)
return;
break;
}
base.WndProc(ref m);
}
Found here
Try to override WndProc:
protected override void WndProc(ref Message m)
{
const int WM_NCLBUTTONDOWN = 161;
const int WM_SYSCOMMAND = 274;
const int HTCAPTION = 2;
const int SC_MOVE = 61456;
if ((m.Msg == WM_SYSCOMMAND) && (m.WParam.ToInt32() == SC_MOVE))
{
return;
}
if ((m.Msg == WM_NCLBUTTONDOWN) && (m.WParam.ToInt32() == HTCAPTION))
{
return;
}
base.WndProc(ref m);
}
It's not all pretty (there is some flashing going on when you try to move the form), but you can use the LocationChanged property to keep the form where you want it:
private Point _desiredLocation;
// assign the _desiredLocation variable with the form location at some
// point in the code where you know that the form is in the "correct" position
private void Form_LocationChanged(object sender, EventArgs e)
{
if (this.Location != _desiredLocation)
{
this.Location = _desiredLocation;
}
}
Out of curiousity; why would you want to do this?
In Windows, the WS_CAPTION style is the non-client area that allows your window to be moved with a mouse. So the easiest way to do what you want is to remove this style from your window.
However, if you need to have a caption and still achieve what you want, then the next style would be to capture the WM_NCHITTEST message and check for HTCAPTION. If the code is HTCAPTION, return NTNOWHERE instead. This will prevent the default window procedure from executing the default move window thing.
It's not a good practice to make your form immovable. I'd think agfain about it if I were you.
Anyway, you can do this by overridding the WinProc to disable the [Move] menuitem from the system menu.
[DllImport("user32.dll")]
private static extern Int32 EnableMenuItem ( System.IntPtr hMenu , Int32uIDEnableItem, Int32 uEnable);
private const Int32 HTCAPTION = 0×00000002;
private const Int32 MF_BYCOMMAND =0×00000000;
private const Int32 MF_ENABLED =0×00000000;
private const Int32 MF_GRAYED =0×00000001;
private const Int32 MF_DISABLED =0×00000002;
private const Int32 SC_MOVE = 0xF010;
private const Int32 WM_NCLBUTTONDOWN = 0xA1;
private const Int32 WM_SYSCOMMAND = 0×112;
private const Int32 WM_INITMENUPOPUP = 0×117;
protected override void WndProc(ref System.Windows.Forms.Message m )
{
if (m.Msg == WM_INITMENUPOPUP)
{
//handles popup of system menu
if ((m.LParam.ToInt32() / 65536) != 0) // 'divide by 65536 to get hiword
{
Int32 AbleFlags = MF_ENABLED;
if (!Moveable)
{
AbleFlags = MF_DISABLED | MF_GRAYED; // disable the move
}
EnableMenuItem(m.WParam, SC_MOVE, MF_BYCOMMAND | AbleFlags);
}
}
if (!Moveable)
{
if (m.Msg == WM_NCLBUTTONDOWN) //cancels the drag this is IMP
{
if (m.WParam.ToInt32() == HTCAPTION) return;
}
if (m.Msg == WM_SYSCOMMAND) // Cancels any clicks on move menu
{
if ((m.WParam.ToInt32() & 0xFFF0) == SC_MOVE) return;
}
}
base.WndProc(ref m);
}
Also, you can handle OnMove event of your form. But I think this will cause some flickering:
private void Form1_Move(object sender, EventArgs e)
{
this.Location = defaultLocation;
}
Just change the FormBorderStyle property to None.
change the Form property StartPostion to Manual.
Then, handle the LocationChanged event:
private void frmMain_LocationChanged(object sender, EventArgs e)
{
Location = new Point(0, 0);
}
Go to form events-> Location changed
write the following code
Location = new Point(this.Width,this.Height);
I would question your need to make the form unmovable. This doesn't sound nice. You could of course save the location of the window when the window closes and reopen the window into that position. That gives the user some control over where the window should be located.
You can subscribe to the Form.Move event and reposition from it.
Just reset the location on formlocation_changed event to where it was i.e. set the Form.Location to a variable before it's moved and when the user tries to move it, it will go back to the variable location you set it to.
Private Sub MyFormLock()
Me.Location = New Point(0, 0)
End Sub
Private Sub SearchSDR_LocationChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.LocationChanged
Call MyFormLock()
End Sub
You can try:
this.Locked = true;

How to override the minimize control?

I want to override the minimize control to instead of sending the window to the taskbar it would do what ever I write it to do.
Basicly this is what I wanted my new minimized and restored effects to be:
private void ChangeForm(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
this.Height = 80;
iDebug.Visible = false;
mainMenu.Visible = false;
}
else
{
this.Height = 359;
iDebug.Visible = true;
mainMenu.Visible = true;
}
}
I have tried to fire an Event on the Resize to do this but without success
this.Resize += new EventHandler(ChangeForm);
Cancel A WinForm Minimize?
Just tested this and it will make the form 100 pixels shorter when minimize is clicked without flicker.
private const int WM_SYSCOMMAND = 0x0112;
private const int SC_MINIMIZE = 0xf020;
protected override void WndProc(ref Message m) {
if (m.Msg == WM_SYSCOMMAND) {
if (m.WParam.ToInt32() == SC_MINIMIZE) {
m.Result = IntPtr.Zero;
Height -= 100;
return;
}
}
base.WndProc(ref m);
}
The Minimize command has a very well defined meaning to a user, it shouldn't be messed with. Winforms accordingly doesn't have an event for it. But not a real problem, you can detect any Windows message by overriding WndProc(). Like tihs:
private void OnMinimize() {
this.Close(); // Do your stuff
}
protected override void WndProc(ref Message m) {
// Trap WM_SYSCOMMAND, SC_MINIMIZE
if (m.Msg == 0x112 && m.WParam.ToInt32() == 0xf020) {
OnMinimize();
return; // NOTE: delete if you still want the default behavior
}
base.WndProc(ref m);
}

Disable the 'requirement' to double click an unfocused window when clicking on a menustrip

Sorry about the weird title, I'm currently playing around with WinForms and I'm wondering if there is any way to make it so that you don't have to 'double-click' the window to activate an item in a menustrip when the window is unfocused?
Currently if the window is unfocused I first have to click on the window to give it focus and then click once again on the menustrip item even if my mouse was hovering above the menustrip item from the start.
Thanks in advance!
Try putting this function in your Form class:
protected override void WndProc(ref Message m) {
int WM_PARENTNOTIFY = 0x0210;
if (!this.Focused && m.Msg == WM_PARENTNOTIFY) {
// Make this form auto-grab the focus when menu/controls are clicked
this.Activate();
}
base.WndProc(ref m);
}
The method in #Detmar's answer will focus the window when the window is destroyed (see https://msdn.microsoft.com/en-us/library/windows/desktop/hh454920(v=vs.85).aspx). That can cause issues if you have multiple windows in your application and you are exiting. Here is one that won't trigger when you are disposing windows:
protected override void WndProc(ref Message m)
{
const int WM_PARENTNOTIFY = 0x0210;
if (!this.Focused && m.Msg == WM_PARENTNOTIFY)
{
const int WM_CREATE = 0x0001;
const int WM_DESTROY = 0x0002;
const int WM_LBUTTONDOWN = 0x0201;
const int WM_MBUTTONDOWN = 0x0207;
const int WM_RBUTTONDOWN = 0x0204;
const int WM_XBUTTONDOWN = 0x020B;
const int WM_POINTERDOWN = 0x0246;
int type = (int)(0xFFFF & (long)m.WParam);
switch (type)
{
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_XBUTTONDOWN:
case WM_POINTERDOWN:
// Make this form auto-grab the focus when menu/controls are clicked
this.Activate();
break;
case WM_DESTROY:
case WM_CREATE:
//do nothing
break;
}
}
base.WndProc(ref m);
}

Categories

Resources