Window State Monitor






4.11/5 (10 votes)
Mar 8, 2005
2 min read

64545

752
A window state monitor.
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
andTag
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;
/// <summary>
/// Triggered when the form is minimized.
/// </summary>
public event EventHandler Minimized;
/// <summary>
/// Triggered when the form in normalized.
/// </summary>
public event EventHandler Normalized;
/// <summary>
/// Triggered when the form is Maximized.
/// </summary>
public event EventHandler Maximized;
/// <summary>
/// Triggered when the form is restored from a minimized state.
/// </summary>
public event EventHandler Restored;
/// <summary>
/// Get/set the Form to monitor.
/// </summary>
public Form Form
{
get {return (Form)form.Target;}
set {form=new WeakReference(value);}
}
/// <summary>
/// Get/set the name of this instance.
/// Useful with declarative programming.
/// </summary>
public string Name
{
get {return name;}
set {name=value;}
}
/// <summary>
/// Get/set a tag associated with this instance.
/// Useful with declarative programming.
/// </summary>
public object Tag
{
get {return tag;}
set {tag=value;}
}
/// <summary>
/// Parameterless constructor.
/// Required for declarative programming.
/// </summary>
public WindowStateMonitor()
{
}
/// <summary>
/// Constructor. This constructor automatically begins monitoring the
/// specified form.
/// </summary>
/// <param name="form">The form to monitor.</param>
public WindowStateMonitor(Form form)
{
this.form=new WeakReference(form);
Hook();
}
/// <summary>
/// Does nothing.
/// </summary>
public void BeginInit()
{
}
/// <summary>
/// If a form has been assigned, automatically begins monitor the form.
/// Supports declarative construction.
/// </summary>
public void EndInit()
{
if (form != null)
{
Hook();
}
}
/// <summary>
/// Begins monitoring the form for min/max/norm/restore events.
/// </summary>
public void Hook()
{
windowState=((Form)form.Target).WindowState;
((Form)form.Target).Layout+=new LayoutEventHandler(OnLayout);
}
/// <summary>
/// Monitors layout changes.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnLayout(object sender, LayoutEventArgs e)
{
// If the state has changed...
if (((Form)form.Target).WindowState != windowState)
{
// ...what is the new state?
switch(((Form)form.Target).WindowState)
{
case FormWindowState.Maximized:
// Trigger Maximized, if it exists.
if (Maximized != null)
{
Maximized(this, EventArgs.Empty);
}
// Trigger Restored if coming from a minimized state.
if (windowState==FormWindowState.Minimized)
{
if (Restored != null)
{
Restored(this, EventArgs.Empty);
}
}
break;
case FormWindowState.Minimized:
// Trigger Minimized, if it exists.
if (Minimized != null)
{
Minimized(this, EventArgs.Empty);
}
break;
case FormWindowState.Normal:
// Trigger Normalized, if it exists.
if (Normalized != null)
{
Normalized(this, EventArgs.Empty);
}
// Trigger Restored if coming from a minimized state.
if (windowState==FormWindowState.Minimized)
{
if (Restored != null)
{
Restored(this, EventArgs.Empty);
}
}
break;
}
// Save new state.
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) // Test for WM_SYSCOMMAND
if (((int)m.WParam & 0xFFF0) == 0xF020) // Test for SC_MINIMIZE
{ // Form is being minimized.
// Here goes your code.
}
base.WndProc(ref m);
}
}
Enjoy!