Brief
I am trying to create a on-screen keypad app in C#. This keypad should act like a on-screen keyboard to emulate keyboard input as the user clicks a button. For example, when user clicks the "A" button, it should type a
in an input box (in another application).
As I only had this idea last night and started to develop it now, I have only made a really simple test ui:
It only has one button "A".
The Problem
One of the problems is to set the window non-activating on mouse click. Thanks to a comment in an earlier question I posted and this answer, I now have the following code:
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
var winH = (HwndSource) PresentationSource.FromVisual(this);
// set mouse activate false
winH.AddHook(WndProc);
}
#region WndProc
private const int WM_MOUSEACTIVATE = 0x0021;
private const int MA_NOACTIVATE = 0x0003;
private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg != WM_MOUSEACTIVATE)
return IntPtr.Zero;
handled = true;
return new IntPtr(MA_NOACTIVATE);
}
#endregion WndProc
However, the code does not work well. Mouse activating is only disabled when the background (empty space) of the MainWindow
is clicked. If the Button
is clicked, the window will get activated. Since a wpf control does not have HWND, I can't AddHook
to it.
In addition, although clicking on the empty space of the MainWindow
does not bring the application to the front, but seems activate the application nonetheless as it gets highlighted on the taskbar.
Appendix - Full code
MainWindow.xaml:
<Window x:Class="OnScreenKeyPad.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:OnScreenKeyPad"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="200"
WindowStyle = "None"
AllowsTransparency="True" >
<StackPanel>
<Button x:Name="KeyA" Content="A" Width="50" Margin="10"/>
</StackPanel>
</Window>
MainWindow.xaml.cs:
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace OnScreenKeyPad
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
var winH = (HwndSource) PresentationSource.FromVisual(this);
// set mouse activate false
winH.AddHook(WndProc);
}
#region WndProc
private const int WM_MOUSEACTIVATE = 0x0021;
private const int MA_NOACTIVATE = 0x0003;
private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg != WM_MOUSEACTIVATE)
return IntPtr.Zero;
handled = true;
return new IntPtr(MA_NOACTIVATE);
}
#endregion WndProc
}
}
Update 1
I have logged a list of WindowProc messages:
ID Event
33 WM_MOUSEACTIVATE
32 WM_SETCURSOR
513 WM_LBUTTONDOWN
NA OnMouseDownPreviewEvent
70 WM_WINDOWPOSCHANGING
71 WM_WINDOWPOSCHANGED
28 WM_ACTIVATEAPP
134 WM_NCACTIVATE
6 WM_ACTIVATE
641 WM_IME_SETCONTEXT
642 WM_IME_NOTIFY
7 WM_SETFOCUS
8 WM_KILLFOCUS
641 WM_IME_SETCONTEXT
641 WM_IME_SETCONTEXT
7 WM_SETFOCUS
641 WM_IME_SETCONTEXT
641 WM_IME_SETCONTEXT
132 WM_NCHITTEST
512 WM_NCHITTEST
132 WM_MOUSEFIRST
514 WM_LBUTTONUP
533 WM_CAPTURECHANGED
132 WM_NCHITTEST
NA ButtonOnClickEvent
Now I probably understand why the code doesn't work. MOUSEACTIVATE
is fired right after the moust button is clicked, which is before the actual mouse button event (ID 513). Although the MOUSEACTIVATE
is handled and window activating prevented, the mouse button event will activate the window again with ID 28 WM_ACTIVATEAPP
. I have checked the documentation of these events and none of them can be blocked.
Maybe I can use a label and make use of its PreviewMouseDown
event.