Dispose of a WPF UserControl (ish)

I've been playing with WPF a little bit, and quite frankly got a bit stuck, (Meh! Who'd have thought).
The problem is as follows:

I create a UserControl - we'll call it 'MyUserControl' (wouldn't want to break traditions now would we). In said user control I have a button, that when pressed will fire off a new thread. This new thread will (say) poll a file / service - whatever - every 10 seconds or so, and update a Label accordingly. When I close the app, if the app is polling, then the app won't actually die, basically because the thread that is doing the polling doesn't know to stop.

So, we have our polling code:

   1:    public void PollService()
   2:    {
   3:      while(Polling)
   4:      {
   5:        string result = Service.Poll();
   6:        UpdateLabel(_lblResults, result);
   7:      }
   8:    }

 
with UpdateLabel defined thusly:

   1:    private void delegate UpdateLabelDelegate(Label label, string text);
   2:    private void UpdateLabel(Label label, string text)
   3:    {
   4:      if(!Dispatcher.CheckAccess())
   5:      {
   6:        Dispatcher.Invoke(DispatcherPriority.Send, new UpdateLabelDelegate(UpdateLabel), label, text);
   7:        return;
   8:      }
   9:     
  10:      label.Content = text;
  11:    }


We also have a property: Polling, which is used to control wether the control is polling the service or not:

   1:    public bool Polling {
   2:      get { return _polling; }
   3:      set
   4:      {
   5:        _polling = value;
   6:        if(_polling)
   7:          new Thread(PollService).Start();
   8:      }
   9:    }


As you can see, when Polling = true; is called, a new Thread is spun off to do the polling. So, in theory (and in practice actually), if we have a Window with a 'MyUserControl' instance on it (called _myUC), we can call:

  _myUC.Polling = true;


to start polling, or

  _myUC.Polling = false;


to end polling.

Now, this is great, if I shut down the window when Polling == false, the app will shut down fine. If Polling == true though, well, that's a different kettle of fish altogether, the thread keeps running in the background leaving the app running (albeit in a hidden stylee). Now, normally, in a WinForms app, I would simply implement IDisposable and do something like:

   1:    public void Dispose()
   2:    {
   3:      Polling = false;
   4:    }


Then I'd know my control would bog off as I want it to.
"So implement already!" - I hear you say. Wish I could, but WPF doesn't use IDisposable, sooooo what to do?

Right, what's next on my hit list? Closed? Closing? OnClosing? OnClosed? Gah! None there!!
In fact none of the events on the control are of any use. The only one that sounded plausible was 'Unloaded' which (I incorrectly) presumed would be raised when the Window closed. No such luck!

