I'm wanting to write a virtual keyboard, like windows onscreen keyboard for touchscreen pcs.
But I'm having problem with my virtual keyboard stealing the focus from the application being used. The windows onscreen keyboard mantains the focus on the current application even when the user clicks on it. Is there a way to do the same with windows forms in C#?
The only thing I can do for now is to send a keyboard event to an especific application, like notepad in the following code. If I could make the form not focusable, I could get the current focused window with GetForegroundWindow.
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName,string lpWindowName);
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
private void button1_Click(object sender, EventArgs e)
{
IntPtr calculatorHandle = FindWindow("notepad", null);
SetForegroundWindow(calculatorHandle);
SendKeys.SendWait("111");
}
Is there a way this can be done? Any suggestions of a better way to have the form sending keyboard events to the application being used?
Thanks!!
Its solved!
I've tried the solution from gehho, but I also needed to override the CreateParams method:
private const int WS_EX_NOACTIVATE = 0x08000000;
protected override CreateParams CreateParams
{
get
{
var createParams = base.CreateParams;
createParams.ExStyle |= WS_EX_NOACTIVATE;
return createParams;
}
}
Instead of trying to reset the active window after your one has been clicked, I would rather try to prevent your window from receiving focus/being activated.
Have a look at this article. At the end, the author briefly explains how this can be done:
How can I prevent my window from getting activation and focus when shown?
In Windows Forms 2.0 there is a new property called
ShowWithoutActivation – which you would need to override on the Form.
In native applications you can use SetWindowPos with the
SWP_NOACTIVATE flag or the ShowWindow with the SW_SHOWNA flag.
Furthermore, in this article he provides a code example for Windows Forms:
If you want a full-on form, you can now override a property called
ShowWithoutActivation:
public class NoActivateForm : Form
{
protected override bool ShowWithoutActivation => true;
}
Keep in mind this does not “prevent” activation all the time – you can
still activate by calling the Activate(), Focus()… etc methods. If
you want to prevent clicks on the client area from activating the
window, you can handle the WM_MOUSEACTIVATE message.
private const int WM_MOUSEACTIVATE = 0x0021, MA_NOACTIVATE = 0x0003;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEACTIVATE)
{
m.Result = (IntPtr)MA_NOACTIVATE;
return;
}
base.WndProc(ref m);
}
Related
I've created a custom form for a numeric keyboard which implements the following code:
private void btn1_Click(object sender, EventArgs e)
{
PressKey(Keys.D1);
}
[DllImport("user32.dll", SetLastError = true)]
static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
public static void PressKey(Keys key)
{
const int KEYEVENTF_EXTENDEDKEY = 0x1;
keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY, (UIntPtr)0);
}
I've also used the following block
protected override bool ShowWithoutActivation
{
get { return true; }
}
It works as charm for notepad, word or other editors (and also using the basic SendKeys method), but in my app, in the grid cell i intend to use it (or other editable control) it fails to insert a value.
Accidentally i've found that it can insert a value only once, when the cell is not on edit mode, but it has focus.
Any example of virtual keyboard implementation from one form to another would be highly appreciated
(Too long for a comment, but maybe not enough for an answer)
We had once to develop such a keyboard, and we used the SendKeys class from the .Net Framework (which is merely a wrapper for the SendInput Win32 API call).
The keyboard form was overriding the Property CreateParams so we could explicitly tell it not to take the focus (or the keys are send to the keyboard form...)
protected override CreateParams CreateParams
{
get
{
var CreateParams cp = base.CreateParams;
cp.ExStyle = cp.ExStyle | WS_EX_NOACTIVATE;
return cp;
}
}
Note: We noticed that some programs like Ultra VNC (used for remote control) prevent the correct usage of the keyboard. I guess UltraVNC is rerouting the input keys through its driver and something is lost during the way, but never found out what.
Okay, so you know how in Windows Vista and Windows 7 MS changed the Hand Cursor (the one that shows up when you hover over a hyperlink), and added more detail to it so it's antialiased and nice and smooth around the edges?
Well, why isn't it like that in Windows Forms apps?
I'm sick off looking at a crappy hand cursor that looks like it was drawn by a caveman.
Is there a way to programmatically tell it to display the one that's actually installed in the system? I looked in the Cursors folder in my Windows directory, and the old hand cursor isn't even there! So why is WinForms still using the old one? How can I 'upgrade' it?
Yes, the WinForms controls still use the old-school hand cursor, as shipped with Windows 98/2000. It lacks the anti-aliasing effects that the one included with the Aero cursors does. This is because the .NET Framework includes its own hard-coded cursor, which it uses instead of the system default. I presume this is because early versions of .NET were targeting operating systems like Windows 95 that didn't come bundled with this cursor, but haven't done the archaeology to prove it.
Fortunately, it's easy enough to force it to use the right one. You just have to tell the operating system you want it to use the default hand cursor, and then it will be correct no matter what version of Windows the user runs your program on, and even if they've changed their mouse cursors from the default theme.
The simplest way of doing that is to subclass the existing control, override the WndProc function to intercept the WM_SETCURSOR message, and tell it to use the system IDC_HAND cursor. You just need a little bit of P/Invoke magic.
The following code is an example of how that might look using the LinkLabel control:
public class LinkLabelEx : LinkLabel
{
private const int WM_SETCURSOR = 0x0020;
private const int IDC_HAND = 32649;
[DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
[DllImport("user32.dll", CharSet=CharSet.Auto)]
private static extern IntPtr SetCursor(IntPtr hCursor);
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_SETCURSOR)
{
// Set the cursor to use the system hand cursor
SetCursor(LoadCursor(IntPtr.Zero, IDC_HAND));
// Indicate that the message has been handled
m.Result = IntPtr.Zero;
return;
}
base.WndProc(ref m);
}
}
Excuse me for resurrecting a year-old thread!!!
After messing around with the original solution and taking a look at the reflected LinkLabel source code, I "finally" found a quick yet clean way of doing it :
using System.Runtime.InteropServices;
namespace System.Windows.Forms {
public class LinkLabelEx : LinkLabel {
private const int IDC_HAND = 32649;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
private static readonly Cursor SystemHandCursor = new Cursor(LoadCursor(IntPtr.Zero, IDC_HAND));
protected override void OnMouseMove(MouseEventArgs e) {
base.OnMouseMove(e);
// If the base class decided to show the ugly hand cursor
if(OverrideCursor == Cursors.Hand) {
// Show the system hand cursor instead
OverrideCursor = SystemHandCursor;
}
}
}
}
This class actually does what we want: It shows the proper system hand cursor without flickering and does this only on the LinkArea of the control.
This post solves problems of the other posts:
It respects to the link location and shows the hand just when cursor is on link
It doesn't flicker on mouse move
You need to change the cursor to system hand cursor. To do so, you need to handle WM_SETCURSOR and check if OverrideCursor is Cursors.Hand then change it to the system cursor by calling SetCursor:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class MyLinkLabel : LinkLabel
{
const int IDC_HAND = 32649;
const int WM_SETCURSOR = 0x0020;
const int HTCLIENT = 1;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
static extern IntPtr SetCursor(HandleRef hcursor);
static readonly Cursor SystemHandCursor =
new Cursor(LoadCursor(IntPtr.Zero, IDC_HAND));
protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_SETCURSOR)
WmSetCursor(ref msg);
else
base.WndProc(ref msg);
}
void WmSetCursor(ref Message m)
{
if (m.WParam == (IsHandleCreated ? Handle : IntPtr.Zero) &&
(unchecked((int)(long)m.LParam) & 0xffff) == HTCLIENT) {
if (OverrideCursor != null) {
if (OverrideCursor == Cursors.Hand)
SetCursor(new HandleRef(SystemHandCursor, SystemHandCursor.Handle));
else
SetCursor(new HandleRef(OverrideCursor, OverrideCursor.Handle));
}
else {
SetCursor(new HandleRef(Cursor, Cursor.Handle));
}
}
else {
DefWndProc(ref m);
}
}
}
Sorry for getting this old post back, but i also have some kind of solution for this.
If you need to apply the systemcursor application wide without touching old controls, use this at applicationstart:
private static void TrySetCursorsDotHandToSystemHandCursor()
{
try
{
typeof(Cursors).GetField("hand", BindingFlags.Static | BindingFlags.NonPublic)
.SetValue(null, SystemHandCursor);
}
catch { }
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
private static readonly Cursor SystemHandCursor = new Cursor(LoadCursor(IntPtr.Zero, 32649 /*IDC_HAND*/));
Doing that without creating a new control wee need to change the Control's Cursor AND create a custom linklabel or else it wouldn't work
we create the custom linklabel by adding a label changing the font underline and changing it's fore color and add an click event
Private Const IDC_HAND As Integer = 32649
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Private Shared Function LoadCursor(ByVal hInstance As IntPtr, ByVal lpCursorName As Integer) As IntPtr
End Function
Private Shared ReadOnly SystemHandCursor As Cursor = New Cursor(LoadCursor(IntPtr.Zero, IDC_HAND))
'add the cursor to custom linklabel
CustomLinkLabel1.Cursor = SystemHandCursor
sorry only have vb .net code you might use an online converter
EDIT: some code was missing
I have a program which runs in the background, and when something happens a message box appears, I want to have it so clicking yes will switch to a specified program.
I just can't find what ClassName and CaptionName work with it, I need it to work with the game World of Warcraft.
The Window Title is "Wold of Warcraft" on Task manager its called "World of Warcraft Retail", when I inspect its' properties it says "Wow-64" Properties, on properties it says product name is "World of Warcraft" so I've tried every combination of these and nothing works. The code works if I put:
BringToFront("Notepad", "Untitled - Notepad");
So it works, I just don't know what I need for it to apply to WoW.
My code is:
[DllImport("User32.dll")]
public static extern Int32 SetForegroundWindow(int hWnd);
[DllImport("user32.dll")]
public static extern int FindWindow(string lpClassName, string lpWindowName);
private void BringToFront(string className, string CaptionName)
{
SetForegroundWindow(FindWindow(className, CaptionName));
}
private void Alert()
{
string title = "WoW Queue Alert: Message";
string message = "The Queue is ready to accept!";
MessageBoxButtons buttons = MessageBoxButtons.YesNo;
DialogResult result;
result = MessageBox.Show(new Form() { TopMost = true }, message, title, buttons, MessageBoxIcon.Information);
if (result == System.Windows.Forms.DialogResult.Yes)
{
BringToFront("World of Warcraft Retail", "World of Warcraft");
}
}
I really don't see anything special about WoW, and going by how the notepad example works the correct code should be:
BringToFront("World of Warcraft Retail", "World of Warcraft");
Being a fullscreen program should affect it, and I can't see there being anyway Blizzard has implemented something to stop this function.
Edit: I just set the ClassName to null and works, as caption name is just the window title. No idea what the ClassName is I tried everything I could find.
You should use some window inspection tool to inspect the window of World of Warcraft to get its Class Name and Window Name. A suggestion would be Spy++ if you installed Visual Studio (it comes along with it). It can help you detect the window's Class Name and Window Name, and Windows Messages sent to that window easily.
For example, the picture below shows all windows in current environment. Highlighted entry is a PowerPoint instance, which has Caption = "Microsoft PowerPoint" and ClassName = "PPTFrameClass".
Another approach will be writing a "always-inactivate" winform and call GetForegroundWindow from that form to get the window handle of the window you want.
To create an "always-inactivate" winform, simply override its CreateParams property:
protected override CreateParams CreateParams
{
get
{
CreateParams p = base.CreateParams;
p.ExStyle |= 0x8000000; // WS_EX_NOACTIVATE - requires Win 2000 or higher
return p;
}
}
By overriding this, the form won't get focus even on a mouse click. Therefore, you could create a button to trigger GetForegroundWindow(). Signature of the function in C# looks like below:
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
which returns the window handle of current fore ground window.
My application needs to display a popup near the cursor position of any active application when certain conditions are met (known by my app).
So I would like to display the form (without stealing the focus from the active application). I tried using ShowWindow with the SW_SHOWNOACTIVATE param but, this way, the my form is displayed under the currently active form.
How can I force my form to be displayed on top of any form active on the screen without stealing the input focus?
Thanks.
What you want is to use the TopMost property of the form you want to stay on top.
You need to add a bit of plumbing to the pop-up form so you can override the WM_ACTIVATE message:
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_ACTIVATE)
{
if (((int)m.WParam & 0xFFFF) != WA_INACTIVE)
{
if (m.LParam != IntPtr.Zero)
{
SetActiveWindow(m.LParam);
}
else
{
// Could not find sender, just in-activate it.
SetActiveWindow(IntPtr.Zero);
}
}
}
base.WndProc(ref m);
}
Make sure you add the following to your pop-up form as well:
[DllImport("user32.dll")]
private extern static IntPtr SetActiveWindow(IntPtr handle);
private const int WM_ACTIVATE = 6;
private const int WA_INACTIVE = 0;
You can use the pop-up form as you would any other, by calling Show() on it. You can make it the top-most window through the TopMost property, as with other forms.
I am trying to pin a form to the desktop, like the gadget sidebar.
It should not be affected by Win+D and Show desktop buttons.
Which is the best methode for this?
I can find the Progman and set the parent of my form to this, but I read it's not the best way.
Disclaimer : just because you can do something a certain way in WinForms without API calls or "hooking," doesn't mean that you should use that technique. The API calls for making a Form/Window immovable, topmost, etc. are really very simple. Note that the usual API "simple technique" will have the same problem as the technique demonstrated below of appearing over file and folder icons on the desktop.
Note that by "interfering" with the designed behaviors of the Windows-D key combination and the "Show Desktop" icon-hickey in the TaskBar, you are engaging in non-standard configuration of the user's machine : common "wisdom" indicates it's not a good idea to do this !
A "pseudo-sidebar" can be emulated in Vista in C# WinForms ... impervious to being hidden by Windows-D or use of the "Show Desktop" icon-hickey in the System Tray ... without API calls or "hooking" : but there's a catch : the Form/Window is going to appear on top of items on the Desktop (folders, files), and, by virtue of its being immovable, you won't be able to access the items "under it." If you're willing to "put up" with that limitation : read on :) Another "smaller weirdness" noted at the end of this.
Example :
create a new Form solution in Visual Studio :
a. set the Form's size, background color, etc. to taste
b. add your controls, or user controls.
c. set the Form's 'TopMost property to 'true if you want to always appear in front of other application's windows, 'false if you want other application's windows to be able to appear in front of it.
set the 'ControlBox, 'MinimizeBox, and 'MaximizeBox properties of the Form to 'false.
set the 'FormBorderStyle to one of the 'fixed styles shown in the Property Explorer drop-down. as you may know using FormBorderStyle = FixedToolWindow with the Text property of the Form an "empty string" : you'll have a window with no TitleBar and Caption. Discussion : yes, you could allow it to be resized if you wish : just write some code in the SizeChanged handler, and leave the Form unlocked.
these event handlers should give you an idea of what's required to make the Form unclosable, unmovable, un-minimizable, etc.
......... rough sketch code p.o.c. follows : obviously you can refactor, improve ...
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
}
// set your Form's 'Top here ...
private void Form1_LocationChanged(object sender, EventArgs e)
{
this.Top = 100;
this.Left = Screen.PrimaryScreen.Bounds.Width - this.Width;
}
private void Form1_VisibleChanged(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized) this.WindowState = FormWindowState.Normal;
}
private void Form1_Deactivate(object sender, EventArgs e)
{
this.Activate();
}
private void Form1_SizeChanged(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized) this.WindowState = FormWindowState.Normal;
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("button1 is alive");
}
What's that 'Click event handler for 'button1 doing in there ? Just to verify in my test that the Form was usable.
It's the re-activation of the Form in the 'Deactivate Event handler above that makes the Form immune to Windows-D and "Show Desktop" : but that also has a strange side-effect in Vista : when you switch focus away from the Form's window (by, for example, clicking on a folder on the Desktop) : the TaskBar will bounce-up, stay up, (if it's hidden) and blink on and off a while : I guess to show you it's yearning to be active.
Putting up with a stuck-open TaskBar may be yet another price of using this "ersatz sidebar," and not using API's.
Note : don't forget : you want 'Opacity : you got 'Opacity :)
[1] Note : You do want to think about giving the user a way to hide the Form or close it ? [/1]
It sounds like what you're after is an Application Desktop Toolbar.
I could be wrong but, may be Win+D probably sends a global minimize message to all open windows
So Try making your form immune to window minimize message.
using hooks
Heres a related article.
Hooking
Thanks all for the answers,
I had this methode at first:
[DllImport("User32.dll")]
static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
static extern int SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
IntPtr pWnd = FindWindow("Progman", null);
pWnd = FindWindowEx(pWnd, IntPtr.Zero, "SHELLDLL_DefVIew", null);
pWnd = FindWindowEx(pWnd, IntPtr.Zero, "SysListView32", null);
IntPtr tWnd = this.Handle;
SetParent(tWnd, pWnd);
But the marjor issue is that child windows are also parented to the deskop, and you cannot use any form of opacity or transparency.
use the following code and pass the window handle to the function while form load hope fully this resolves your problem
public void SetFormOnDesktop(IntPtr hwnd) {
IntPtr hwndf = hwnd;
IntPtr hwndParent = FindWindow("ProgMan", null);
SetParent(hwndf, hwndParent);
}
A simple workaround could be to just use Win+M and don't listen to the Deactivation Event. This will remove the flickering from activating/deactivating.