I have a class based on the NativeWindow class and I want to be able to perform some action when the user clicks inside of it. Here is what I tried:
Code removed
However it doesn't work. Debugging suggests for some reason the message is never 0x0201 which is supposedly a left mouse button click. What's wrong with it?
I'm not sure tooltips can receive a WM_LBUTTONDOWN. You could try using the TTM_RELAYEVENT message that's meant for passing a mouse message to a tooltip control for processing, something like this:
protected override void WndProc(ref System.Windows.Forms.Message m)
{
const int TTM_RELAYEVENT = 0x407;
if (m.Msg == TTM_RELAYEVENT)
{
Message relayed = (Message)Marshal.PtrToStructure(m.LParam, typeof(Message));
if (related.Msg == WM_LBUTTONDOWN)
{
// Do something
}
}
base.WndProc(ref m);
}
Related
I am trying to detect a click and preform calling a function only after the click actually happened.
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m) {
//Detect a Click
if (m.Msg == 0x210 && m.WParam.ToInt32() == 513){
lastClick = DateTime.Now;
clickedHappened();
Debug.Print("Click Detected!");
}
base.WndProc(ref m);
}
private void clickedHappened(){
MessageBox.Show("Click Already Happened");
}
I think that WndProc happens way before the actual click takes place.
I was wondering if there was a way to solve this with out using a timer? or sleep(400);
The only solution I can come up with is using a timer, but I want to get rid of some of my existing timers. It seems that the click actually happens 200 - 350 ms after it was detected in WndProc.
WndProc is short for Window Procedure, its the procedure that handles everything for the window, drawing, mouse capture, keyboard capture, resizing...
There are also several ways you can capture the mouse for varying results
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m)
{
switch(m.Msg)
{
case 0x201:
//left button down
break;
case 0x202:
clickedHappened(); //left button up, ie. a click
break;
case 0x203:
//left button double click
break;
}
base.WndProc(ref m);
}
The thing is, windows forms in C# already handles all of these for you and from the WndProc, fires events there is no real need to handle WndProc yourself for this type of thing.
For a full list of mouse notification messages, see MSDN: Mouse Input Notifications and for a list of all wndproc messages, see MSDN System Defined Messages
Probably, you can handle WM_LBUTTONUP message that occured after click.
You can do it like this:
const int WM_LBUTTONUP = 0x202;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_LBUTTONUP)
{
clickedHappened();
}
base.WndProc(ref m);
}
I have a simple windows form with no border and several label controls (nothing that needs to be clicked). I needed to be able to allow the user to move the form by clicking anywhere on it, so I found this question, and used the following code found there.
private const int WM_NCHITTEST = 0x84;
private const int HTCLIENT = 0x1;
private const int HTCAPTION = 0x2;
protected override void WndProc(ref Message m)
{
switch (m.Msg) {
case WM_NCHITTEST:
base.WndProc(ref m);
if ((int)m.Result == HTCLIENT) {
m.Result = (IntPtr)HTCAPTION;
return;
} else {
return;
}
break;
}
base.WndProc(ref m);
}
This works well...to a point. If I click anywhere on the form itself (the background), WM_NCHITTEST is HTCLIENT, so I can move my form as expected. However, if I click on a label control itself, the message is something different, and I can't tell what it is.
I found this article about the various possible values for WM_NCHITTEST but none of them seem to be what I need.
I realize I could disable all my label controls and that would allow me to click "on" them as if it was the form itself, but I'm wondering if there's a better/different way to do this.
Thanks for the help!
You are overriding the WndProc for the form, but when the cursor is over a label the WM_NCHITTEST message is sent to the label.
You could create your own label control derived from Label and override its WndProc. This should always return HTTRANSPARENT in response to WM_NCHITTEST. Something like:
private const int HTTRANSPARENT = -1;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_NCHITTEST:
m.Result = (IntPtr)HTTRANSPARENT;
return;
}
base.WndProc(ref m);
}
Also note that there's a small bug in your WndProc. If the message is WM_NCHITTEST but the region isn't HTCLIENT then you call the base class twice.
I want to play a sound when the left mouse button is clicked anywhere in my form without having to place Mouse click events on every single control in the form. Is there a way to accomplish this?
You can detect the Windows notification before it is dispatched to the control with the focus with the IMessageFilter interface. Make it look similar to this:
public partial class Form1 : Form, IMessageFilter {
public Form1() {
InitializeComponent();
Application.AddMessageFilter(this);
this.FormClosed += delegate { Application.RemoveMessageFilter(this); };
}
public bool PreFilterMessage(ref Message m) {
// Trap WM_LBUTTONDOWN
if (m.Msg == 0x201) {
System.Diagnostics.Debug.WriteLine("BEEP!");
}
return false;
}
}
This works for any form in your project, not just the main one.
This should do the trick
const int WM_PARENTNOTIFY = 0x210;
const int WM_LBUTTONDOWN = 0x201;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_LBUTTONDOWN || (m.Msg == WM_PARENTNOTIFY && (int)m.WParam == WM_LBUTTONDOWN))
DoIt();
base.WndProc(ref m);
}
This project may be overkill for your needs (since it hooks global mouse events, not just ones on your form), but I think it shows the basics of what you need.
Case WM_PARENTNOTIFY
Select Case Wparam
Case 513 ' WM_LBUTTODOWN
PlaySoundA
End Select
Using For Vb Or Vba
I saw the code posted on this forum for moving the WinForm without borders but my dialog (C#) has a panel covering the whole area. I know I have to use WndProc to do this. I don't know what to do at this point. My window doesn't move unless I expose some of it by shrinking the size of the panel. Thank you.
The code I have:
protected override void WndPro(ref Message m)
{
switch(m.Msg)
{
case 0x84:m.Result = new intPtr(0x2);
return
}
base.wndProc(ref m);
}
You'll need to give the panel the same kind of treatment, except that you return HTTRANSPARENT. That makes it transparent to hit tests and the form will get the message. Now it works. Add a class to your project and paste the code shown below. Compile. Replace your existing panel with this one.
using System;
using System.Windows.Forms;
class BackPanel : Panel {
protected override void WndProc(ref Message m) {
if (m.Msg == 0x84) m.Result = (IntPtr)(-1);
else base.WndProc(ref m);
}
}
I am using the Scintilla control from ScintillaNet, and I need to have some control over the paste operations (in order to be able to check some things and/or update the text to be pasted).
I've tried to create a subclass of the Scintilla control and override the WndProc method.
Then, I intercept the WM_PASTE message (0x0302), no luck. I never catch it.
Here is the code I use:
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_PASTE)
{
MessageBox.Show("Paste");
}
base.WndProc(ref m);
}
Any idea?
You could remove ScintillaNET's built in CTRL+V handler with:
scintilla.Commands.RemoveBinding(Keys.V, Keys.Control, ScintillaNet.BindableCommand.Paste);
And add your own CTRL+V handler (menu item?) to do a:
ScintillaNet.Selection.Text = your_processed_clipboard_data;
That would insert at the current cursor position, or replace the current selection.