Эквивалентный из PostMessage в C# для синхронизации с основным потоком с MVVM?

Я должен быть задержан с поиском, потому что вот другая на вид типичная проблема, которую я не смог решить.

Вот моя проблема - я использую WPF и MVVM, и у меня есть statemachine, который выполняется в модели. Если ошибка происходит, я должен передать информацию до ViewModel для отображения ошибки. Эта часть, кажется, работает хорошо. Когда пользователь нажимает желаемое поведение, код в модели продолжается и смотрит на объект, с которым взаимодействует пользователь определить, что сделать затем.

Проблема состоит в том, что модель должна перезагрузить файл, который обновляет GUI с содержанием упомянутого файла. Поскольку модель выполняется в потоке, можно ли вообразить то, что я собираюсь спросить затем - как, черт возьми, Вы синхронизируетесь с GUI правильно? В MFC я использовал бы или SendMessage или PostMessage для выполнения обновления GUI.

Я прочитал статьи для WinForms, которые предлагают использовать InvokeRequired для автоматического вызова BeginInvoke при необходимости. Я на самом деле не знал, что BeginInvoke выполнит то, что я хотел, таким образом, я был поощрен изучить это.

Как я на самом деле называю BeginInvoke из своей модели? Этот метод даже относится к WPF? Я шел вперед и реализовал делегата и затем звонил, Вызывают, но я получаю ту же ошибку, которая говорит мне, что набор не может быть изменен от этого потока. Я также попробовал BeginInvoke за ад его, но я предполагаю, что также не работал бы, потому что он просто запустится от другого потока так или иначе.

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

РЕДАКТИРОВАНИЕ - я должен, вероятно, также добавить, что ищу что-то другое, чем таймер или находящееся в BackgroundWorker решение, если это не единственный способ решить это в WPF / MVVM. Кроме того, интересно, имел ли какой-либо из инструментариев MVVM уже средства для этого вида вещи...

7
задан Dave 9 March 2010 в 17:34
поделиться

3 ответа

Если вы хотите запланировать некоторую работу из фонового потока в поток пользовательского интерфейса в WPF, используйте DispatcherObject . Вот хорошая статья о том, как создавать более отзывчивые приложения с помощью Dispatcher .

Обновление: Обратите внимание, что если вы используете событие для отправки уведомлений из Модели в ViewModel, вам все равно нужно где-то переключиться на поток пользовательского интерфейса. Должен ли этот переключатель быть в модели или в модели представления - это хорошее обсуждение дизайна, но оно ортогонально вашему вопросу.

Событие будет инициировано в соответствующем потоке диспетчера. Поскольку вам нужно добраться до потока пользовательского интерфейса, вам необходимо использовать диспетчер, который создается в потоке пользовательского интерфейса. Самый простой способ получить его - использовать свойство DispatcherObject.Dispatcher в одном из элементов пользовательского интерфейса. Альтернативный вариант - создать его в вашей модели или ViewModel. Если вы сторонник дизайна, я бы посоветовал вам создать диспетчер в своей модели и отправить вызов обратно в поток пользовательского интерфейса, прежде чем вызывать событие, которое прослушивает ViewModel. Таким образом, все переключение потоков и управление ими содержатся в вашей модели, а ViewModel работает как однопоточная только в потоке пользовательского интерфейса.

8
ответ дан 6 December 2019 в 19:36
поделиться

Я думаю, что ваша 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
}
4
ответ дан 6 December 2019 в 19:36
поделиться

У меня есть другой подход, который, кажется, работает, и я просто хотел выкинуть его, чтобы получить несколько комментариев (если кто-то еще читает этот вопрос!).

Я начал использовать класс Messenger MVVM Light Toolkit, и, похоже, он мне очень хорошо подходит. Например, возьмем для примера ProgressBar. Я зарегистрировал два сообщения с моей ViewModel для установки значения прогресса и максимального прогресса. Затем в моей модели, когда она устанавливает задачи и общий процесс, она отправляет эти сообщения. Когда виртуальная машина получает сообщения, она просто обновляет значения привязки данных, и мой графический интерфейс обновляется автоматически! Это очень просто, но мне было интересно, что вы все думаете об этом подходе. Кто-нибудь еще делает это без происшествий?

1
ответ дан 6 December 2019 в 19:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: