blearyeye
blearyeye

Reputation: 265

Winform doesn't fully dispose

I have a form Menu that won't fully dispose. Below is the complete form code. It's part of a larger system so other forms open and close before Menu is first opened.

There's a form timer that fires every second and prints whether the form is disposed or not. There's a button that opens another form, Search, and closes Menu. Search also has a timer that prints whether it is disposed or not.

When Menu opens, the debug output is as expected

*********** (in main menu): Disposed False
*********** (in main menu): Disposed False

When I click, I get timer ticks for both Menu and Search

*********** (in main): Disposed True
*************** (in search) Disposed False

It shows that the first instance of Menu is disposed but obviously the timer is still running. When I exit Search and Main is opened, there are now two Main timers running

*********** (in main): Disposed True
*********** (in main): Disposed False

I can keep doing this (click to open Search and exit) and the number of Main timers running keeps increasing. I'm perplexed. Here's the code for Main

using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Diagnostics;

namespace Gui
{
public partial class Menu : Form
{
    private System.Windows.Forms.Timer timer1;
    private Button button1;
    private IContainer components;

    public Menu()
    {
        InitializeComponent();
    }

    private void Menu_Load(object sender, EventArgs e)
    {
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        Debug.Print("*********** (in main): Disposed {0}", IsDisposed);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var search = new Search();
        search.Show();
        Close();
    }
    private void InitializeComponent()
    {
        this.components = new System.ComponentModel.Container();
        this.timer1 = new System.Windows.Forms.Timer(this.components);
        this.button1 = new System.Windows.Forms.Button();
        this.SuspendLayout();
        // 
        // timer1
        // 
        this.timer1.Enabled = true;
        this.timer1.Interval = 1000;
        this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
        // 
        // button1
        // 
        this.button1.Location = new System.Drawing.Point(11, 17);
        this.button1.Name = "button1";
        this.button1.Size = new System.Drawing.Size(125, 32);
        this.button1.TabIndex = 0;
        this.button1.Text = "button1";
        this.button1.UseVisualStyleBackColor = true;
        this.button1.Click += new System.EventHandler(this.button1_Click);
        // 
        // Menu
        // 
        this.ClientSize = new System.Drawing.Size(282, 253);
        this.Controls.Add(this.button1);
        this.Name = "Menu";
        this.Load += new System.EventHandler(this.Menu_Load);
        this.ResumeLayout(false);
    }
}
}

Upvotes: 1

Views: 994

Answers (1)

Hans Passant
Hans Passant

Reputation: 941218

    this.timer1 = new System.Windows.Forms.Timer(this.components);

It looks like you copy/pasted the content of a Designer.cs file of a form class. The InitializeComponent() method is certainly boilerplate. But you didn't do it right, you forgot to actually use the this.components member. Which exists for only one reason, disposing any components that the form class uses. Like timer1. It is automatic for any controls you drop on the form, they can be found back through the form's Controls member but components need extra help.

So don't just copy/paste InitializeComponent(), you must copy/paste the Dispose() method as well:

    protected override void Dispose(bool disposing) {
        if (disposing && (components != null)) {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

And the timer now stops ticking when you close the form.

Upvotes: 3

Related Questions