C# Disable click/doubleClick or any WindowsMessage - c#

My goal is to disable all DoubleClick() events in my application. Just simply unsubscribing from those events is not possbile, because those are external custom controls. So I am aiming for disabling either the DoubleClick() for those controls or my whole application, it doesn't really matter.
What I am trying to do is to intervene once the window gets a WindowsMessage WM and the ID number Message.Msg is the code of e.g. a Button.Click() event.
private const int WM_COMMAND = // ???
if (m.Msg == WM_COMMAND)
...
But no matter which WindowsMessage notification code I use, I don't get the correct one which gets fired once a control gets clicked. But I was abel to intervene on a DoubeClick() on the Form.
private const int WM_NCLBUTTONDBLCLK = 0x00A3;
private const int WM_LBUTTONDBLCLK = 0x0203;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_NCLBUTTONDBLCLK || m.Msg == WM_LBUTTONDBLCLK)
{
m.Result = IntPtr.Zero;
return;
}
base.WndProc(ref m);
}
This works totally fine and disabels a DoubleClick on the client area and non client area of the Form. But in which area do I locate when I am hovering a control? Because those WindowsMessages referred to either the client area and non client area dont' get fired when I am on a control.
Sent when the user clicks a button.
The parent window of the button receives this notification code through the WM_COMMAND message.
MSDN doc about a button click notifaction message
The WM_COMMAND message has this notfication code:
private const int WM_COMMAND = 0x0111;
So when I try to react to this message being sent I can't, because this message doesn't get fired.
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_COMMAND)
MessageBox.Show("foo"); // nothing happens
base.WndProc(ref m);
}
What do I miss or missunderstand about this WindowsMessage?

Since no one posted an answer yet and I found a well working solution here is my attempt to disable all DoubleClick()s or just for one/a few desired Control/s.
The problem is that WndProc() doesn't filter or respond to all WMs that are being sent. Therefore the WM for a default doubleClick on the non client area 0x0203 can't get detected. So I went deeper to already filter the WMs before they reach WndProc(). One way is to use a MessageFilter that is being assigned to my Application.
private MessageFilter msgFilter = new MessageFilter();
Application.AddMessageFilter(msgFilter);
The method Param is an object of my own class MessageFilter, which needs to implement the IMessageFilter Interface in order to use it's PreFilterMessage(). This method gets invoked for each single WM that gets send to your application.
class MessageFilter : IMessageFilter
{
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == 0x0203)
{
m.Result = IntPtr.Zero;
return true;
}
else
return false;
}
}
So basically once there is a WM with the desired Message.Msg ID, I set the Result to zero so the DoubleClick() is completely disabled for the whole application.
Later on it turned out it would be better to disable the DoubleClick() only from one Control. Since every Control in Windows has a unique Handle HWnd, which is part of the Message, I can use it to scan if the certain Message aims to my specific single Control.
When you create your Control either subscribe in the .Designer.cs to the event:
this.yourControl.HandleCreated += new System.EventHandler(this.yourControl_HandleCreated);
Or if it's a custom Control subscribe to the event after it's creation. In the event you assign the value of the created Handle to any instance your MessageFilter has access to, like a Property in this class.
private void yourControl_HandleCreated(object sender, EventArgs e)
{
msgFilter.OwnHandle = yourControl.Handle;
}
After that simply add a second condition to the if statement:
if (m.Msg == 0x0203 && OwnHandle == m.HWnd)
...

Related

WndProc Overloading + Unmanaged DLL Wrapper: Better way?

