How pass a mouse event to an underlying element? - winforms

I've got a borderless movable form (dragged around by clicking and holding a mouse button). The dragging only works when I click on the form empty spaces, e.g. margin/padding area. How do I render a control (let's say label) so that it doesn't capture mouse events, but instead passes it to the underlying form element? In other words, I want to click on this label element and drag the form.
The closest thing I can think of is pointer-events: none in CSS.
Here's the powershell code I use to make my form movable:
function makeFormDraggable {
param($form)
Add-Type '
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
public class User32{
//const and dll functions for moving form
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HT_CAPTION = 0x2;
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImport("user32.dll")]
public static extern bool ReleaseCapture();
}
'
$onMouseDown = [System.Windows.Forms.MouseEventHandler] {
#Event Argument: $_ = [System.Windows.Forms.MouseEventArgs]
[user32]::ReleaseCapture()
[user32]::SendMessage($form.Handle, [user32]::WM_NCLBUTTONDOWN, [user32]::HT_CAPTION, 0);
}
$form.Add_MouseDown($onMouseDown)
}

Related

How to turn off all touch input at application, window or control level?

Using c# for a wpf application, if in Windows 7 touch is enabled in the control panel, a user by default can 'write' on an InkCanvas with a finger. I want to disable that and force stylus input only.
I'd like to know how to do it more than one way if possible: first by disabling touch on the InkCanvas, second by disabling it for a particular window, and third by disabling it for the entire application. A bonus fourth would be knowing how to turn touch on or off system-wide.
I have tried UnregisterTouchWindow, and I have tried setting Stylus.SetIsTouchFeedbackEnabled to false for the InkCanvas, but neither has worked.
Further digging helped me put together the following as a way to toggle touch on/off system-wide. If anyone knows how to accomplish this in any of the other 3 ways, I'd still appreciate those answers.
The basic steps are to check the current registry status, change it if necessary (and then refresh the system to recognize the change), and make note of the initial state to restore if needed on program exit.
Thanks to the posters at these two links for the education.
public MainWindow(){
InitializeComponent();
RegistryKey regKey = Registry.CurrentUser;
regKey = regKey.OpenSubKey(#"Software\Microsoft\Wisp\Touch", true);
string currKey = regKey.GetValue("TouchGate").ToString();
if (currKey == "1")
{
regKey.SetValue("TouchGate", 0x00000000);
User32Utils.Notify_SettingChange();
UserConfig.TGate = "1";
}
regKey.Close();
...
}
public static class UserConfig {
public static string TGate { get; set; }
...
}
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e){
...
if (UserConfig.TGate == "1")
{
RegistryKey regKey = Registry.CurrentUser;
regKey = regKey.OpenSubKey(#"Software\Microsoft\Wisp\Touch", true);
regKey.SetValue("TouchGate", 0x00000001);
User32Utils.Notify_SettingChange();
regKey.Close();
}
}
//------------------User32Utils.cs
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace (...)
{
internal class User32Utils
{
#region USER32 Options
static IntPtr HWND_BROADCAST = new IntPtr(0xffffL);
static IntPtr WM_SETTINGCHANGE = new IntPtr(0x1a);
#endregion
#region STRUCT
enum SendMessageTimeoutFlags : uint
{
SMTO_NORMAL = 0x0000,
SMTO_BLOCK = 0x0001,
SMTO_ABORTIFHUNG = 0x2,
SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
}
#endregion
#region Interop
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr SendMessageTimeout(IntPtr hWnd,
uint Msg,
UIntPtr wParam,
UIntPtr lParam,
SendMessageTimeoutFlags fuFlags,
uint uTimeout,
out UIntPtr lpdwResult);
#endregion
internal static void Notify_SettingChange()
{
UIntPtr result;
SendMessageTimeout(HWND_BROADCAST, (uint)WM_SETTINGCHANGE,
UIntPtr.Zero, UIntPtr.Zero,
SendMessageTimeoutFlags.SMTO_NORMAL, 1000, out result);
}
}
}

WPF: Non focusable window

I am developing WPF Touch Screen Keyboard.
I need to know how is it possible to make main window non focusable, so other windows will be receiving the input when I click on virtual keyboard buttons.
Simple applying "Focusable="False"" to the main window and all child controls doesn't work.
I think there is a clickable attribute you can set to false which stops the form receiving click messages.
Problem was solved by using Popup instead of Window, which not grab the focus, when you click on it.
From here: https://social.msdn.microsoft.com/Forums/vstudio/en-US/41ca3605-247c-4c5b-ac5d-74ce5abd7b92/making-a-window-invisible-to-mouse-events-ishittestvisiblefalse-not-working?forum=wpf
I've figured out how to do this. The key being the WS_EX_TRANSPARENT flag for the window's extended style.You can set the topmost property like you normally would, then this code takes care of making the window transparent to mouse clicks:
Code Snippet
public const int WS_EX_TRANSPARENT = 0x00000020;
public const int GWL_EXSTYLE = (-20);
[DllImport("user32.dll")]
public static extern int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
public static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
// Get this window's handle
IntPtr hwnd = new WindowInteropHelper(this).Handle;
// Change the extended window style to include WS_EX_TRANSPARENT
int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
}

