I've a user control, pretty simple just a label and a picturebox, they are docked that you can't even see the underlying
most of the events never occurs (click, hover and others)
i think (i could be so wrong), it's because the events won't be triggered as they are being clicked either on the label or the picturebox and not on the background
i need somehow to redirect these events (i.e create that events in the usercontrol and somehow redirect them to my code and with my code i mean the form that uses them) or any other way to capture those events
i've seen like 5 topics about the same problem and none were solved, any workaround ?
You need to redirect your Click event of your label and pictureBox to the OnClick event of your user control using this code:
public UserControl1()
{
InitializeComponent();
this.label1.Click += myClickEvent;
this.pictureBox1.Click += myClickEvent;
}
private void myClickEvent(object sender, EventArgs e)
{
this.OnClick(e);
}
If you can Inherit the Label and PictureBox, Inherit them and override WndProc and do the following.
protected override void WndProc(ref Message m)
{
if (m.Msg == (int)WindowsMessage.WM_NCHITTEST)
{
m.Result = (IntPtr)(-1);//Transparent
return;
}
base.WndProc(ref m);
}
If not, Use this
public partial class MyUserControl : UserControl
{
private TransparentWindow label;
private TransparentWindow pic;
public MyUserControl()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
label = new TransparentWindow(label1);
pic = new TransparentWindow(pictureBox1);
}
}
class TransparentWindow : NativeWindow
{
public TransparentWindow(Control control)
{
this.AssignHandle(control.Handle);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == (int)WindowsMessage.WM_NCHITTEST)
{
m.Result = (IntPtr)(-1);//Transparent
return;
}
base.WndProc(ref m);
}
}
WindowMessage enumeration can be found here
Related question
The answer from Matin Lotfaliee is definetly correct, but in my case, where i want to forward an event from one control to 'OtherControl', you may prefer one-liners:
this.OtherControl.Click += (s, e) => { this.OnClick(e); };
Related
Is there a way to distinguish whether the Enter event on a control was raised by keyboard (Tab, Shift+Tab) or by direct mouse click?
I need to perform an action only when the user is moving to the control using Tab, but not when the user directly clicks on the control. I have tried to intercept the mouse click directly, but it seems the Enter event is raised before Click.
Instead of tracking the Tab key, you can use the WM_MOUSEACTIVATE message to detect activation of the control with the mouse. You could either sub-class each control type you use and override the WndProc method or use a NativeWindow listener class like the one presented below. Depending on how many types of controls you use, it may be less work and clutter to just sub-class those controls to provide a property that indicates that the control was selected using the mouse. It is your decision to make, but the pattern will be the same.
This code is a slight modification of the example shown in the MS documentation.
public class MouseActivateListener : NativeWindow
{
private Control parent;
public MouseActivateListener(Control parent)
{
parent.HandleCreated += this.OnHandleCreated;
parent.HandleDestroyed += this.OnHandleDestroyed;
parent.Leave += Parent_Leave;
this.parent = parent;
if (parent.IsHandleCreated)
{
AssignHandle(parent.Handle);
}
}
private void Parent_Leave(object sender, EventArgs e)
{
MouseActivated = false;
}
private void OnHandleCreated(object sender, EventArgs e)
{
AssignHandle(((Form)sender).Handle);
}
private void OnHandleDestroyed(object sender, EventArgs e)
{
ReleaseHandle();
}
public bool MouseActivated { get; set; }
[System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m)
{
const Int32 WM_MouseActivate = 0x21;
base.WndProc(ref m);
if (m.Msg == WM_MouseActivate && m.Result.ToInt32() < 3)
{
MouseActivated = true;
}
}
}
Example Usage:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private MouseActivateListener textBox1Listener;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
textBox1Listener = new MouseActivateListener(textBox1);
}
private void textBox1_Enter(object sender, EventArgs e)
{
if (textBox1Listener.MouseActivated)
{
MessageBox.Show("Mouse Enter");
}
else
{
MessageBox.Show("Tab Enter");
}
}
}
You can use the Form.KeyPreview event and store the last key press in a variable. Then in your control's Enter event, check the value of the key that was pressed last. If this is a tab, do whatever you need to:
private Keys lastKeyCode;
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
this.lastKeyCode = e.KeyCode;
}
Then in the Enter event, check it:
if (lastKeyCode == Keys.Tab)
{
// Whatever...
}
Intercepting WM_KEYUP and WM_KEYDOWN directly with a message filter to retrieve the state of the Tab key worked. This seems excessive for such a seemingly straightforward task, but apparently the Tab key is suppressed from most windows forms events.
Would be happy to take a cleaner answer, but for now, this is it:
class TabMessageFilter : IMessageFilter
{
public bool TabState { get; set; }
public bool PreFilterMessage(ref Message m)
{
const int WM_KEYUP = 0x101;
const int WM_KEYDOWN = 0x100;
switch (m.Msg)
{
case WM_KEYDOWN:
if ((Keys)m.WParam == Keys.Tab) TabState = true;
break;
case WM_KEYUP:
if ((Keys)m.WParam == Keys.Tab) TabState = false;
break;
}
return false;
}
}
class MainForm : Form
{
TabMessageFilter tabFilter;
public MainForm()
{
tabFilter = new TabMessageFilter();
Application.AddMessageFilter(tabFilter);
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
Application.RemoveMessageFilter(tabFilter);
base.OnFormClosed(e);
}
void control_Enter(object sender, EventArgs e)
{
if (tabFilter.TabState) // do something
else // do domething else
}
}
I have added this
public class MyListView : ListView
{
public event EventHandler<EventArgs> Scrolled;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
const int wm_vscroll = 0x115;
if (m.Msg == wm_vscroll && Scrolled != null)
{
Scrolled(this, new EventArgs());
}
}
}
and when i scroll the mouse wheel it scroll the list perfectly, my scroll event doesn't fire.
I have tried hooking MouseWheel, but the scroll happens after the mousewheel event returns to windows, but still doesn't call Scroll event.
EDIT:-
I have added an OnMouseWheel event that calls my update code, but this is called before the visible area is scrolled, so my update code misses some parts.
I want the mousewheel event to scroll the visible area then call the onScroll event,
OR
for the onscroll event to get called as a by product of the mousewheel scrolling the visible area
Trapping WM_MOUSEWHEEL worked for me:
public class MyListView : ListView
{
private const int WM_MOUSEWHEEL = 0x20a;
public event EventHandler<EventArgs> Scrolled;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_MOUSEWHEEL && Scrolled != null)
{
Scrolled(this, new EventArgs());
}
}
}
default listview's visual tree have ScrollViewer control.
ScrollViewer override OnMouseWheel and set e.handled = true,
if you want to handle this event, you must use EventManager.RegisterClassHandler(Type classType, RoutedEvent routedEvent,Delegate handler,true);
true to invoke this class handler even if arguments of the routed event have been marked as handled;
try it:
class CustomListView: ListView
{
public CustomListView()
{
EventManager.RegisterClassHandler(typeof(CustomListView), MouseWheelEvent, new RoutedEventHandler(OnMouseWheel), true);
}
internal static void OnMouseWheel(object sender, RoutedEventArgs e)
{
//Do something you want
}
}
So I want to instantly, as this portion of the program relies on speed, trigger a function when the windowstate is changed in my main form. I need it to be something like this:
private void goButton_Click(object sender, EventArgs e)
{
//Code
}
I checked through the events tab of the form, I have no WindowStateChanged, etc. How do I do this?
The form will be resized a lot, so checking when the size changes won't work.
The Resize event (or SizeChanged) will fire when the WindowState changes.
On a side note, WPF does include a StateChanged event for this directly.
I hope i'm not too late for the party.
The way I chose to implement it is pretty straight forward and doesn't require allocating global variables, simply check the form's WindowState value before and after base.WndProc is called:
protected override void WndProc(ref Message m)
{
FormWindowState org = this.WindowState;
base.WndProc(ref m);
if (this.WindowState != org)
this.OnFormWindowStateChanged(EventArgs.Empty);
}
protected virtual void OnFormWindowStateChanged(EventArgs e)
{
// Do your stuff
}
Bottom line - it works.
You could try overriding the WndProc function as this link suggests.
From the post:
protected override void WndProc(ref Message m)
{
if (m.Msg == /*WM_SIZE*/ 0x0005)
{
if(this.WindowState == FormWindowState.Minimized)
{
// do something here
}
}
base.WndProc(ref m);
}
This code just checks what the form state is whenever a Resize event is fired.
Alternatively, you could probably just grab the form's Resize event and check the Window State from there. But I hear that it doesn't fire when a control (or Form?) is Maximized.
Hope this helps!
You can chage the window state of a form, from another thread using this. This works in .Net Framework 3.5
Invoke(new Action(() => { this.WindowState = FormWindowState.Normal; }));
I hope this might help you.
public partial class Form1 : Form {
private FormWindowState mLastState;
public Form1() {
InitializeComponent();
mLastState = this.WindowState;
}
protected override void OnClientSizeChanged(EventArgs e) {
if (this.WindowState != mLastState) {
mLastState = this.WindowState;
OnWindowStateChanged(e);
}
base.OnClientSizeChanged(e);
}
protected void OnWindowStateChanged(EventArgs e) {
// Do your stuff
}
}
I have a panel in my Form with a click event handler. I also have some other controls inside the panel (label, other panels, etc.). I want the click event to register if you click anywhere inside the panel. The click event works as long as I don't click on any of the controls inside the panel but I want to fire the event no matter where you click inside the panel. Is this possible without adding the same click event to all of the controls inside the panel?
Technically it is possible, although it is very ugly. You need to catch the message before it is sent to the control that was clicked. Which you can do with IMessageFilter, you can sniff an input message that was removed from the message queue before it is dispatched. Like this:
using System;
using System.Drawing;
using System.Windows.Forms;
class MyPanel : Panel, IMessageFilter {
public MyPanel() {
Application.AddMessageFilter(this);
}
protected override void Dispose(bool disposing) {
if (disposing) Application.RemoveMessageFilter(this);
base.Dispose(disposing);
}
public bool PreFilterMessage(ref Message m) {
if (m.HWnd == this.Handle) {
if (m.Msg == 0x201) { // Trap WM_LBUTTONDOWN
Point pos = new Point(m.LParam.ToInt32());
// Do something with this, return true if the control shouldn't see it
//...
// return true
}
}
return false;
}
}
I needed the exact same functionality today, so this is tested and works:
1: Create a subclasser which can snatch your mouseclick:
internal class MessageSnatcher : NativeWindow
{
public event EventHandler LeftMouseClickOccured = delegate{};
private const int WM_LBUTTONDOWN = 0x201;
private const int WM_PARENTNOTIFY = 0x210;
private readonly Control _control;
public MessageSnatcher(Control control)
{
if (control.Handle != IntPtr.Zero)
AssignHandle(control.Handle);
else
control.HandleCreated += OnHandleCreated;
control.HandleDestroyed += OnHandleDestroyed;
_control = control;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_PARENTNOTIFY)
{
if (m.WParam.ToInt64() == WM_LBUTTONDOWN)
LeftMouseClickOccured(this, EventArgs.Empty);
}
base.WndProc(ref m);
}
private void OnHandleCreated(object sender, EventArgs e)
{
AssignHandle(_control.Handle);
}
private void OnHandleDestroyed(object sender, EventArgs e)
{
ReleaseHandle();
}
}
2: Initialize the snatcher to hook into the panel WndProc:
private MessageSnatcher _snatcher;
public Form1()
{
InitializeComponent();
_snatcher = new MessageSnatcher(this.panel1);
}
3: The Message snatcher will get the WM_PARENTNOTIFY if you click on a child control.
You used to be able to override the OnBubbleEvent method on the control. In WPF the mechanism is called Routed events : http://weblogs.asp.net/vblasberg/archive/2010/03/30/wpf-routed-events-bubbling-several-layers-up.aspx
Bit late to the party, But What I did was to map all the click event of all the controls inside of the panel to panel click event. I know its nasty approach. but hey!!
a simple solution:
every control inside the usercontrol gets the same click-event "ControlClick". The usercontrol event click works with any controls inside.
private void ControlClick(Object sender, EventArgs e)
{
if (sender is UC_Vorgang uC_vorgang)
{
uC_vorgang.OnClick(e);
}
else
{
ControlClick(((Control)sender).Parent, e);
}
}
I have a listview that generates thumbnail using a backgroundworker. When the listview is being scrolled i want to pause the backgroundworker and get the current value of the scrolled area, when the user stopped scrolling the listview, resume the backgroundworker starting from the item according to the value of the scrolled area.
Is it possible to handle scroll event of a listview? if yes how? if not then what is a good alternative according to what i described above?
You'll have to add support to the ListView class so you can be notified about scroll events. Add a new class to your project and paste the code below. Compile. Drop the new listview control from the top of the toolbox onto your form. Implement a handler for the new Scroll event.
using System;
using System.Windows.Forms;
class MyListView : ListView {
public event ScrollEventHandler Scroll;
protected virtual void OnScroll(ScrollEventArgs e) {
ScrollEventHandler handler = this.Scroll;
if (handler != null) handler(this, e);
}
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
if (m.Msg == 0x115) { // Trap WM_VSCROLL
OnScroll(new ScrollEventArgs((ScrollEventType)(m.WParam.ToInt32() & 0xffff), 0));
}
}
}
Beware that the scroll position (ScrollEventArgs.NewValue) isn't meaningful, it depends on the number of items in the ListView. I forced it to 0. Following your requirements, you want to watch for the ScrollEventType.EndScroll notification to know when the user stopped scrolling. Anything else helps you detect that the user started scrolling. For example:
ScrollEventType mLastScroll = ScrollEventType.EndScroll;
private void myListView1_Scroll(object sender, ScrollEventArgs e) {
if (e.Type == ScrollEventType.EndScroll) scrollEnded();
else if (mLastScroll == ScrollEventType.EndScroll) scrollStarted();
mLastScroll = e.Type;
}
Based upon the post that #Adriaan Stander posted my class for raising scroll events is below.
internal class ControlScrollListener : NativeWindow, IDisposable
{
public event ControlScrolledEventHandler ControlScrolled;
public delegate void ControlScrolledEventHandler(object sender, EventArgs e);
private const uint WM_HSCROLL = 0x114;
private const uint WM_VSCROLL = 0x115;
private readonly Control _control;
public ControlScrollListener(Control control)
{
_control = control;
AssignHandle(control.Handle);
}
protected bool Disposed { get; set; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (Disposed) return;
if (disposing)
{
// Free other managed objects that implement IDisposable only
}
// release any unmanaged objects
// set the object references to null
ReleaseHandle();
Disposed = true;
}
protected override void WndProc(ref Message m)
{
HandleControlScrollMessages(m);
base.WndProc(ref m);
}
private void HandleControlScrollMessages(Message m)
{
if (m.Msg == WM_HSCROLL | m.Msg == WM_VSCROLL)
{
if (ControlScrolled != null)
{
ControlScrolled(_control, new EventArgs());
}
}
}
}
Use it like so...
Declare a field:
private ControlScrollListener _processListViewScrollListener;
Instantiate it with the controls which you need to know issrolling:
_processListViewScrollListener = new ControlScrollListener(ProcessesListView);
Wire in a handler:
_processListViewScrollListener.ControlScrolled += ProcessListViewScrollListener_ControlScrolled;
Handler the event:
void ProcessListViewScrollListener_ControlScrolled(object sender, EventArgs e)
{
// do what you need to do
}
The event args in the event raised could be tweaked to contain more useful information. I just needed to know my control had been scrolled!
See this post ListView Scroll Event
Use the native window class to listen
for the scroll messages on the
listbox. Will work with any control.
Catching the scroll event now is easily done in .net 4.
Catch the Loaded event from your ListView (m_ListView) and do this:
if (VisualTreeHelper.GetChildrenCount(m_ListView) != 0)
{
Decorator border = VisualTreeHelper.GetChild(m_ListView, 0) as Decorator;
ScrollViewer sv = border.Child as ScrollViewer;
sv.ScrollChanged += ScrollViewer_ScrollChanged;
}
then, implement your ScrollViewer_ScrollChanged function:
private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
...
}