I am making an application for personal use where the stylus can be used to draw on the current screen but the normal use (with mouse) won't be interrupted.
Currently, I am trying to use WS_EX_TRANSPARENT to set the window to allow mouse events through, but it seems like that stylus events also get passed through without being captured.
Is there any other method I can use to pass through mouse/keyboard events while still allowing stylus events? Here is what my program looks like so far:
Disable the RealTimeStylus for WPF Applications on MSDN states:
[...] (WPF) has built in support for processing Windows 7 touch input [...] Windows 7 also provides multi-touch input as Win32 WM_TOUCH window messages. These two APIs are mutually exclusive on the same HWND.
This seems to imply that, for a given window, you can receive stylus events or touch events but not both. As you do want to handle the stylus events this means you don't need to bother filtering the touch events. That just leaves the mouse and keyboard.
At first I thought you might be able to use a custom window procedure (WndProc) and filter-out the mouse and keyboard messages. However, the WndProc (when used in WPF) is really just a notification mechanism and you can't block the received messages.
I found a Windows API called BlockInput that supposedly "Blocks keyboard and mouse input events from reaching applications". However from the docs this appears to be system-wide not app-specific so may not be any use to you.
The only other way I can think of is to use a low-level keyboard or mouse hook. This requires some P/Invoke but it's not too difficult. These hooks allow you to register callback functions that get called when keyboard and mouse events are raised. The advantage is that you can prevent those events from propagating and effectively "swallow" them, which sounds like what you need.
I don't really like posting an answer that basically says "do a search for ..." but the amount of code involved is non-trivial and has been posted in numerous places both on Stack Overflow and elsewhere, so: try doing a search for low level keyboard hook c# wpf and you should find some code that might help!
One thing you may have trouble with even if you go down this route is focus. As soon as your "invisible" topmost window gets a stylus message that it responds to, I'm presuming focus will switch to your WPF application, thus "stealing" focus from whatever application was being used prior. You might be able to use P/Invoke again to set the window style flags of your main window to prevent this (as per the accepted answer to this SO question).
ORIGINAL ANSWER
You can override the appropriate keyboard, mouse and touch Preview... event handler methods of the Window and mark them as handled. This has the effect of stopping child controls from receiving those events.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
e.Handled = true;
base.OnPreviewKeyDown(e);
}
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
e.Handled = true;
base.OnPreviewMouseDown(e);
}
protected override void OnPreviewMouseMove(MouseEventArgs e)
{
e.Handled = true;
base.OnPreviewMouseMove(e);
}
protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
{
e.Handled = true;
base.OnPreviewMouseWheel(e);
}
protected override void OnPreviewTouchDown(TouchEventArgs e)
{
e.Handled = true;
base.OnPreviewTouchDown(e);
}
protected override void OnPreviewTouchMove(TouchEventArgs e)
{
e.Handled = true;
base.OnPreviewTouchMove(e);
}
}
I've done the basic keyboard, mouse and touch events here. In a simple app test it seemed to do the trick and I assume it would still let stylus events through (I don't have a stylus I can test with).
You may have to experiment with which events need to be handled like this. I only did KeyDown for example, not KeyUp as I presume the latter is irrelevant without the former. I may also have implemented some that didn't need to be handled, and I'm not sure the calls to the base methods are needed either. As I say, experiment until you get something that works for you.
Related
I'm looking to intercept every mouse click in my WPF application. Seems this should be easy with the command routing mechanism, but sorry I'm not finding anything.
My application implements several security levels, and has the requirement to automatically revert to the most restrictive level if no one interacts with (clicks) the application in x minutes. My plan is to add a timer that expires after x minutes and adjusts the security level. Each mouse click into the application will reset the timer.
You can register a class handler:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
EventManager.RegisterClassHandler(typeof(Window), Window.PreviewMouseDownEvent, new MouseButtonEventHandler(OnPreviewMouseDown));
base.OnStartup(e);
}
static void OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
Trace.WriteLine("Clicked!!");
}
}
This will handle any PreviewMouseDown event on any Window created in the application.
<Window .... PreviewMouseDown="Window_PreviewMouseDown_1">
</Window>
This should work for you.
This fires even if other MouseDown events fire for components that it contains.
As per Clemens suggestion in the comments, PreviewMouseDown is a better choice than MouseDown, as that makes sure you can't stop the event bubbling from happening in a different event.
You have a few options:
Low level mouse hook:
http://filipandersson.multiply.com/journal/item/7?&show_interstitial=1&u=%2Fjournal%2Fitem
WPF Solution (I'd check to see if this does what you need first):
WPF. Catch last window click anywhere
I am developing a custom user control in WPF and trying to receive all touch events OUTSIDE of my control (so I can implement "cancel touches", where the user cancels an action by tapping somewhere else). Is there any good way to do this? Obviously, if I use TouchDown or other events I will only get notified of touches inside my control.
I tried receiving PreviewTouchDown events from Application.Current.MainWindow, but besides the fact it feels kind of dirty it also doesn't work that well - as soon as I capture those events, the SurfaceListBox inside my custom control stops working (it does not recognize touches anymore).
Is there a good way to solve this? Receiving simply ALL touch events would be sufficient as well, I can then check if they are inside my control.
In WPF, events travel the tree of elements. Events starting with "Preview" are the ones that travel up to bottom, meaning from the parent to the elements it contains. In your case, TouchDown event will travel from the control in which it originates and then it will bubble up towards the parents. PreviewTouchDown will travel in the opposite direction: from the parent towards the control.
Your idea of handling PreviewTouchDown event in the Application.Current.MainWindow is a good one and should handle all PreviewTouchDown events, if this is what you want. But in your case, if you want to check if the event originates in your custom control, you should catch all the TouchDown events in your MainWindow and check their Source property, like this:
private void CommonClickHandler(object sender, RoutedEventArgs e)
{
FrameworkElement feSource = e.Source as FrameworkElement;
if(feSource.Name == "MyCustomControlName")
{
//cancel whatever you want to cancel
e.Handled=true;
}
}
Do not catch the PreviewTouchDown event as this one will first pop in the MainWindow and just later will go all the way to your control.
This msdn link should give you more information: http://msdn.microsoft.com/en-us/library/ms742806.aspx
How can I capture a mouse click outside of a panel in an Windows Form application?
To capture global mouse and keyboard events you need to capture the windows messages WM_MOUSE_LL and WM_KEYBOARD_LL, not just WM_MOUSE and WM_KEYBOARD. These events can only be captured in NT/2000/XP. In later OS versions this is not possible (for fairly obvious security reasons I assume).
If you are using NT/2000/XP here is some example code:
http://www.codeproject.com/KB/cs/globalhook.aspx
The answer is in your question, set the panel's Capture property to true. All mouse input events are now directed to the panel, even if the mouse is outside of the panel window. This is however a temporary conditions (as it should be), a button click is going to cancel the capture after the click is delivered to the panel. The MouseCaptureChanged event lets you know when that happened. Unconditionally capturing the mouse is not an option, typing Ctrl+Esc for example will always cancel it.
You can handle the MouseClick event of the form itself.
class YourForm : Form
{
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
// Do something.
}
}
Is it possible to detect mouse clicks without listening to any mouse events defined in framework controls?
I mean, I don't want to write code like :
control.MouseLeftButtonDown += this.HandleMouseLeftButtonDown;
Yet I want to know if user clicks on the screen or not. Is it possible in C# (WPF or Silverlight)?
You can register a class handler in a static constructor you your main window, for example:
static MainWindow() {
EventManager.RegisterClassHandler(typeof (MainWindow),
Mouse.MouseDownEvent,
new MouseButtonEventHandler(OnGlobaMouseDown));
}
It will be a global handler for all MouseDown events.
You could use the Win32 API and detect the mouse message WM_MOUSE, something like this:
http://support.microsoft.com/kb/318804
or this example, showing use of the global mouse message WM_MOUSE_LL:
http://www.codeproject.com/KB/cs/globalhook.aspx
This is done by capturing the mouse. Which forces any mouse event to be directed to you, even if it moves outside of the window. Mouse.Capture() method.
If you need to handle mouse events of all your application, the best way is to subscribe to InputManager events.
"I mean, I don't want to write code like :
control.MouseLeftButtonDown += this.HandleMouseLeftButtonDown;"
You could always use:
if (Mouse.LeftButton == MouseButtonState.Pressed)
{
...
}
You will need to include this though.
using System.Windows.Input;
This works for me in wpf.
Basically, I have a form with a custom control on it (and nothing else). The custom control is completely empty, and the form has KeyPreview set to true.
With this setup, I am not receiving any KeyDown events for any arrow keys or Tab. Every other key that I have on my keyboard works. I have KeyDown event handlers hooked up to everything that has such events, so I'm sure I'm not missing anything.
Also of note is that if I remove the (completely empty) custom control, I DO get the arrow key events.
What on earth is going on here?
EDIT:
I added this to both the form and the control, but I'm STILL not getting arrow keys:
protected override void WndProc(ref Message m) {
switch (m.Msg) {
case 0x100: //WM_KEYDOWN
//this is the control's version. In the form, it's this.Text
ParentForm.Text = ((Keys)m.WParam).ToString();
break;
}
base.WndProc(ref m);
}
I also checked with Spy++, and determined that the form itself is not getting any WM_KEYDOWN messages, they're all going to the control. However, that said, the control IS getting the arrow key WM_KEYDOWN messages. Sigh.
Edit 2: I've also updated the ZIP file with this version. Please look at it, if you want to help...
Edit 3:
I've figured this out, sort of. The form is eating the arrow keys, probably in an attempt to maintain focus amongst its children. This is proven by the fact that I DO get the events if the form is empty.
Anyway, if I add this code to the form, I start getting the events again:
public override bool PreProcessMessage(ref Message msg) {
switch (msg.Msg) {
case 0x100: //WM_KEYDOWN
return false;
}
return base.PreProcessMessage(ref msg);
}
When I override this, the form doesn't get a chance to do its dirty work, and so I get my KeyDown events as I expect. I assume that a side effect of this is that I can no longer use my keyboard to navigate the form (not a big deal in this case, as it's a game, and the entire purpose of this exercise is to implement keyboard navigation!)
The question still remains about how to disable this "properly", if there is a way...
I've done some extensive testing, and I've figured everything out. I wrote a blog post detailing the solution.
In short, you want to override the ProcessDialogKey method in the form:
protected override bool ProcessDialogKey(Keys keyData) {
return false;
}
This will cause the arrow keys (and tab) to be delivered as normal KeyDown events. HOWEVER! This will also cause the normal dialogue key functionality (using Tab to navigate controls, etc) to fail. If you want to retain that, but still get the KeyDown event, use this instead:
protected override bool ProcessDialogKey(Keys keyData) {
OnKeyDown(new KeyEventArgs(keyData));
return base.ProcessDialogKey(keyData);
}
This will deliver a KeyDown message, while still doing normal dialogue navigation.
If focus is your issue, and you can't get your user control to take a focus and keep it, a simple work-around solution would be to echo the event to your user control on the key event you are concerned about. Subscribe your forms keydown or keypress events and then have that event raise an event to your user control.
So essentially, Form1_KeyPress would Call UserControl1_KeyPress with the sender and event args from Form1_KeyPress e.g.
protected void Form1_KeyPress(object sender, KeyEventArgs e)
{
UserControl1_KeyPress(sender, e);
}
Otherwise, you may have to take the long route and override your WndProc events to get the functionality you desire.