Some questions about focus on WPF

I've a little problem about focus on WPF.
I whant to create a window, always on top, and that never get the focus (even if we click on it).
Here's my solution :
public partial class SkinWindow : Window
{
public SkinWindow()
{
InitializeComponent();
Loaded += ( object sender, RoutedEventArgs e ) => SetNoActiveWindow();
}
private void SetNoActiveWindow()
{
WindowInteropHelper helper = new WindowInteropHelper( this );
SetWindowLong( helper.Handle, GWL_EXSTYLE, WS_EX_NOACTIVATE );
LockSetForegroundWindow( LSFW_LOCK );
}
const int GWL_EXSTYLE = -20;
const int WS_EX_NOACTIVATE = 134217728;
const int LSFW_LOCK = 1;
[DllImport( "user32" )]
public static extern bool LockSetForegroundWindow( uint UINT );
[DllImport( "user32" )]
public static extern IntPtr SetWindowLong( IntPtr hWnd, int nIndex, int dwNewLong );
}
First problem : It's works, but I've to select an other window to "remove" the focus of my application (after the focus is not gave again, even if I click on my window).
Second problem : When I move or resize the window, the modifications happens when I drop the window.
Do you have any ideas / links / docs ?
Thank you :)
You might want to have a look at this SO post: Make a form not focusable in C#. The answer is specific to Windows Forms. However, the main part is done using Win32 functions, so maybe you can get some ideas from there...

Subscribing to mouse events of all controls in form

How can i easily catch the "mouse down" events of all the controls in a form, without manually subscribing to each and every event? (C#)
Something like the "KeyPreview" feature, but for the mouse events.
I found this to be the best solution for my purposes.
Create a new class derived from IMessageFilter:
public class GlobalMouseHandler : IMessageFilter
{
private const int WM_LBUTTONDOWN = 0x201;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_LBUTTONDOWN)
{
// do something
((YourMainForm)Form.ActiveForm).YourMainForm_Click(null, null);
}
return false;
}
}
Then in your main form add this to register the message filter:
GlobalMouseHandler globalClick = new GlobalMouseHandler();
Application.AddMessageFilter(globalClick);
And add this function to do whatever you have to, in your form:
public void YourMainForm_Click(object sender, EventArgs e)
{
// do anything here...
}
Solution 1
Subscribing to each and every event on every control within a form is certainly the most simplest approach to take, since you just use the code given by Ramesh.
However, another technique involves overriding the default windows message processing method ("WndProc") on the parent control - in this case, the form that contains all the controls.
This has a side effect that you won't be able to detect when the mouse cursor moves over controls contained inside another parent control.
For example, you won't be able to detect when the mouse cursor is over a TextBox that is contained inside a TabControl. This is because the TabControl will continue to process all mouse events.
Solution 2
The following solution will overcome all issues in attempting to detect which control the mouse cursor is over using a technique known as windows hooks.
Hooks essentially allow us to trap mouse and keyboard events even before they are dispatched to the window with focus.
Here's a sample:
public enum HookType : int
{
WH_JOURNALRECORD = 0,
WH_JOURNALPLAYBACK = 1,
WH_KEYBOARD = 2,
WH_GETMESSAGE = 3,
WH_CALLWNDPROC = 4,
WH_CBT = 5,
WH_SYSMSGFILTER = 6,
WH_MOUSE = 7,
WH_HARDWARE = 8,
WH_DEBUG = 9,
WH_SHELL = 10,
WH_FOREGROUNDIDLE = 11,
WH_CALLWNDPROCRET = 12,
WH_KEYBOARD_LL = 13,
WH_MOUSE_LL = 14
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
}
[StructLayout(LayoutKind.Sequential)]
public struct MouseHookStruct
{
public POINT pt;
public int hwnd;
public int hitTestCode;
public int dwExtraInfo;
}
[DllImport("user32.dll", SetLastError = true)]
static extern int SetWindowsHookEx(HookType hook, HookProc callback, IntPtr hInstance, uint dwThreadId);
[DllImport("user32.dll", SetLastError= true)]
static extern int CallNextHookEx(int hook, int code, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
static extern int GetLastError();
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();
public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
private static int hHook;
public Form1()
{
InitializeComponent();
hHook = SetWindowsHookEx(HookType.WH_MOUSE, MouseHookProc, IntPtr.Zero, (uint)GetCurrentThreadId());
if (hHook == 0)
MessageBox.Show("GetLastError: " + GetLastError());
}
private int MouseHookProc(int code, IntPtr wParam, IntPtr lParam)
{
//Marshall the data from the callback.
MouseHookStruct mouseInfo = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct));
if (code < 0)
{
return CallNextHookEx(hHook, code, wParam, lParam);
}
else
{
//Create a string variable that shows the current mouse coordinates.
String strCaption = "x = " + mouseInfo.pt.X.ToString("d") +
" y = " + mouseInfo.pt.Y.ToString("d");
//You must get the active form because it is a static function.
Form tempForm = Form.ActiveForm;
Control c = Control.FromHandle((IntPtr)mouseInfo.hwnd);
if (c != null)
label1.Text = c.Name;
else
label1.Text = "Control not found";
//Set the caption of the form.
tempForm.Text = strCaption;
return CallNextHookEx(hHook, code, wParam, lParam);
}
}
Other conttrols in the form cannot listen to the Mouse event handlers of the form. Because each control got its own mouse event listners.
But You can subscribe each controls mouse events to the forms mouse events
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MDown);
this.label1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MDown);
this.ListBox1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.Form1_MDown);
this way you can have single handler for all the controls mouse events.

