Preface
I had originally posted this article when leppie and others pointed out that there are some potential issues with associating a separate instance as the event sink to the form. And to make matters worse, that instance references the form! What happens when the form is garbage collected? Does the window state monitor instance get garbage collected as well? Does the form itself get garbage collected, since the monitor has an instance of the form?
After investigating these issues, the short answer is yes. It would appear that the GC can reclaim both the form and the monitor instance even though the form references the monitor and the monitor references the form. If you think about it, you would expect this behavior (I think). In any case, I also modified the code slightly so that the monitor keeps a WeakReference
to the form. This should aid the garbage collection when the form is destroyed.
But the question still remained, why not code the handlers directly into the Form
derived class? And you could certainly do so! I just don't like the idea that for every class that needs this (I'm working with a lot of modeless forms), I would have to copy and paste that same code. And I don't want a common WindowStateMonitorForm
specialized from Form
from which all my forms derive. I just don't like the architecture. I like the idea that the window state monitor is a separate class that I can easily plug into an existing form, whether it's mine, someone else's, or not even specialized from Form
!
So, after reviewing the issues and making the WeakReference
change, I'm resubmitting the article.
Introduction
For a project I'm working on, I needed to detect when a form's WindowState
changed. Unfortunately, there is no event associated with this property, so I wrote a window state monitor.
How It Works
The WindowStateMonitor
class implements an event handler for the Layout
event. It then fires the appropriate event corresponding to the state change:
Minimized
Maximized
Normalized
It also fires a "Restored
" event if the window state is transitioning from minimized to either maximized or normalized.
The Implementation
Written For Declarative Programming
The implementation is very straightforward. I've written the class so that it is suitable for declarative programming. This means:
- it has a parameterless constructor
- it implements
Name
and Tag
properties (very useful for any class to have, actually)
- it implements
ISupportInitialize
, which auto-hooks the form when initialization of the instance completes.
Thus, declaratively, one could write (I've left off any xmlns prefixes):
<MyForm def:Name="MyForm">
<WindowFormMonitor Form="{myForm}" Minimized="OnMinimized"
Restored="OnRestored"/>
</Form>
The Code
Here's the actual implementation:
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace Marc.WindowStateMonitor
{
public class WindowStateMonitor : ISupportInitialize
{
protected string name;
protected object tag;
protected WeakReference form;
protected FormWindowState windowState;
public event EventHandler Minimized;
public event EventHandler Normalized;
public event EventHandler Maximized;
public event EventHandler Restored;
public Form Form
{
get {return (Form)form.Target;}
set {form=new WeakReference(value);}
}
public string Name
{
get {return name;}
set {name=value;}
}
public object Tag
{
get {return tag;}
set {tag=value;}
}
public WindowStateMonitor()
{
}
public WindowStateMonitor(Form form)
{
this.form=new WeakReference(form);
Hook();
}
public void BeginInit()
{
}
public void EndInit()
{
if (form != null)
{
Hook();
}
}
public void Hook()
{
windowState=((Form)form.Target).WindowState;
((Form)form.Target).Layout+=new LayoutEventHandler(OnLayout);
}
private void OnLayout(object sender, LayoutEventArgs e)
{
if (((Form)form.Target).WindowState != windowState)
{
switch(((Form)form.Target).WindowState)
{
case FormWindowState.Maximized:
if (Maximized != null)
{
Maximized(this, EventArgs.Empty);
}
if (windowState==FormWindowState.Minimized)
{
if (Restored != null)
{
Restored(this, EventArgs.Empty);
}
}
break;
case FormWindowState.Minimized:
if (Minimized != null)
{
Minimized(this, EventArgs.Empty);
}
break;
case FormWindowState.Normal:
if (Normalized != null)
{
Normalized(this, EventArgs.Empty);
}
if (windowState==FormWindowState.Minimized)
{
if (Restored != null)
{
Restored(this, EventArgs.Empty);
}
}
break;
}
windowState=((Form)form.Target).WindowState;
}
}
}
}
Conclusion
There're other ways to do this also--for example, CPian Stefan Troschütz suggested:
public class YourForm : Form
{
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0112)
if (((int)m.WParam & 0xFFF0) == 0xF020)
{
}
base.WndProc(ref m);
}
}
Enjoy!