Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF - removing a control that has a DispatcherTimer doesn't seem to go away

Tags:

wpf

I've seen the question asked on StackOverflow on how to properly remove controls in WPF. Generally there is some comment how you don't dispose them manually(or can't) and as long as you are not holding a reference to them they will be cleared eventually by the GC. I noticed quite by accident that one of my controls that I thought I removed was sticking around and still doing work even though I removed it from its parent.

I've recreated the example in as few lines as possible. The control has a DispatcherTimer. Here is the WPF code behind for the control I want to remove.

public partial class MyControl : UserControl
{
    private DispatcherTimer timer;
    public MyControl()
    {
        InitializeComponent();
        timer = new DispatcherTimer();
        timer.Tick += TimerOnTick;
        timer.Interval = TimeSpan.FromSeconds(1);
        timer.Start();
    }

    private void TimerOnTick(object sender, EventArgs args)
    {
        //this continues to get written out even after this control is removed.
        System.Diagnostics.Debug.WriteLine("Tick From MyControl.");
    }

}

Here is the code behind for a window that adds and removes my control.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void AddClicked(object sender, RoutedEventArgs e)
    {
        anyControlHolder.Children.Add(new MyControl());
    }

    private void RemoveClicked(object sender, RoutedEventArgs e)
    {
        anyControlHolder.Children.Clear();
    }
}

The problem I'm having when I run this code and then dynamically add and remove the custom usercontrol (MyControl) is that it's timer keeps ticking (in this example you can see it write out a message in the output window) and it keeps doing work in its tick event. What pattern should I use to at least cause the timer to stop ticking when the control is removed?

like image 371
DTig Avatar asked Feb 05 '11 03:02

DTig


1 Answers

You can hook into your control's Unloaded event and call timer.Stop() inside. I just tested this scenario, and the Unloaded event is raised when anyControlHolder.Children.Clear() was called, thus stopping the debug messages.

Code:

public partial class MyControl  : UserControl {
    private DispatcherTimer timer;

    public MyControl() {
        InitializeComponent();

        this.Loaded += new RoutedEventHandler(MyControl_Loaded);
        this.Unloaded += new RoutedEventHandler(MyControl_Unloaded);
    }

    void MyControl_Loaded(object sender, RoutedEventArgs e) {
        timer = new DispatcherTimer();
        timer.Tick += TimerOnTick;
        timer.Interval = TimeSpan.FromSeconds(1);
        timer.Start(); 
    }

    void MyControl_Unloaded(object sender, RoutedEventArgs e) {
        timer.Stop();
    }

    private void TimerOnTick(object sender, EventArgs args) {
        Debug.WriteLine("Tick From MyControl.");
    }
}

Hope this helps!

like image 63
Mark Carpenter Avatar answered Oct 04 '22 17:10

Mark Carpenter