Click here to Skip to main content
Click here to Skip to main content

A behavior study of Windows form events

, 31 May 2008
Rate this:
Please Sign up or sign in to vote.
How much can we rely on WinForm events to fire in a particular order when loading.

At96.png

Introduction

In my previous article (about the Game of Life), I made a common mistake of expecting an object to be instantiated by the time the code reached the form re-size event. This worked fine during my tests, but as you will read in the comments below the article, chengkc found the null reference exception. The problem went away when chengkc changed the DPI setting back to the default of 96 DPI (was 120 DPI). This article tries to shed light on this phenomenon.

About DPI

You can reach this setting via:

  1. Display Properties,
  2. Settings tab,
  3. Advanced button,
  4. General tab.

AdvancedDisplayProperties.png

This property page allows the user to increase the size of the desktop and task-bar icons by changing the DPI from 96 to 120. There is no apparent reason why this setting will affect the way WinForms load.

The test plan

We want to detect the sequence of events that are raised during a form start-up. By looking at the generated list of events and comparing the list for each circumstance, we should be able to gather important information. This information can then be used to understand why we need to be careful about our assumptions regarding form events.

The code

Each event handler will have to add a string to a list.

//..
        private List<string> _Events = new List<string>();

        protected void Form1_Activated(object sender, EventArgs e)
        {
            _Events.Add("Form1_Activated");
        }

        protected void Form1_Deactivate(object sender, EventArgs e)
        {
            _Events.Add("Form1_Deactivate");
        }

        protected void Form1_Enter(object sender, EventArgs e)
        {
            _Events.Add("Form1_Enter");
        }
//..

And the easiest way to hookup these events is by using the form designer.

        #region Windows Form Designer generated code
//..
        private void InitializeComponent()
        {
//..
            this.SuspendLayout();
//..
            this.Deactivate += new System.EventHandler(this.Form1_Deactivate);
            this.Activated += new System.EventHandler(this.Form1_Activated);
            this.Enter += new System.EventHandler(this.Form1_Enter);
//..
            this.ResumeLayout(false);
            this.PerformLayout();
                }

However, during the tests with the first version of this application, it was noted that some events do not fire as expected. It appears the designer does not fire all relevant events; probably, some events are suppressed by the "SuspendLayout" method.

To ensure we analyze this properly, the event hookup code was tested using two additional scenarios. The three scenarios are as follows:

  • Allow the designer to hookup these events.
  • Hookup all relevant events before the call to "InitializeComponent".
  • Hookup all relevant events after the call to "InitializeComponent".

This expanded our test plan to a minimum of six sets as test results (2 DPI states x 3 scenarios). To allow the reader to repeat the test cases on other configurations, the code was re-factored using inheritance. The CaptureForm is our base class.

    public class CaptureForm : Form
    {
        private List<string> _Events = new List<string>();

        protected void Form1_Activated(object sender, EventArgs e)
        {
            _Events.Add("Form1_Activated");
        }

        protected void Form1_Deactivate(object sender, EventArgs e)
        {
            _Events.Add("Form1_Deactivate");
        }

        protected void Form1_Enter(object sender, EventArgs e)
        {
            _Events.Add("Form1_Enter");
        }

//..

        public string GetRecordedEvents()
        {
            StringBuilder sb = new StringBuilder();
            foreach (string eventText in _Events.ToArray())
            {
                sb.Append(eventText);
                sb.Append("\r\n");
            }
            return sb.ToString();
        }
    }

The forms that handle our three scenarios are, firstly, FormBefore:

    public partial class FormBefore : CaptureForm
    {
        public FormBefore()
        {
            InitEventHandlers();
            InitializeComponent();
        }

        private void InitEventHandlers()
        {
            this.SuspendLayout();
            this.StyleChanged += new System.EventHandler(this.Form1_StyleChanged);
            this.Deactivate += new System.EventHandler(this.Form1_Deactivate);
            this.Load += new System.EventHandler(this.Form1_Load);
//..
            this.ResumeLayout(false);
        }

    }

and then, FormAfter:

    public partial class FormAfter : CaptureForm
    {
        public FormAfter()
        {
            InitializeComponent();
            InitEventHandlers();
        }

        private void InitEventHandlers()
        {
            this.SuspendLayout();
            this.StyleChanged += new System.EventHandler(this.Form1_StyleChanged);
            this.Deactivate += new System.EventHandler(this.Form1_Deactivate);
            this.Load += new System.EventHandler(this.Form1_Load);
//..
            this.ResumeLayout(false);
        }

    }

and lastly, FormDesigner:

    public partial class FormDesigner : CaptureForm
    {
        public FormDesigner()
        {
            InitializeComponent();
        }
    }

where FormDesigner has the event hookups in the form designer code.

To drive the test, we create a main form called CaptureForm with buttons, timers, and text boxes. On request, this main form will loop through each scenario and record the events. The list of events will appear side by side to facilitate easy comparison.

At the default 96 DPI, we get:

At96.png

At 120 DPI, we get:

At120.png

One other thing to mention is that in case a particular test produces a different result in a subsequent pass, we will record those events as well. However, the results seen above do not show more than our expected six sets.

Analysis

To analyze our results, I used a well known file compare tool.

Comparing 96 vs. 120 DPI using designer hookup:

InDesigner.png

Comparing 96 vs. 120 DPI using custom hookup before the designer code:

BeforeDesigner.png

Comparing 96 vs. 120 DPI using custom hookup after the designer code:

AfterDesigner.png

From these comparisons, we see that for 120 DPI, extra events are raised. More specifically, the re-size event occurs before any other events. If a programmer would have code in the re-size event handler that depended on a state set in one of the other events, an exception or logical bug is effected. While developing and unit testing using 96 DPI, no apparent problems occur. Once deployed, some users will experience problems due to different configurations.

Conclusion

The first point to make is, the assumptions we make about the sequence of form events. This article may help to make the reader more careful about events. Using conditional statements or late referenced properties may alleviate the problem, but this article is not a focus on better design patterns.

The second and final point to make is that the test cases were limited to one changeable aspect of computer configuration (DPI). Using the application, I leave you with a test platform to scrutinize other configuration settings that may affect form events. Please post your findings below.

History

  • Initial version dated 31 May 2008.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Johan Fourie
Architect
Australia Australia
No Biography provided

Comments and Discussions

 
GeneralNice to have stuff documented Pinmembersupercat931-May-08 8:02 
GeneralNice Pinmemberdavepermen31-May-08 3:30 

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
Web03 | 2.8.140709.1 | Last Updated 31 May 2008
Article Copyright 2008 by Johan Fourie
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid