MouseScroll event does not trigger when TextBox is scrollable - c#

I found that when I attach a MouseWheel event to my UserControl or a TextBox, it does not trigger if the TextBox is scrollable. It will scroll the textbox and not trigger MouseWheel, can I make it such that it runs MouseWheel and then after doing something e.Cancel it so that the TextBox does not scroll?
UPDATE: With Video & Code
Video
http://screenr.com/ZGF
Code
http://www.mediafire.com/?x3o09dz6dr5zoym
public MainWindow()
{
InitializeComponent();
textBox.MouseWheel += (s, e) =>
{
Random rand = new Random();
Debug.WriteLine(rand.NextDouble());
};
}

I assume you mean that the MouseWheel event on the UserControl won't trigger. That's normal, the TextBox is happy to accept the message when it is multiline. The reason that the MouseWheel event is not visible in the designer. The parent window will only see the message when the control with the focus won't process it.
Not sure if you should fix this, the user would really expect the text box to scroll. But you can by intercepting the message so the text box can't see it and passing the message to the parent. Add a new class to your project, paste the code shown below. Compile. Drop the new control from the top of the toolbox.
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class MyTextBox : TextBox {
protected override void WndProc(ref Message m) {
if (m.Msg == 0x20a && this.Parent != null) {
PostMessage(this.Parent.Handle, m.Msg, m.WParam, m.LParam);
}
else base.WndProc(ref m);
}
[DllImport("user32.dll")]
private static extern IntPtr PostMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}

Related

Detecting click anywhere on form (including controls) in WinForms

A WinForms application, in which a certain UI control has to disappear 5 seconds after the user interacts with the window (clicking anywhere on the form including any control, or key presses).
Assuming an event is fired when this happens, the following is the event handler:
private async void userInteract(object sender, System.EventArgs e)
{
if (progressBarFinished)
{
await Task.Delay(5000);
statusIdle(); // this method hides a progress bar after 5 seconds. It is working.
}
}
In the form constructor, the event is subscribed to as follows:
// at the moment, it is not working for when a user clicks anywhere on the form
// it is working when a specific control click event occurs, like this one
progressBar.Click += userInteract;
Subscribing to the MouseClick and KeyPress events of the Form itself have been tried, but the event does not seem to fire then. It's only when specific control events are used, that it works, as stated above.
A form’s WndProc method processes messages sent to the form by the Windows operating system. This is an extremely important method that allows forms to move, resize, repaint, and perform other critical operations.
// Constants for decoding the Win32 message.
protected const int WM_MOUSEACTIVATE = 0x0021;
protected const int WM_LBUTTONDOWN = 0x201;
protected const int WM_RBUTTONDOWN = 0x204;
protected override void WndProc(ref Message m)
{
// Check the Message parameter to see if the message is WM_MOUSEACTIVATE indicating that a control was clicked.
if (m.Msg == WM_MOUSEACTIVATE)
{
int wparam = m.WParam.ToInt32();
if (wparam == WM_LBUTTONDOWN || wparam == WM_RBUTTONDOWN)
{
// TODO: Do something with the mouse event.
Console.WriteLine(m);
return;
}
}
base.WndProc(ref m);
}
Ref 1
Ref 2

Winform, mouse click out of popup

I have a small popup window , and I'd like to trigger event when I click the mouse outside of the window bound.
I've tried initiating it on OnMouseClick, and check if the mouse cursor is out of the pop up bounds, but it doesn't seem to trigger out of the popup bounds.
For discussion:
public partial class FrmPopup : Form {
public FrmPopup() {
InitializeComponent();
}
const uint WM_NCACTIVATE = 0x0086;
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
if (m.Msg == WM_NCACTIVATE && m.WParam == IntPtr.Zero) {
if(!ClientRectangle.Contains(PointToClient(Control.MousePosition)) && MouseButtons == MouseButtons.Left)
label1.Text = "Clicked outside of window";
}
}
}
Have you considered the Deactivate event? This will fire when the form loses focus. This could be from a mouse click OR a context switch, but may deliver what you want.

Adding scroll bar to windows forms, c#

I need to add scroll horizontal and vertical scroll bar. The problem is that they doesn't work, as in when I use them the screen doesn't move.
VScrollBar vScrollBar1 = new VScrollBar();
HScrollBar hScrollBar1 = new HScrollBar();
vScrollBar1.Dock = DockStyle.Left;
hScrollBar1.Dock = DockStyle.Bottom;
Controls.Add(vScrollBar1);
Controls.Add(hScrollBar1);
I use the code to add scroll bars, how do I activate them or get them to work as I need?
Thanks!
You usually don't add Scrollbars; you set AutoScroll = true in the form's property panel.
Now when any control grows out of the Form or is moved over right or bottom border the Form will show the necessary Scrollbar.
You can test it with a Label and a TextBox: Set the Label to the right border and script the TextBox's TextChanged event like this:
private void textBox1_TextChanged(object sender, EventArgs e)
{
label1.Text = textBox1.Text;
}
Now run the programm and enter stuff into the Textbox; you will observe how the Label grows and how the Form brings up a horizontal Scrollbar when it goes over the edge.
Note 1: This will not work if the Form has AutoSize = true - then instead the form will grow! If the Form has both AutoSize and AutoScroll true, then AutoSize will win.
Note 2: This test will only work if the Label has AutoSize = true, as it has by default..
You need to use the Panel control as container of your child controls and set "AutoScroll" property to true.
Set true to AutoScroll property of Form.
Write this code in your Form Load Event, and you will get your scroll bar, like I am writing it here in my Form Load Event.
private void Form1_Load(object sender, EventArgs e)
{
Panel my_panel = new Panel();
VScrollBar vScroller = new VScrollBar();
vScroller.Dock = DockStyle.Right;
vScroller.Width = 30;
vScroller.Height = 200;
vScroller.Name = "VScrollBar1";
my_panel.Controls.Add(vScroller);
}
vScrollbars and hScrollbars are just plain controls without code. [UI]
You need to code to make them do something!
Or just set the property 'AutoScroll = true;' in your form or add a panel and set it to true.
However your control needs Focus() to scroll with your mouse wheel.
Here is a little workaround:
public Main()
{
InitializeComponent();
//Works for panels, richtextboxes, 3rd party etc..
Application.AddMessageFilter(new ScrollableControls(panel1, richtextbox1, radScrollablePanel1.PanelContainer));
}
ScrollableControls.cs:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
//Let controls scroll without Focus();
namespace YOURNAMESPACE
{
internal struct ScrollableControls : IMessageFilter
{
private const int WmMousewheel = 0x020A;
private readonly Control[] _controls;
public ScrollableControls(params Control[] controls)
{
_controls = controls;
}
bool IMessageFilter.PreFilterMessage(ref Message m)
{
if (m.Msg != WmMousewheel) return false;
foreach (var item in _controls)
{
ScrollControl(item, ref m);
}
return false;
}
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
private static void ScrollControl(Control control, ref Message m)
{
if (control.RectangleToScreen(control.ClientRectangle).Contains(Cursor.Position) && control.Visible)
{
SendMessage(control.Handle, m.Msg, m.WParam.ToInt32(), m.LParam.ToInt32());
}
}
}
}

Winforms - WM_NCHITEST message for click on control

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.

C# Application-Wide Left Mouse Click Event

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

Categories