(BTW this is C# .NET 4.5)
I have some unmanaged DLL that talks to some hardware. I wrap a bunch of code and get something simple, as a class object, that I can create in a WinForm.
private AvaSpec AS = new AvaSpec();
public AvaSpec_Form()
{
InitializeComponent();
AS.SpectrumMeasuredEvent += (se, ev) => { SpectrumMeasured(ev); };
AS.Init(this.Handle);
AS.Activate();
// configure as desired
// AS.l_PrepareMeasData.m_IntegrationDelay = 0;
if (AS.DeviceList.Count > 0)
{
AS.Start();
}
}
However, the DLL relies on receiving messages through WndProc. The best way I could figure out to do this is to overload the WndProc method on the Form:
protected override void WndProc(ref Message m)
{
// catch WndProc messages that AvaSpec defines as its own
if (m.Msg == AvaSpec.WM_MEAS_READY ||
m.Msg == AvaSpec.WM_APP ||
m.Msg == AvaSpec.WM_DBG_INFOAs ||
m.Msg == AvaSpec.WM_DEVICE_RESET )
{
AS.WndProcMessageReceived(ref m);
}
// else pass message on to default message handler
base.WndProc(ref m);
}
How can I hide this overload somehow in the class definition so that the overload method does not need to be added to the Form itself? There is some talk of the IMessageFilter interface, but it still looks to require some code in the form to add the filter. Any ideas on how to make this more elegant?
Ok I figured it out based on Colin Smith's hints.
You derive your class from NativeWindow:
https://msdn.microsoft.com/en-us/library/system.windows.forms.nativewindow(v=vs.110).aspx
Then assign the parent (form) Handle (that you pass by some initialization) to the Handle that NativeWindow provides to the class object. Then, you can overload the WndProc method directly in the object.
// object definition
public class AvaSpec : NativeWindow
{
protected override void WndProc(ref Message m)
{
// catch WndProc messages that AvaSpec defines as its own
if (m.Msg == AvaSpec.WM_MEAS_READY ||
m.Msg == AvaSpec.WM_APP ||
m.Msg == AvaSpec.WM_DBG_INFOAs ||
m.Msg == AvaSpec.WM_DEVICE_RESET)
{
WndProcMessageReceived(ref m);
}
// Call base WndProc for default handling
base.WndProc(ref m);
}
...(snip)
public void Init(IntPtr parentHandle)
{
this.AssignHandle(parentHandle);
...(snip)
and use it (pass handle pointer via some init) like so:
// WinForm definition
public partial class AvaSpec_X : Form
{
private AvaSpec AS = new AvaSpec();
public AvaSpec_X()
{
InitializeComponent();
AS.SpectrumMeasuredEvent += (se, ev) => { SpectrumMeasured(ev); };
AS.Init(this.Handle);
AS.Activate();
// configure as desired
//AS.l_PrepareMeasData.m_IntegrationDelay = 0;
if (AS.DeviceList.Count > 0)
{
AS.Start();
}
}
...(snip)
You could create a hidden modeless "form"/window and then use its .Handle in the call to 'AS.Init'.
By using a separate "window" rather than piggy-backing onto the main application window, it offers a bit better encapsulation.
For example, if in the future you needed to support the handling of multiple devices at the same time...then the "separate" windows would enable good separation of messages for different devices.
Your hardware/device handling code might use wParam or lParam to identify the "device id"...but it's more likely to be using them for something else, and relying on the "window destination" as the distinguisher.
Then let the main apps UI thread message pump...automatically dispatch messages to the windows you have created.
In your message handling code for that "window", you would handle messages, which would include the special privately registered messages such as WM_DBG_INFOAs, etc...which you then forward back to the AvaSpec via WndProcMessageReceived.
If that AvaSpec class is relying on you processing those messages in a timely fashion, then you might need to then explore creating multiple UI threads.
This might be needed if your main apps UI thread was getting overloaded, or was "busy" processing other messages e.g when resizing, moving window, etc.
By having a separate UI thread that is pumping the messages for your hidden "device" window, then it might provide a better response for your "device".
Note: multiple UI threads is an advanced topic, and there are some gotchas, but basically it involves creating a thread, telling it to use the STA (single-threaded apartment), creating your window form, and then usually use Application.Run with that form to cause message pumping.

windows 8 tap and hold event c#

I'm trying to write an application that senses when someone taps and holds something. I am using windows forms. I tried using the mouse down even but it doesn't appear to fire all the time. This is also going to be a multi touch application. I'm going to have two buttons , and the user can tap and hold one button, while they press on the other button. Or Just press one button. I'm not even sure how a windows form app can handle that.
All the examples inhave seen for a windows touch app use xaml. Is this really the only way to capture tap and hold ??
I'm essentially making an onscreen keyboard here, and I don't think that isnpossible WITHOUT windows forms. Correct me if I am wrong here.
Any help or guidance in this is greatly appreciated. Thanks.
If your program is running on Windows 8, you can use the WM_POINTER API to get the input you need. Override WndProc to capture the messages. You will have to do some P/Invoke to get it working, but it's not terribly hard. Here's some incomplete code to get you started, you'll need to add cases for up, down, and update events for each type of pointer you want to track. Keep track of the pointer IDs to process multi touch. To handle the press-and-hold you'll need to track the time yourself from WM_POINTERDOWN to WM_POINTERUP and act accordingly. Hope this helps.
public const int WM_POINTERDOWN = 0x0246;
public const int WM_POINTERUP = 0x0247;
public const int WM_POINTERUPDATE = 0x0245;
public enum POINTER_INPUT_TYPE : int
{
PT_POINTER = 0x00000001,
PT_TOUCH = 0x00000002,
PT_PEN = 0x00000003,
PT_MOUSE = 0x00000004
}
public static uint GET_POINTERID_WPARAM(uint wParam) { return wParam & 0xFFFF; }
[DllImport("User32.dll")]
public static extern bool GetPointerType(uint pPointerID, out POINTER_INPUT_TYPE pPointerType);
protected override void WndProc(ref Message m)
{
bool handled = false;
uint pointerID;
POINTER_INPUT_TYPE pointerType;
switch(m.Message)
{
case WM_POINTERDOWN:
pointerID = User32.GET_POINTERID_WPARAM((uint)m.WParam);
if (User32.GetPointerType(pointerID, out pointerType))
{
switch (pointerType)
{
case POINTER_INPUT_TYPE.PT_PEN:
// Stylus Down
handled = true;
break;
case POINTER_INPUT_TYPE.PT_TOUCH:
// Touch down
handled = true;
break;
}
}
break;
}
if (handled)
m.Result = (IntPtr)1;
base.WndProc(ref m);
}
This question has been around for a while and might benefit from a simple approach. You can simulate the "tap and hold" (or click and hold) by measuring the time between the MouseDown event and the Click event (which fires before MouseUp). If the time is greater than some value then you cancel the Click and (perhaps) fire your own TapAndHold event. I have created a test control that anyone can use to try this approach out. Just add a UserControl to your test app (I called mine TestTapAndHold) and then paste in the following:
public partial class TestTapAndHold : UserControl
{
private string showText = "Tap Me";
private DateTime mouseDown;
private const int holdTime = 500;
public TestTapAndHold()
{
InitializeComponent();
this.Paint += drawText;
}
public delegate void OnTapAndHold(EventArgs e);
public event OnTapAndHold TapAndHold;
private void drawText(object sender, PaintEventArgs e)
{
using (var drawBrush = new SolidBrush(Color.Black))
{
e.Graphics.DrawString(showText, Font, drawBrush, new Point(5,3));
}
}
protected override void OnClick(EventArgs e)
{
if (DateTime.Now.Subtract(mouseDown).Milliseconds >= holdTime)
{
showText = "Tap Hold";
TapAndHold?.Invoke(e);
} else
{
base.OnClick(e);
showText = "Tapped";
}
Invalidate();
}
private void TestTapAndHold_MouseDown(object sender, MouseEventArgs e)
{
mouseDown = DateTime.Now;
}
}
Build the app and then pop one of the test controls onto a form. You can then add an event handler to your form like:
private void testTapAndHold1_TapAndHold(EventArgs e)
{
MessageBox.Show("You tapped and Held");
}
This general approach enabled me to add "Tap and Hold" functionality to a Windows Forms app running on a Microsoft Surface 4

Sync mouseclicks between controls

I’ve got a TableLayoutPanel and a Treeview and I would like to sync the mouseclicks with each other.
The reason for this is that I want to be able to select something in my TableLayoutPanel and then it should also select something in the Treeview.
This is how it looks:
My first attempt works but there is some delay.
I hooked up my Treeview to the NodeMouseClick event and when that event fires I Refresh() the TableLayoutPanel so the CellPaint event gets called and paints the whole row. With this approach I’m seeing some delay because the Treeview gets painted first and then the TableLayoutPanel.
When I’m using the same method but the other way around (click the TableLayoutPanel and select the corresponding node in the Treeview) I don’t get AS MUCH delay. I’m guessing this is because it takes longer to paint my rows than it takes to select a node.
I tried a different solution:
class TableControl : TableLayoutPanel
{
TreeViewWithPaint m_TreeviewChild;
public void AddChildControl(TreeViewWithPaint treeview)
{
m_TreeviewChild = treeview;
}
protected override void WndProc(ref Message message)
{
const int WM_LBUTTONDOWN = 0x201;
switch (message.Msg)
{
case WM_LBUTTONDOWN:
//invalidate our table control so the OnPaint Method gets fired
this.Update();
//now copy the message and send it to the treeview
Message copy = new Message
{
HWnd = m_TreeviewChild.Handle,
LParam = message.LParam,
Msg = message.Msg,
Result = message.Result,
WParam = message.WParam
};
//pass the message onto the linked tree view
m_TreeviewChild.RecieveWndProc(ref copy);
break;
}
base.WndProc(ref message);
}
In my Treeview class I added this:
public void RecieveWndProc(ref Message m)
{
base.WndProc(ref m);
}
I got the idea of an example how to sync Treeview scrollbars
The problem with this is that the CellPaint event in the TableLayoutPanel isn’t getting fired anymore, even with Update()… It does however select the correct node in the Treeview:
I’m also foreseeing some problems with this if I try to implement the same thing in Treeview (overriding the WndProc), will this cause a crazy loop of copied messages?
So is there a(n) (easy) way to do this?
Thanks  
Solved it, instead of trying to send another click message to the TableLayoutPanel I just did all the painting in the Treeview WM_LBUTTONDOWN (I did the same for the TableLayoutPanel WM_LBUTTONDOWN message)
const int WM_LBUTTONDOWN = 0x201;
switch( message.Msg )
{
case WM_LBUTTONDOWN:
Int16 x = (Int16)message.LParam;
Int16 y = (Int16)((int)message.LParam >> 16);
//Getting the control at the correct position
Control control = m_TableControl.GetControlFromPosition(0, (y / 16));
if (control != null)
m_TableControl.Refresh();
TreeNode node = this.GetNodeAt(x, y);
this.SelectedNode = node;
break;
}

How can i get messages in C#?

How can I get a specific message on a specific method?
I've seen some examples and people use "ref" ,but I dont understand it.
In delphi,for example,my function(method) must be declared in the Main Form class and next to the declaration I have to put the message
type
TForm1 = class(TForm)
...
protected
procedure MessageHandler(var Msg:Tmessage);Message WM_WINSOCK_ASYNC_MSG;
end;
I need this in C# so I can use WSAAsyncSelect in my application
Check >my other Question< with bounty 550 reputation to understand what I mean
You can override the WndProc method on a control (e.g. form).
WndProc takes a reference to a message object. A ref parameter in C# is akin to a var parameter in Delphi. The message object has a Msg property that contains the message type, e.g (from MSDN):
protected override void WndProc(ref Message m)
{
// Listen for operating system messages.
switch (m.Msg)
{
// The WM_ACTIVATEAPP message occurs when the application
// becomes the active application or becomes inactive.
case WM_ACTIVATEAPP:
// The WParam value identifies what is occurring.
appActive = (((int)m.WParam != 0));
// Invalidate to get new text painted.
this.Invalidate();
break;
}
base.WndProc(ref m);
}
In .NET winforms, all messages go to WndProc, so you can override that:
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_WINSOCK_ASYNC_MSG)
{
// invoke your method
}
else
{
base.WndProc(ref m);
}
}
If I have misunderstood, please say - but I think you would do well to avoid this low-level approach, and describe what you want to achieve - i.e. it might be that .Invoke/.BeginInvoke are more appropriate.

How to disable the minimize button in C#?

In my application I need to temporarily gray out the minimize button of the main form. Any ideas how this can be achieved? I don't mind doing p/invokes to Win32 dlls.
Edit: Graying out the minimize button would be the preferred solution, but is there any other way of preventing the form from becoming minimized?
I read your comment in regards to my response and was able to drum up a more complete solution for you. I ran this quickly and it seemed to have the behavior that you wanted. Instead of deriving your winforms from Form, derive from this class:
using System;
using System.Windows.Forms;
using System.ComponentModel;
namespace NoMinimizeTest
{
public class MinimizeControlForm : Form
{
private const int WM_SYSCOMMAND = 0x0112;
private const int SC_MINIMIZE = 0xf020;
protected MinimizeControlForm()
{
AllowMinimize = true;
}
protected override void WndProc(ref Message m)
{
if (!AllowMinimize)
{
if (m.Msg == WM_SYSCOMMAND)
{
if (m.WParam.ToInt32() == SC_MINIMIZE)
{
m.Result = IntPtr.Zero;
return;
}
}
}
base.WndProc(ref m);
}
[Browsable(true)]
[Category("Behavior")]
[Description("Specifies whether to allow the window to minimize when the minimize button and command are enabled.")]
[DefaultValue(true)]
public bool AllowMinimize
{
get;
set;
}
}
}
You could do a bit more if you wanted to be able to decide whether to allow minimizing at the time the click is sent, for instance:
using System;
using System.Windows.Forms;
using System.ComponentModel;
namespace NoMinimizeTest
{
public class MinimizeControlForm : Form
{
private const int WM_SYSCOMMAND = 0x0112;
private const int SC_MINIMIZE = 0xf020;
protected MinimizeControlForm()
{
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_SYSCOMMAND)
{
if (m.WParam.ToInt32() == SC_MINIMIZE && !CheckMinimizingAllowed())
{
m.Result = IntPtr.Zero;
return;
}
}
base.WndProc(ref m);
}
private bool CheckMinimizingAllowed()
{
CancelEventArgs args = new CancelEventArgs(false);
OnMinimizing(args);
return !args.Cancel;
}
[Browsable(true)]
[Category("Behavior")]
[Description("Allows a listener to prevent a window from being minimized.")]
public event CancelEventHandler Minimizing;
protected virtual void OnMinimizing(CancelEventArgs e)
{
if (Minimizing != null)
Minimizing(this, e);
}
}
}
For more information about this window notification, see the MSDN article about it.
form.MinimizeBox = false;
or if in the form scope
MinimizeBox = false;
Just do MinimizeBox = false; in your form's code.
Put this code in your form's Resize event:
if (this.WindowState == FormWindowState.Minimized)
{
this.WindowState = FormWindowState.Normal;
}
This will make your form un-minimizable (DISCLAIMER: I do not advocate altering the standard behavior of windows in this way).
You can also implement handle to the Minimize event to cancel the command
Don't. Don't mess with my windows. They are mine, not yours. It is my computer and if I want to minimize, I should be able to. I can't think of, and have never been given, a good reason for doing this.
Coincoin's answer is correct. MinimizeBox is also available as a property in the designer properties window.
#Kevin: While I appreciate the sentiment, that's not always a valid answer. If the application displays a modal dialog box by creating a new instance of a Form and then calling .ShowDialog() on it, you don't want the user to minimize that Form, because then all input on the main UI thread is blocked until that Form's modal status is satisfied. The user could potentially click on the main form and just get the "ding ding ding" unresponsive sound from Windows and not know what to do.
just set the MinimizeBox property of your form to false.
this will disable the minimize button but other buttons will remain functional.

Categories

Resources