Обратный вызов таймера закрывает приложение WPF (DispatcherTimer работает ..) - PullRequest
4 голосов
/ 03 февраля 2011

У меня есть приложение WPF с текстовым блоком, который отображает текущее время. Указанный текстовый блок привязан к DependencyProperty в ViewModel. Естественно, мне нужно постоянно обновлять время, поэтому я использовал таймер (System.Threading.Timer) примерно так:

public MainViewModel()
{
    _dateTimer = new Timer(_dateTimer_Tick, null, 0, 60000);
}

void _dateTimer_Tick(object sender)
{
    Time = DateTime.Now.ToString("HH:mm");
    Date = DateTime.Now.ToString("D");
}

Дело в том, что когда вызывается обратный вызов, приложение закрывается ... bummer (вывод говорит: «Первое случайное исключение типа« System.InvalidOperationException »произошло в WindowsBase.dll», когда он собирался записать в Время DP).

Если я использую DispatcherTimer, все работает нормально. Я не возражаю против использования DispatcherTimer, просто приложение довольно большое, и я хотел настроить его производительность как можно лучше. Насколько я вижу, я не обращаюсь к потоку пользовательского интерфейса (я просто обновляю свойство), поэтому DispatcherTimer не понадобится.

Я что-то упустил?

Спасибо.

EDIT: Свойства определены так:

    public string Time
    {
        get { return (string)GetValue(TimeProperty); }
        set { SetValue(TimeProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Time.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TimeProperty =
        DependencyProperty.Register("Time", typeof(string), typeof(MainViewModel), new UIPropertyMetadata(""));

(Дата такая же)

1 Ответ

2 голосов
/ 03 февраля 2011

Обратный вызов таймера выполняется в потоке, который не является потоком пользовательского интерфейса, это вызывает проблемы;причина, по которой это происходит, несмотря на то, что вы «только обновляете свойство», заключается в том, что такой вызов создает цепочку вызовов, а именно, чтобы уведомить заинтересованные стороны об изменениях свойства, которое в данном случае является пользовательским интерфейсом,и, следовательно, область действия всплывает, вызывая несоответствующие вызовы между потоками.

Чтобы преодолеть это, вы можете указать Dispatcher вашего Window в конструкторе таймера в качестве аргумента для параметра state, затем используйте Dispatcher.Invoke.

Например ...

public MainViewModel()
{
    _dateTimer = new Timer(_dateTimer_Tick, Dispatcher, 0, 60000);
}

void _dateTimer_Tick(object state)
{
    ((Dispatcher)state).Invoke(UpdateUI);
}

void UpdateUI()
{
    Time = DateTime.Now.ToString("HH:mm");
    Date = DateTime.Now.ToString("D");
}

РЕДАКТИРОВАТЬ:

Используя DispatcherTimer, как предложено Хенком, и даже рассмотренооднако, вы сами можете пойти сюда - я просто не знал о типе и поэтому не мог продемонстрировать в своем ответе.Что касается DispatcherTimer и производительности, на чем основано ваше беспокойство?

...