Click here to Skip to main content
15,883,901 members
Articles / Programming Languages / C#
Article

Window State Monitor

Rate me:
Please Sign up or sign in to vote.
4.11/5 (10 votes)
8 Mar 20052 min read 63.6K   750   29   8
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 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):

XML
<MyForm def:Name="MyForm">
   <WindowFormMonitor Form="{myForm}" Minimized="OnMinimized"
                                      Restored="OnRestored"/>
</Form>

The Code

Here's the actual implementation:

C#
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:

C#
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!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect Interacx
United States United States
Blog: https://marcclifton.wordpress.com/
Home Page: http://www.marcclifton.com
Research: http://www.higherorderprogramming.com/
GitHub: https://github.com/cliftonm

All my life I have been passionate about architecture / software design, as this is the cornerstone to a maintainable and extensible application. As such, I have enjoyed exploring some crazy ideas and discovering that they are not so crazy after all. I also love writing about my ideas and seeing the community response. As a consultant, I've enjoyed working in a wide range of industries such as aerospace, boatyard management, remote sensing, emergency services / data management, and casino operations. I've done a variety of pro-bono work non-profit organizations related to nature conservancy, drug recovery and women's health.

Comments and Discussions

 
NewsA bug on the restore window! Pin
Spacix One4-Sep-07 8:43
Spacix One4-Sep-07 8:43 
Generalsimpler way Pin
Giorgi Dalakishvili21-Apr-07 4:38
mentorGiorgi Dalakishvili21-Apr-07 4:38 
GeneralSmall mod ... Pin
Onder2320-Jan-07 11:39
Onder2320-Jan-07 11:39 
GeneralGreat, Just what I was looking for. :) [modified] Pin
Jab19574-Aug-06 14:54
Jab19574-Aug-06 14:54 
GeneralInvoking Events Pin
Mark Belles8-Mar-05 19:32
Mark Belles8-Mar-05 19:32 
GeneralRe: Invoking Events Pin
Marc Clifton9-Mar-05 8:28
mvaMarc Clifton9-Mar-05 8:28 
Generalteeny typo Pin
leppie8-Mar-05 18:41
leppie8-Mar-05 18:41 
GeneralRe: teeny typo Pin
Marc Clifton9-Mar-05 8:26
mvaMarc Clifton9-Mar-05 8:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.