I have a Window with a TextBox. The cursor is inside the TextBox. If I press a key, then I receive a message in WndProc (for KeyUp and KeyDown). But if I set e.Handled = true in the KeyUp and KeyDown events, then I don't receive any key messages:
public partial class MainWindow : Window
{
public MainWindow()
{
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var textBox = new TextBox();
textBox.KeyDown += TextBox_KeyDown;
textBox.KeyUp += TextBox_KeyUp;
Content = textBox;
(PresentationSource.FromVisual(this) as HwndSource).AddHook(WndProc);
}
private void TextBox_KeyDown(object sender, KeyEventArgs e)
{
e.Handled = true;
}
private void TextBox_KeyUp(object sender, KeyEventArgs e)
{
e.Handled = true;
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
Debug.WriteLine(msg + " " + wParam);
return IntPtr.Zero;
}
}
Is it possible to receive a PreviewKeyDown/PreviewKeyUp event in WndProc?
There are tons of way to intercept key messages. You don't even need any library for this. Using pure Win32 API is OK but if you want simplicity, try handling the ThreadPreprocessMessage event of ComponentDispatcher:
ComponentDispatcher.ThreadPreprocessMessage += (ref MSG m, ref bool handled) => {
//check if WM_KEYDOWN, print some message to test it
if (m.message == 0x100)
{
System.Diagnostics.Debug.Print("Key down!");
}
};
The event is able to receive any key messages before it is actually sent to your window. So it's what you want in this case if you want to handle raw messages (instead of handling PreviewKeyDown, ...).
The AddHook method allows to add some hook proc for the window but it's really limited in WPF (while the equivalent WndProc protected method of Form in winforms can intercept much more messages).
Try using ManagedWinApi. You can install it with NuGet.
PM> Install-Package ManagedWinapi
For extensive examples of keyboard and other msg interception: http://mwinapi.sourceforge.net/
Another alternative is https://easyhook.github.io/
Both libraries are well documented.
Related
A WPF application has a Test() method that is called when a keyboard shortcut is pressed CTRL+G.
The method call works because the string test is printed to the console, from the first line of the method.
The method should programmatically press the key combination CTRL+A to select the text in any input field, but this does not happen.
I tried 3 ways:
First: The System.Windows.Forms.SendKeys.SendWait() method, which takes a string, where ^ is CTRL - according to documentation
private void Test(object sender, EventArgs e)
{
Console.WriteLine("test");
SendKeys.SendWait("^A");
}
However, there is no pressing.
Second: Implementation via user32.dll, solution taken from here:
[DllImport("user32.dll", SetLastError = true)]
static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
public static void PressKey(Keys key, bool up)
{
const int KEYEVENTF_EXTENDEDKEY = 0x1;
const int KEYEVENTF_KEYUP = 0x2;
if (up)
keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, (UIntPtr)0);
else
keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY, (UIntPtr)0);
}
private void Test(object sender, EventArgs e)
{
Console.WriteLine("test");
PressKey(Keys.ControlKey, up: false);
PressKey(Keys.A, up: false);
PressKey(Keys.A, up: true);
PressKey(Keys.ControlKey, up: true);
}
But in this case, nothing happens.
Third: Installed the package: Install-Package InputSimulator:
private static void Test(object sender, EventArgs e)
{
Console.WriteLine("test");
var simu = new InputSimulator();
simu.Keyboard.ModifiedKeyStroke(VirtualKeyCode.CONTROL, VirtualKeyCode.VK_A);
}
Full code: https://pastebin.com/ay8vRtjA
There are no errors, what am I doing wrong?
The key combination technically works, but the code executes before you have any time to release the ALT key, making the final combination CTRL + ALT + A instead of CTRL + A. I may be overlooking some simpler solution, but the way I found (mostly) works is:
Intercept the ALT key
If G is pressed while ALT is down, execute the command
If G wasn't pressed, allow the keystroke through and send a simulated ALT (so that hotkeys in other applications can still be activated)
It's a hacky workaround and still messes up some regular functionality (for instance if you press a hotkey like ALT + A to open a menu, the menu will close as soon as you release ALT), but it makes your hotkey work.
I used a library that I created a couple of years back, called InputHelper, to set up a global keyboard hook to intercept the keystrokes and execute the hotkey. I've yet to publish this to NuGet, so for now you'll have to download it via Releases and add the DLL as a reference in your project.
You'll also need to add a reference to System.Windows.Forms.
using System;
using System.Windows;
using System.Windows.Forms;
namespace HotkeyTest
{
public partial class MainWindow : Window
{
InputHelper.Hooks.KeyboardHook kbHook = new InputHelper.Hooks.KeyboardHook();
bool AltHotkeyConsumed = false;
public MainWindow()
{
InitializeComponent();
kbHook.KeyDown += KeyboardHook_KeyDown;
kbHook.KeyUp += KeyboardHook_KeyUp;
}
private void KeyboardHook_KeyUp(object sender, InputHelper.EventArgs.KeyboardHookEventArgs e)
{
if(e.KeyCode == Keys.LMenu || e.KeyCode == Keys.RMenu)
{
if(!AltHotkeyConsumed)
// If no hotkey was consumed, press the key again (otherwise it will just be blocked altogether)
InputHelper.Keyboard.PressKey(e.KeyCode);
else
AltHotkeyConsumed = false;
}
}
private void KeyboardHook_KeyDown(object sender, InputHelper.EventArgs.KeyboardHookEventArgs e)
{
if(e.KeyCode == Keys.LMenu || e.KeyCode == Keys.RMenu)
e.Block = true;
if(e.Modifiers == InputHelper.ModifierKeys.Alt)
{
if(e.KeyCode == Keys.G)
{
Test();
AltHotkeyConsumed = true;
e.Block = true;
}
else
{
e.Block = false;
}
}
}
private static void Test()
{
InputHelper.Keyboard.SetKeyState(Keys.LControlKey, true);
InputHelper.Keyboard.PressKey(Keys.A);
InputHelper.Keyboard.SetKeyState(Keys.LControlKey, false);
}
}
}
I am working on a WPF application where I need to know if my application is visible to the user or not. I tried Application.Current.Activated/Deactivated events to change a bool property and use this bool to check as per my requirements. Everything works fine except that the Deactivated event is getting called when a popup (like gtalk chat window) opens or I click in the taskbar, so my bool is changing.
What can I do to check if my app is visible to the user rather than activated/deactivated (which checks if my application is the foreground app or not)?
Application.Current.Activated += Current_Activated;
Application.Current.Deactivated += Current_Deactivated;
void Current_Deactivated(object sender, EventArgs e)
{
IsActive = false;
}
void Current_Activated(object sender, EventArgs e)
{
IsActive = true;
}
And if !IsActive I am showing the notification.
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
source.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == 0x1c)
{
OnActivateApp(wParam != IntPtr.Zero);
}
return IntPtr.Zero;
}
protected void OnActivateApp(bool activate)
{
Console.WriteLine("Activate {0}", activate);
}
Excuse me, do not write and do not understand English very well, but this article has enhanced the solution http://www.codeproject.com/Articles/704390/How-to-be-Notified-when-your-application-is-active
You can use the Application.Windows property to tell you which Windows have been instantiated and not closed (currently open) and then use the Window.IsActive property to tell if any of them are currently active:
Window activeWindow =
Application.Current.Windows.OfType<Window>().SingleOrDefault(w => w.IsActive));
if (activeWindow != null)
{
// activeWindow is the active Window
}
There's a VisibilityChanged Event where you can hook up. You'll find the current visibility state in its EventArgs.
Let me know if I need to post any code ;-)
I want to give a textbox focus when the user starts typing anywhere in my app.
My page inherits from LayoutAwarePage.
Can this be achieved ?
Edit:
I got this code:
// In constructor
Window.Current.CoreWindow.KeyDown += CoreWindow_KeyDown;
// Somewhere else in class
void CoreWindow_KeyDown(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.KeyEventArgs args)
{
this.setSearchboxFocus((int)args.VirtualKey);
}
private void setSearchboxFocus(int keyCode)
{
if (keyCode == 38)
return;
if (keyCode == 40)
return;
if (this.searchBox.FocusState == Windows.UI.Xaml.FocusState.Unfocused)
{
this.searchBox.Text = "";
this.searchBox.Focus(Windows.UI.Xaml.FocusState.Keyboard);
}
}
For anyone reading this thread in the future, it is because of the webview. I asked a similar question on the msdn forum here. As of Windows 8.1, the webview is implemented as a separate window and completely steals all keyboard input when it has focus without passing any of it up to the controlling application. If you are able to change the HTML in the website being called it may be possible to use javascript listeners to pass events between the application and webview, but I did not test this myself. Unfortunately there does not seem to be any other workaround at this time.
You can handle the KeyDown/KeyUp event for the whole page by subscribing to these events
Window.Current.CoreWindow.KeyDown += CoreWindow_KeyDown;
Window.Current.CoreWindow.KeyUp += CoreWindow_KeyUp
This might help
private void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
textBox1.Focus();//sets focus to textBox1 when user presses a key on form
}
How about something like this?
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (!textBox1.Focused)
{
textBox1.Focus();
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
How about, on the Windows.UI.Xaml.Controls.Page:
protected override void OnKeyDown(KeyRoutedEventArgs e)
{
textBox1.Focus(Windows.UI.Xaml.FocusState.Keyboard);
base.OnKeyDown(e);
}
I want to focus a textbox when a key is pressed.
I use this code:
private void MainForm_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
textBoxCode.Focus();
}
With KeyPreview = true on my form.
But when I do that, if I write 'az', only the 'z' char appear in my textbox. If I press only 'a', textboxCode is empty but have the focus.
How not to lose the key pressed?
SOLUTION:
private void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
if (textBox1.Focused == false)
{
textBox1.Text += e.KeyChar.ToString();
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.Focus();
}
}
This is pretty hard to do, the WM_KEYDOWN message that Windows sends is already committed to the window that has the focus. You do not want to get in the business of translating key-down events into typing characters, that's a rocket science on keyboard layouts with dead keys that produces only exploding rockets.
One thing you can do is re-post the keyboard message, now with a window handle of the textbox. You can do this by overriding the form's ProcessCmdKey() method to detect the keystroke and return true to prevent it from being processed any further. Like this:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (!textBox1.Focused) {
PostMessage(textBox1.Handle, msg.Msg, msg.WParam, msg.LParam);
textBox1.Focus();
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr PostMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
Something like this:
private void MainForm_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
textBoxCode.Focus();
textBoxCode.Text = (char)e.KeyCode;
}
DataGridView keydown event is not working when I am editing text inside a cell.
I am assigning shortcut Alt+S to save the data, it works when cell is not in edit mode, but if it is in edit mode below code is not working
private void dataGridView1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == (Keys.Alt | Keys.S))
{
//save data
}
}
Whenever a cell is in edit mode, its hosted control is receiving the KeyDown event instead of the parent DataGridView that contains it. That's why your keyboard shortcut is working whenever a cell is not in edit mode (even if it is selected), because your DataGridView control itself receives the KeyDown event. However, when you are in edit mode, the edit control contained by the cell is receiving the event, and nothing happens because it doesn't have your custom handler routine attached to it.
I have spent way too much time tweaking the standard DataGridView control to handle edit commits the way I want it to, and I found that the easiest way to get around this phenomenon is by subclassing the existing DataGridView control and overriding its ProcessCmdKey function. Whatever custom code that you put in here will run whenever a key is pressed on top of the DataGridView, regardless of whether or not it is in edit mode.
For example, you could do something like this:
class MyDataGridView : System.Windows.Forms.DataGridView
{
protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData)
{
MessageBox.Show("Key Press Detected");
if ((keyData == (Keys.Alt | Keys.S)))
{
//Save data
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
Also see related, though somewhat older, article: How to trap keystrokes in controls by using Visual C#
Another way of doing it is by using the EditingControlShowing event to redirect the event handling to a custom event handler as below:
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if (e.Control is DataGridViewTextBoxEditingControl tb)
{
tb.KeyDown -= dataGridView1_KeyDown;
tb.KeyDown += dataGridView1_KeyDown;
}
}
//then in your keydown event handler, execute your code
private void dataGridView1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == (Keys.Alt | Keys.S))
{
//save data
}
}
This is true that EditingControlShowing can help, but not if you wants to catch the Enter key. In that case, one should use the following method:
private void dataGridView_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if (e.Control is DataGridViewTextBoxEditingControl)
{
DataGridViewTextBoxEditingControl tb = e.Control as DataGridViewTextBoxEditingControl;
tb.KeyDown -= dataGridView_KeyDown;
tb.PreviewKeyDown -= dataGridView_PreviewKeyDown;
tb.KeyDown += dataGridView_KeyDown;
tb.PreviewKeyDown += dataGridView_PreviewKeyDown;
}
}
void dataGridView_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyData == Keys.Enter)
{
<your logic goes here>
}
}
A simpler way I just tried out is as follows:
Set the KeyPreview property of the Form to true.
Instead of catching the KeyDown event on Grid, catch the KeyDown event on Form.
Code as follows:
Private Sub form1_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
If grd.Focused Then
'Do your work
End If
End Sub
I worked with this
private void grdViewOrderDetail_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
grdViewOrderDetail_KeyDown(null,null);
}
private void grdViewOrderDetail_KeyDown(object sender, KeyEventArgs e)
{
//Code
}
The solution
class MyDataGridView : System.Windows.Forms.DataGridView {
protected override bool ProcessCmdKey(ref System.Windows.Forms.Message msg, System.Windows.Forms.Keys keyData) {
if ( keyData == Keys.Enter ) {
.
Process Enter Key
.
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
Worked perfectly for me
use PreviewKeyDown event
private void dataGridView1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
}