I know that I can get the current state by WindowState, but I want to know if there's any event that will fire up when the user tries to minimize the form.
You can use the Resize event and check the Forms.WindowState Property in the event.
private void Form1_Resize ( object sender , EventArgs e )
{
if ( WindowState == FormWindowState.Minimized )
{
// Do some stuff
}
}
To get in before the form has been minimised you'll have to hook into the WndProc procedure:
private const int WM_SYSCOMMAND = 0x0112;
private const int SC_MINIMIZE = 0xF020;
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
protected override void WndProc(ref Message m)
{
switch(m.Msg)
{
case WM_SYSCOMMAND:
int command = m.WParam.ToInt32() & 0xfff0;
if (command == SC_MINIMIZE)
{
// Do your action
}
// If you don't want to do the default action then break
break;
}
base.WndProc(ref m);
}
To react after the form has been minimised hook into the Resize event as the other answers point out (included here for completeness):
private void Form1_Resize (object sender, EventArgs e)
{
if (WindowState == FormWindowState.Minimized)
{
// Do your action
}
}
I don't know of a specific event, but the Resize event fires when the form is minimized, you can check for FormWindowState.Minimized in that event
For people who search for WPF windows minimizing event :
It's a bit different. For the callback use WindowState :
private void Form1_Resize(object sender, EventArgs e)
{
if (WindowState == FormWindowState.Minimized)
{
// Do some stuff
}
}
The event to use is StateChanged (instead Resize):
public Main()
{
InitializeComponent();
this.StateChanged += Form1_Resize;
}
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
}
}
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 added a Windows Media control to my form and have been able to use it perfectly except when it is in the fullscreen state. It seems that I am unable to manipulate any aspects of the control through key events within my application. My current goal is to handle 'esc' key down to exit out of full screen. I can do more from there on.
If you have any idea please let me know!
Thanks, Kevin
I once found this code somewhere and worked pretty well, but don't remember where i got it from.
public partial class WMForm : Form,IMessageFilter
{
public WMForm()
{
InitializeComponent();
}
private void WMForm_Load(object sender, EventArgs e)
{
this.MyWindowsMediaPlayer.URL = #"YourFilePath/Url";
Application.AddMessageFilter(this);
}
private void WMForm_FormClosing(object sender, FormClosingEventArgs e)
{
Application.RemoveMessageFilter(this);
}
#region IMessageFilter
private const UInt32 WM_KEYDOWN = 0x0100;
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_KEYDOWN)
{
Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode;
if (keyCode == Keys.Escape)
{
this.MyWindowsMediaPlayer.fullScreen = false;
}
return true;
}
return false;
}
#endregion
}
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)
{
...
}