2015-05-13 1 views
4

У меня есть таймер в приложении форм Windows (Visual C#), что вызывает проблемы, когда я хочу выйти из приложения.Как предотвратить событие с истечением таймера, если приложение формы закрывается C#

Таймер определяется в качестве члена класса формы в:

partial class Form1 
    { 
     //These are the members in question: 
     internal ComACRServerLib.Channel channel; 
     private System.Timers.Timer updateStuff; 
    } 

Таймер объявляется/построено в конструкторе формы приложения:

public Form1() 
     { 
      InitializeComponent(); 
      updateStuff = new System.Timers.Timer(); 
      updateStuff.Elapsed += new System.Timers.ElapsedEventHandler(updateStuff_Elapsed); 
     } 

Таймер запускается и конфигурируется с нажатие одной кнопки:

private void btnAcquire_Click(object sender, EventArgs e) 
    { 
     updateStuff.Interval = 100; 
     updateStuff.Enabled = true; 
     updateStuff.AutoReset = true; 
     updateStuff.Start(); 
    } 

Когда таймер истекает, он вызывает updateStuff_Elapsed, который получает информацию для отображения с setText (есть код, чтобы убедиться, что вызов setText является потокобезопасным).

private void updateStuff_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
     { 
       if (!channel.isOffline) 
       { 
        object[] status = channel.GetACRCustom("P6144"); 
        setText(System.Convert.ToString(status[0])); 
       } 
     } 

public delegate void setTextDelegate(string text); 

public void setText(string text) 
     { 
      if (this.lblTest.InvokeRequired == true) 
      { 
        setTextDelegate d = new setTextDelegate(setText); 
        this.Invoke(d, new object[] { text }); 
      } 
      else 
      { 
        lblTest.Text = text; 
      } 
     } 

На выходе из приложения я пытаюсь избавиться от таймера и предотвратить его снова стреляя со следующим:

protected override void Dispose(bool disposing) 
     { 

      if (disposing && (components != null)) 
      { 
       updateStuff.AutoReset = false; 
       updateStuff.Stop(); 
       updateStuff.Close(); 
       updateStuff.Dispose(); 

       components.Dispose(); 
      } 
      base.Dispose(disposing); 
     } 

Но если таймер autorunning и я выйти из программы, я всегда получаю ошибки, которые подпрограмма, вызванная событием с истечением таймера updateStuff_elapsed, пытается использовать уже имеющиеся ресурсы! Несмотря на то, что я изо всех сил старался остановить и уничтожить таймер перед удалением.

Как остановить таймер от срабатывания при закрытии приложения?

EDIT

Я попытался переместить Dispose код вокруг, чтобы попытаться заставить таймер не закрывать, но не повезло. Я также попытался использовать updateStuff.Elapsed -= updateStuff_Elapsed, чтобы удалить вызов события перед остановкой и удалением;

protected override void Dispose(bool disposing) 
     { 
      //now this code HAS to run always. 
      updateStuff.Elapsed -= updateStuff_Elapsed; 
      updateStuff.AutoReset = false; 
      updateStuff.Stop(); 
      updateStuff.Close(); 
      updateStuff.Dispose(); 

      if (disposing && (components != null)) 
      {   
       components.Dispose(); 
      } 
      base.Dispose(disposing); 
     } 
+1

Попробуйте сначала удалить событие «updateStuff.Elapsed - = updateStuff_Elapsed;», затем выполните код удаления. Вероятно, это должно быть сделано в методе 'FormClosing', и просто вызовите' updateStuff.Dispose' в методе 'Dispose'. –

+0

Я попытался вызвать 'updateStuff.Elapsed - = updateStuff_Elapsed' перед остановкой, закрытием, отключением таймера, не повезло. У моего приложения форм нет метода FormClosing, является ли метод класса формы, из которого form1 является подклассом? – OrangeSherbet

+0

Вам нужно добавить переопределение. В вашем классе формы начните вводить 'override', и вы увидите' OnFormClosing' в списке методов, выберите его, чтобы создать переопределение и поместить туда код. В качестве альтернативы вы можете создать событие в конструкторе для события FormClosing, но вы должны переопределить его, а не использовать внутренние события, подобные этому. –

ответ

2

Как указано в документации для System.Timers.Timer, вызовы обработчика событий таймера помещаются в очередь ThreadPool. Поэтому вы должны предположить, что обработчик событий может быть вызван сразу несколько раз или может быть вызван после отключения таймера. Поэтому обработчик событий должен быть разработан для правильной обработки этих ситуаций.

Сначала укажите SynchronizingObject свойство таймера к экземпляру формы. Это вызовет все вызовы обработчика событий для потока пользовательского интерфейса, поэтому нам не нужно беспокоиться о блокировке полей формы (мы всегда будем обращаться ко всем из одного и того же потока пользовательского интерфейса). С этим набором свойств вам также не нужно вызывать this.Invoke (...) в методе setText.

public Form1() 
{ 
    updateStuff = new System.Timers.Timer(); 
    updateStuff.SynchronizingObject = this; 
    ... 
} 

public void setText(string text) 
{ 
    lblTest.Text = text; 
} 

Тогда создайте флаг, который позволит вам узнать, был ли установлен таймер или нет.Тогда просто установите этот флаг в обработчике события:

partial class Form1 
{ 
    private bool Disposed; 
    .... 
} 

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

    base.Dispose(disposing); 
} 

private void updateStuff_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
{ 
    if(!Disposed) 
    { 
      if (!channel.isOffline) 
      { 
       object[] status = channel.GetACRCustom("P6144"); 
       setText(System.Convert.ToString(status[0])); 
      } 
    } 
} 
+0

Очень элегантное решение, оно работает. Рад найти два решения одной и той же проблемы. – OrangeSherbet

0

Обработка события dispose происходит слишком поздно (что происходит во время сбора мусора). Вам необходимо выполнить процесс очистки в событии FormClosing.

+0

Хорошо, я попытаюсь выяснить, как выставить это событие в моем коде и остановить таймер из него. – OrangeSherbet

0

Чтобы расширить то, что сказал Рон и Марк, Решение было переопределить OnFormClosing метод класса Form, из которого Form1 является наследнике:

protected override void OnFormClosing(FormClosingEventArgs e) 
    { 
     //It appears it needs everything here to be turned off...amazing. 
     updateStuff.Elapsed -= updateStuff_Elapsed; 
     updateStuff.AutoReset = false; 
     updateStuff.Stop(); 
     updateStuff.Dispose(); 
     base.OnFormClosing(e); 
    } 

Другим решением является установка таймера на тот же поток, что и пользовательский интерфейс, как указано в Nuf.

+0

Утилизация таймера в методе OnFormClosing IMHO не решает проблему. В лучшем случае это делает проблему более маловероятной (или, другими словами, она увеличивает вероятность того, что все вызовы обработчиков вызовов в очереди будут закончены до того, как будет настроена форма), но это не гарантирует правильную функциональность. –