.Dispatcher.Invoke
отправит вызов в поток пользовательского интерфейса.Вы не должны получать это исключение.Вместо этого вы должны опубликовать измененный код.
В любом случае, дизайн не очень хорош, поскольку добавляет жесткую зависимость между потоком мониторинга и пользовательским интерфейсом.Формы и модули не должны иметь прямых ссылок друг на друга, особенно в WPF.
WPF добавляет привязку данных, команды, сообщения, чтобы приложения не жестко кодировали ссылки между формами.То, что отображается в текстовом поле сегодня, может появиться в текстовом поле панели состояния на следующей неделе.Вам не нужно изменять бизнес или сервисные модули для такого незначительного изменения пользовательского интерфейса.
Среды MVVM добавляют явную поддержку приложений / бизнес-событий через сообщения или агрегаторы событий.Фактические имена зависят от инфраструктуры MVVM.
.NET Runtime itsel предоставляет интерфейс IProgress<T>
и класс Progress<T>
для публикации объектов прогресса между потоками.Класс Progress<T>
будет вызывать событие или вызывать обратный вызов в потоке, который он создавал, каждый раз, когда кто-то вызывает IProgress<T>
.Это означает, что вы можете просто обойти интерфейс и код мониторинга / рабочего не должен знать, как или что обрабатывает событие прогресса.
StartMonitoring
можно упростить до этого:
public void StartMonitoring(IProgress<string> progress)
{
var _schedule = DateTime.Now;
var _nextTaskSched = _schedule.AddSeconds(10);
var _timerTicks = (_nextTaskSched - DateTime.Now).TotalMilliseconds;
var taskTimer = new Timer(_timerTicks);
taskTimer.Elapsed += delegate
{
//call DeviceController here//
//do stuff//
//something went wrong//
progress.Report("Something went wrong";);
};
taskTimer.Start();
}
Или вы можете передать интерфейс в конструктор класса мониторинга
public class MyMonitor
{
IProgress<sring> _progress;
public MyMonitor(IProgress<string> progress,...)
{
....
_progress=progress;
}
public void StartMonitoring(IProgress<string> progress)
{
...
taskTimer.Elapsed += delegate
{
//call DeviceController here//
//do stuff//
//something went wrong//
_progress.Report("Something went wrong";);
};
taskTimer.Start();
}
}
Если метод создан вВ главном окне все, что вам нужно сделать, это заранее создать Progress<T>
и передать его методу:
public class MainWindow :...
{
Progress<string> _progress;
public MainWindow()
{
InitializeComponent();
_progress=new Progress<string>(OnProgress);
}
private void OnProgress(string message)
{
txtError.Text = message;
}
public void MethodThatStartsMonitoring()
{
//This could be passed in a constructor too.
myMonitor.StartMonitoring(_progress);
}
}
IProgress<T>
может принять любой объект, а не только строку.Это в сочетании с привязкой данных означает, что вы можете одновременно обновлять несколько элементов управления.
Вместо строки можно использовать класс Status
, например:
public class Status
{
public bool IsError{get;set;}
public string Message {get;set;}
public Status(bool isError,string message)
{
IsError=isError;
Message=message;
}
}
Этот класс можно использовать с IProgress<T>
:
public void StartMonitoring(IProgress<Status> progress)
{
...
taskTimer.Elapsed += delegate
{
progress.Report(new Status(false,"Starting"));
//call DeviceController here//
//do stuff//
//something went wrong//
progress.Report(new Status(true,"Something went wrong"));
};
...
}
И измените код основной формы следующим образом:
public class MainWindow:INotifyPropertyChanged,...
{
Progress<Status> _progress;
private Status _status=new Status();
public Status Status
{
get=>_status;
set
{
__status=value;
OnPropertyChanged("Status");
}
}
public MainWindow()
{
InitializeComponent();
_progress=new Progress<Status>(OnProgress);
this.DataContext=this;
}
private void OnProgress(Status status)
{
Status=status;
}
Теперь вы можете добавить привязки из нескольких элементов управления к свойству Status
, либо в XAML, либо в коде, например:
<TextBox x:Name="MyErrorBox" Text="{Binding Status.Message}"/>
Теперь обработчику прогресса и даже выделенному коду не нужно знать об элементах, которые будут отображать данные.
Вы можете также связать другие свойства, например, видимость:
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
</Window.Resources>
...
<TextBox x:Name="MyErrorBox"
Text="{Binding Status.Message}"
Visibility="{Binding Path=Status.IsError, Converter={StaticResource BoolToVisConverter} }" />
Текстовое поле теперь будет отображаться только для сообщений об ошибках