Click here to Skip to main content
Click here to Skip to main content
Go to top

Window State Monitor

, 8 Mar 2005
Rate this:
Please Sign up or sign in to vote.
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):

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

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

Share

About the Author

Marc Clifton

United States United States
Marc is the creator of two open source projets, MyXaml, a declarative (XML) instantiation engine and the Advanced Unit Testing framework, and Interacx, a commercial n-tier RAD application suite.  Visit his website, www.marcclifton.com, where you will find many of his articles and his blog.
 
Marc lives in Philmont, NY.

Comments and Discussions

 
Generalteeny typo Pinmemberleppie8-Mar-05 18:41 
GeneralRe: teeny typo PinprotectorMarc Clifton9-Mar-05 8:26 

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

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

| Advertise | Privacy | Mobile
Web04 | 2.8.140921.1 | Last Updated 8 Mar 2005
Article Copyright 2005 by Marc Clifton
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid