views:

567

answers:

5

Hi, I have a main wpf window and one of its controls is a user control that I have created. this user control is an analog clock and contains a thread that update hour, minute and second hands. Initially it wasn't a thread, it was a timer event that updated the hour, minutes and seconds but I have changed it to a thread because the application do some hard work when the user press a start button and then the clock don't update so I changed it to a thread.

COde snippet of wpf window:

     <Window
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
xmlns:local="clr-namespace:GParts"
       xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes
       assembly=PresentationFramework.Aero"            
       xmlns:UC="clr-namespace:GParts.UserControls"
       x:Class="GParts.WinMain"
       Title="GParts"    
       WindowState="Maximized"
       Closing="Window_Closing"    
       Icon="/Resources/Calendar-clock.png"
       x:Name="WMain"
     >
     <...>
          <!-- this is my user control -->
          <UC:AnalogClock Grid.Row="1" x:Name="AnalogClock" Background="Transparent"
           Margin="0" Height="Auto" Width="Auto"/>
     <...>
     </Window>

My problem is when the user exits the application then the thread seems to continue executing. I would like the thread finishes automatically when main windows closes.

code snippet of user control constructor:

namespace GParts.UserControls
{
/// <summary>
/// Lógica de interacción para AnalogClock.xaml
/// </summary>
public partial class AnalogClock : UserControl
{
    System.Timers.Timer timer = new System.Timers.Timer(1000);

    public AnalogClock()
    {
        InitializeComponent();

        MDCalendar mdCalendar = new MDCalendar();
        DateTime date = DateTime.Now;
        TimeZone time = TimeZone.CurrentTimeZone;
        TimeSpan difference = time.GetUtcOffset(date);
        uint currentTime = mdCalendar.Time() + (uint)difference.TotalSeconds;

        christianityCalendar.Content = mdCalendar.Date("d/e/Z", currentTime, false);

        // this was before implementing thread
        //timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
        //timer.Enabled = true;

        // The Work to perform 
        ThreadStart start = delegate()
        {

            // With this condition the thread exits when main window closes but
            // despite of this it seems like the thread continues executing after
            // exiting application because in task manager cpu is very  busy
            // 
            while ((this.IsInitialized) && 
                   (this.Dispatcher.HasShutdownFinished== false))
           {

            this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
            {
                DateTime hora = DateTime.Now;

                secondHand.Angle = hora.Second * 6;
                minuteHand.Angle = hora.Minute * 6;
                hourHand.Angle = (hora.Hour * 30) + (hora.Minute * 0.5);

                DigitalClock.CurrentTime = hora;
            }));
        }
            Console.Write("Quit ok");
        };

        // Create the thread and kick it started!
        new Thread(start).Start();
    }

    // this was before implementing thread
    void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {

        this.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(() =>
        {
            DateTime hora = DateTime.Now;

            secondHand.Angle = hora.Second * 6;
            minuteHand.Angle = hora.Minute * 6;
            hourHand.Angle = (hora.Hour * 30) + (hora.Minute * 0.5);

            DigitalClock.CurrentTime = hora;
        }));
    }
 } // end class
 } // end namespace

How can I exit correctly from thread automatically when main window closes and then application exits?

Thanks very much!

+1  A: 

Well, one major problem you have is you seem to be running an infinite loop that queues a lot of dispatcher jobs to update your clock, continuously and quickly. An easy fix might be to put a Thread.Sleep(1000); statement in the loop, then making your thread a background thread as Taylor suggests.

Anyway, I'm a little surprised that background work would cause a Timer to fail to update. Getting that approach working would be the ideal solution. Maybe try out DispatcherTimer and see if that can do updates while background work is going on.

RandomEngy
The dispatcher only runs when an event handler finishes. If he has a long computation in an event handler, the dispatcher won't be running to dispatch the timer events.
Gabe
Ok, thanks, but how to exit while loop when main application (window) is closed or when user exit application? my while loop is correct? i have test that when exiting application the thread finishes and reaches at line: console.write("Quit OK") But for some reason after exiting application I note that CPU usage is very high in task manager and it seems thread really hasn't finished executing. What is the correct condition in while loop in order to exit from it and from the thread when the user exit application (close main windows that contains the user control analogclock)?thanks.
toni
I think what's happening is all the dispatcher jobs you've been pouring on the the work queue are grinding out before it lets your app close. Putting a sleep statement for 1 second in your loop, then changing the thread to a background thread should work.
RandomEngy
gabe: The dispatcher jobs he's using are not long-running. The background thread will spew them out full speed and the UI thread will work on them almost full-time.
RandomEngy
A: 

May be use DependecyProperty in Your clock-model http://www.wpftutorial.net/HowToCreateADepProp.html DependecyProperty is faster than INotifyPropertyChanget interface, so maybe it's will good for you http://blog.lexique-du-net.com/index.php?post/2010/02/24/DependencyProperties-or-INotifyPropertyChanged

Victor
+1  A: 

Just set the IsBackground property of the Thread to true so it doesn't prevent the process from terminating.

Thread t = new Thread(...) { IsBackground = true };
Taylor Leese
Ok thanks, I'll try it. If I set IsBackground property to true and I do and infinite loop, for example while (true) { // do stuff }, when exit application the thread will terminate executing?
toni
Yes. .NET will kill the process when all non-background threads have exited. Background threads will get silently terminated. However, for my money, although setting the worker thread to background is the Right Thing, I still think it's only a partial solution: as RandomEngy points out, your worker thread is basically sitting on 100% CPU queuing up work for the UI thread, and if you address that design issue then your termination issue should go away into the bargain. (But setting IsBackground = true will still be worth doing.)
itowlson
OK,I'll try it.One question I would like that you suggest me the best and efficient way to do the task for updating clock.What I want is a efficient way to update clock and when main window (application exit) closes it stops.As you say a good way is doing thread background but is it correct to put all update operations inside a infinite while loop placed inside a thread?or is there a better way to do it?Is this thread correct:while (true) { this.Dispatcher.invoke(...); Thread.Sleep(1000);}
toni
A: 

Hey!

Finally I mixed two solutions, RandomEngy and Taylor but it wasn't working quit well (application wasn't exit successfully) so I decided to combine them with another one solution in the thread:

http://stackoverflow.com/questions/2523378/wpf-cancel-backgroundworker-on-application-exits

I main window application, from XAML I set the ShutdownMode property of my application to OnMainWindowClose as Thomas said in his comment.

Thanks!

A: 

I know you solved your problem, but as I was having a similar issue regarding a running thread and exiting the application correctly, I thought I'd share how I solved my problem.

I ended up skirting the dispatcher/thread model you used and instead opted to use the BackgroundWorker class (System.ComponentModel.BackgroundWorker). It was a simple 4 step process:

  1. create a private member to hold the BackgroundWorker
  2. assign the BackgroundWorker.DoWork event handler in the ctor
  3. define the DoWork event handler method
  4. call _myBackgroundWorker.RunWorkAsync() when I wanted to fire off the actual work.

There are details on using BackgroundWorker embedded in this article about using the Dispatcher in WPF.

Hope it helps someone...

mannish