Googling for terms such as: "WPF Dispose Control", "WPF Dispose UserControl", "Why the F**k can't I dispose", "WPF IDisposable" all come up with nothing of any use (aside from the fact that WPF doesn't support IDisposable). Erm, right, time to take the battle to the IDE...

 this.
 
   [ Scroll up ]
   [ Scroll down ]
   [ Hover tentatively over Dispatcher ]
  
hmmmm... well, why not...
 
this.Disp [CTRL+ENTER]
 System.Windows.Threading.Dispatcher
 
 Argh NO! Damn you intellisense!!!
 
  [ CTRL+Backspace ] x lots
 
 D i s p a t c h e r .
 
 Thats better...
 
   [ Scroll down ]
  
What's this??? 'ShutDownStarted' an event about shutting down? Ok, it's for the Dispatcher to shut down, but when the Window is closed, well, that's what is gonna happen... right? May as well try it, it's not gonna hurt after all... So, into the constructor we get:

   1:    public MyUserControl()
   2:    {
   3:      /* init code */
   4:      Dispatcher.ShutdownStarted += Dispatcher_ShutdownStarted;
   5:    }

 
which in turn (via the *wonders* of intellisense) generates:

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

Can it be? I put my faithful:
Polling = false;
code in, cross my fingers, and F5 that bad boy....

Compiled (good start)
Running (good going)
Start polling (Poll going)
Close app (.......)

App closed.
Holy fig rolls batman! The thread has been stopped! Setting a breakpoint on the Polling = false; line does indeed show it getting hit when the form is shut... So the next time the erroneous thread runs, it realises Polling does in fact == false, and stops.

Conclusions

To be honest, this is a bit, well, sh!t, and I'm convinced there are better ways of achieving this goal. However, having said that, I've had no joy in google or numerous forums. Hopefully someone can point out the errors of my ways (and yes, I imagine that seperating the polling logic into another non-wpf class would do it - but I wanted to see if I could do it this way!!).

Print | posted @ Monday, June 23, 2008 4:21 PM

Comments on this entry:

Gravatar # re: Dispose of a WPF UserControl (ish)
by Daniel Kemper at 1/19/2009 8:00 PM

Hi,

I was wanting to execute some code when my control was either disposed of or when the main application window was shutdown by the user. I was stifled at every turn until I saw this post. Thanks a lot for your attention to detail here!

Dan, MCP, MCTS
Gravatar # re: Dispose of a WPF UserControl (ish)
by Chris at 1/20/2009 8:25 AM

I'm glad it helped! Thank you for the comment!
Gravatar # re: Dispose of a WPF UserControl (ish)
by Faraz at 3/9/2009 6:58 PM

You are awesome man, god knows how much I looked for such thing. Thanks alot.
Gravatar # re: Dispose of a WPF UserControl (ish)
by Erich at 6/30/2009 2:56 AM

Great, thanks.
Gravatar # re: Dispose of a WPF UserControl (ish)
by Jouko at 7/2/2009 5:21 AM

Thanks a lot!

I had just the same problem and by reading almost one day many forums I found the same question many times but not a proper solution. Then I found this...
Gravatar # re: Dispose of a WPF UserControl (ish)
by Dan Puzey at 7/28/2009 5:47 AM

Hey,

The easier solution is to declare your thread differently. By default, a new Thread is created with IsBackground = false. That means that the thread will keep the application alive if it's still running.

If you create and start the thread like this instead then you'll hopefully find that it aborts when the app shuts down:

if(_polling)
{
Thread t = new Thread(PollService);
t.IsBackground = true;
t.Start();
}

Hope that's of use!

Dan.
Gravatar # re: Dispose of a WPF UserControl (ish)
by Chris at 7/28/2009 6:10 AM

Hi Dan,

That is of use! :)

I guess part of me still wants to have the ability to Dispose though. I'd like to end my thread when I want to, i.e. clear resources etc.

From what it looks like the Background thread is just terminated, and as a consequence, if I was in the middle of writing something to a file (for example) presumably there is a risk of invalid files etc...

For my example given though - IsBackground would have solved a lot of ache :)

Thanks!

Chris
Gravatar # re: Dispose of a WPF UserControl (ish)
by Xcalibur at 12/16/2009 11:07 PM

I suggest reading this page about element (including control) lifecycles

BTW the regex used to validate the Website on this site is buggered - i tried to put my website http://members.iinet.net.au/~lahg/procreate/ in and it didnt like it because of the ~ character.
Gravatar # re: Dispose of a WPF UserControl (ish)
by Toby at 3/31/2010 4:48 AM

You can use IDisposable with a WPF user control, you just need to implimented it yourself :

public partial class MyUserControl : UserControl, IDisposable
Gravatar # re: Dispose of a WPF UserControl (ish)
by DP at 11/5/2010 10:46 AM

I was working from home trying to minimize our new app's memory footprint when I ended up reading this post and started to laugh out loud. Have to tell you, it was so hard to explain my wife why i was laughing. Thanks for the post, it was nice to read something with a humor in it.
Gravatar # re: Dispose of a WPF UserControl (ish)
by Martin Prohn at 9/27/2013 12:02 AM

You can probably combine an background thread with a foreground thread like this.



1: public void PollService()
2: {
3: while(Polling)
4: {
5: var t = new Thread((ThreadStart)delegate {
6: string result = Service.Poll();
7: UpdateLabel(_lblResults, result);
8: });
9: t.Start();
10: t.Join();
11: }
12: }


BTW, this

3: set
4: {
5: _polling = value;
6: if(_polling)
7: new Thread(PollService).Start();
8: }

probably will cause problems if true is set again.

Post A Comment
Title:
Name:
Email:
Comment:
Verification: