Я должен быть задержан с поиском, потому что вот другая на вид типичная проблема, которую я не смог решить.
Вот моя проблема - я использую WPF и MVVM, и у меня есть statemachine, который выполняется в модели. Если ошибка происходит, я должен передать информацию до ViewModel для отображения ошибки. Эта часть, кажется, работает хорошо. Когда пользователь нажимает желаемое поведение, код в модели продолжается и смотрит на объект, с которым взаимодействует пользователь определить, что сделать затем.
Проблема состоит в том, что модель должна перезагрузить файл, который обновляет GUI с содержанием упомянутого файла. Поскольку модель выполняется в потоке, можно ли вообразить то, что я собираюсь спросить затем - как, черт возьми, Вы синхронизируетесь с GUI правильно? В MFC я использовал бы или SendMessage или PostMessage для выполнения обновления GUI.
Я прочитал статьи для WinForms, которые предлагают использовать InvokeRequired для автоматического вызова BeginInvoke при необходимости. Я на самом деле не знал, что BeginInvoke выполнит то, что я хотел, таким образом, я был поощрен изучить это.
Как я на самом деле называю BeginInvoke из своей модели? Этот метод даже относится к WPF? Я шел вперед и реализовал делегата и затем звонил, Вызывают, но я получаю ту же ошибку, которая говорит мне, что набор не может быть изменен от этого потока. Я также попробовал BeginInvoke за ад его, но я предполагаю, что также не работал бы, потому что он просто запустится от другого потока так или иначе.
Запутанный. Если я пропустил что-то действительно очевидное, это было отправлено о на всем протяжении Интернета, разрешения и дает мне словесный упрек, я, вероятно, заслуживаю его.
РЕДАКТИРОВАНИЕ - я должен, вероятно, также добавить, что ищу что-то другое, чем таймер или находящееся в BackgroundWorker решение, если это не единственный способ решить это в WPF / MVVM. Кроме того, интересно, имел ли какой-либо из инструментариев MVVM уже средства для этого вида вещи...
Если вы хотите запланировать некоторую работу из фонового потока в поток пользовательского интерфейса в WPF, используйте DispatcherObject
. Вот хорошая статья о том, как создавать более отзывчивые приложения с помощью Dispatcher .
Обновление: Обратите внимание, что если вы используете событие для отправки уведомлений из Модели в ViewModel, вам все равно нужно где-то переключиться на поток пользовательского интерфейса. Должен ли этот переключатель быть в модели или в модели представления - это хорошее обсуждение дизайна, но оно ортогонально вашему вопросу.
Событие будет инициировано в соответствующем потоке диспетчера. Поскольку вам нужно добраться до потока пользовательского интерфейса, вам необходимо использовать диспетчер, который создается в потоке пользовательского интерфейса. Самый простой способ получить его - использовать свойство DispatcherObject.Dispatcher
в одном из элементов пользовательского интерфейса. Альтернативный вариант - создать его в вашей модели или ViewModel. Если вы сторонник дизайна, я бы посоветовал вам создать диспетчер в своей модели и отправить вызов обратно в поток пользовательского интерфейса, прежде чем вызывать событие, которое прослушивает ViewModel. Таким образом, все переключение потоков и управление ими содержатся в вашей модели, а ViewModel работает как однопоточная только в потоке пользовательского интерфейса.
Я думаю, что ваша ViewModel действительно ничего не должна знать о View, в том числе о том, является ли он пользовательским интерфейсом WPF или есть ли в этом пользовательском интерфейсе понятие потока диспетчера, поэтому красный флаг должен слететь как можно скорее. когда вы начинаете писать код в вашей ViewModel, который пытается выполнить CheckAccess () или InvokeRequired, чтобы маршалировать некоторый код в поток пользовательского интерфейса. Вместо этого я бы попросил модель генерировать событие, которое View может прослушивать и обновлять соответствующим образом, или чтобы ViewModel предоставлял свойство (например, bool FileIsLoading), к которому View просто привязывается, чтобы определить и отобразить, что это за модель. выполняется асинхронно, и ViewModel несет ответственность за точность значения этого свойства.
Например:
public partial class MainWindow : Window {
private ViewModel _model = new ViewModel();
public MainWindow() {
InitializeComponent();
DataContext = _model;
}
private void Button_Click(object sender, RoutedEventArgs e) {
_model.Run();
}
}
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Click="Button_Click"
Content="Run"
IsEnabled="{Binding IsIdle}" />
</Grid>
</Window>
public class ViewModel : INotifyPropertyChanged {
private bool _isIdle = true;
public bool IsIdle {
get { return _isIdle; }
set {
_isIdle = value;
OnPropertyChanged("IsIdle");
}
}
public void Run() {
ThreadPool.QueueUserWorkItem((state) => {
IsIdle = false;
Thread.Sleep(10000);
IsIdle = true;
});
}
#region INotifyPropertyChanged Implementation
protected void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if (propertyChanged != null) {
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
У меня есть другой подход, который, кажется, работает, и я просто хотел выкинуть его, чтобы получить несколько комментариев (если кто-то еще читает этот вопрос!).
Я начал использовать класс Messenger MVVM Light Toolkit, и, похоже, он мне очень хорошо подходит. Например, возьмем для примера ProgressBar. Я зарегистрировал два сообщения с моей ViewModel для установки значения прогресса и максимального прогресса. Затем в моей модели, когда она устанавливает задачи и общий процесс, она отправляет эти сообщения. Когда виртуальная машина получает сообщения, она просто обновляет значения привязки данных, и мой графический интерфейс обновляется автоматически! Это очень просто, но мне было интересно, что вы все думаете об этом подходе. Кто-нибудь еще делает это без происшествий?