How to remove minimize and maximize buttons from a resizable window?

WPF doesn't provide the ability to have a window that allows resize but doesn't have maximize or minimize buttons. I'd like to able to make such a window so I can have resizable dialog boxes.
I'm aware the solution will mean using pinvoke but I'm not sure what to call and how. A search of pinvoke.net didn't turn up any thing that jumped out at me as what I needed, mainly I'm sure because Windows Forms does provide the CanMinimize and CanMaximize properties on its windows.
Could someone point me towards or provide code (C# preferred) on how to do this?
I've stolen some code I found on the MSDN forums and made an extension method on the Window class, like this:
internal static class WindowExtensions
{
// from winuser.h
private const int GWL_STYLE = -16,
WS_MAXIMIZEBOX = 0x10000,
WS_MINIMIZEBOX = 0x20000;
[DllImport("user32.dll")]
extern private static int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
extern private static int SetWindowLong(IntPtr hwnd, int index, int value);
internal static void HideMinimizeAndMaximizeButtons(this Window window)
{
IntPtr hwnd = new System.Windows.Interop.WindowInteropHelper(window).Handle;
var currentStyle = GetWindowLong(hwnd, GWL_STYLE);
SetWindowLong(hwnd, GWL_STYLE, (currentStyle & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX));
}
}
The only other thing to remember is that for some reason this doesn't work from a window's constructor. I got around that by chucking this into the constructor:
this.SourceInitialized += (x, y) =>
{
this.HideMinimizeAndMaximizeButtons();
};
One way is to set your ResizeMode="NoResize". It will behave like this.
Don't know if this works for your req. visually.. This is
<Window x:Class="DataBinding.MyWindow" ...Title="MyWindow" Height="300" Width="300"
WindowStyle="ToolWindow" ResizeMode="CanResizeWithGrip">
If anyone use Devexpress window (DXWindow) accepted answer doesn't work. One ugly approach is
public partial class MyAwesomeWindow : DXWindow
{
public MyAwesomeWIndow()
{
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
// hides maximize button
Button button = (Button)DevExpress.Xpf.Core.Native.LayoutHelper.FindElementByName(this, DXWindow.ButtonParts.PART_Maximize.ToString());
button.IsHitTestVisible = false;
button.Opacity = 0;
// hides minimize button
button = (Button)DevExpress.Xpf.Core.Native.LayoutHelper.FindElementByName(this, DXWindow.ButtonParts.PART_Minimize.ToString());
button.IsHitTestVisible = false;
button.Opacity = 0;
// hides close button
button = (Button)DevExpress.Xpf.Core.Native.LayoutHelper.FindElementByName(this, DXWindow.ButtonParts.PART_CloseButton.ToString());
button.IsHitTestVisible = false;
button.Opacity = 0;
}
}
Here's a solution I'm using. Note that maximize button is still displayed.
Markup:
<Window x:Class="Example"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Example"
StateChanged="Window_StateChanged">
Code behind:
// Disable maximizing this window
private void Window_StateChanged(object sender, EventArgs e)
{
if (this.WindowState == WindowState.Maximized)
this.WindowState = WindowState.Normal;
}
This variant of the solution proposed by #MattHamilton can (and must) be called in the constructor of the Window. The trick is to subscribe a delegate to the SourceInitialized event within the extension method.
private const int GWL_STYLE = -16, WS_MAXIMIZEBOX = 0x10000, WS_MINIMIZEBOX = 0x20000;
[DllImport("user32.dll")]
extern private static int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
extern private static int SetWindowLong(IntPtr hwnd, int index, int value);
/// <summary>
/// Hides the Minimize and Maximize buttons in a Window. Must be called in the constructor.
/// </summary>
/// <param name="window">The Window whose Minimize/Maximize buttons will be hidden.</param>
public static void HideMinimizeAndMaximizeButtons(this Window window)
{
window.SourceInitialized += (s, e) => {
IntPtr hwnd = new System.Windows.Interop.WindowInteropHelper(window).Handle;
int currentStyle = GetWindowLong(hwnd, GWL_STYLE);
SetWindowLong(hwnd, GWL_STYLE, currentStyle & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX);
};
}
You can set the ResizeMode="NoResize" of the window if you want to remove Minimize and Maximize button
Just use
WindowStyle="ToolWindow"
It hides the maximize and minimize buttons, but the window can still be resized by dragging the window borders and minimize using the hide button in the bottom right corner of the taskbar.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.window.windowstyle?view=windowsdesktop-6.0

Resources