Неправильно ли использовать Диспетчер в моей модели ViewModel?
Я конвертирую чат-парсер для игры, в которую играю, я писал в c # winforms для wpf, главным образом, чтобы получить лучший дескриптор MVVM и wpf. Ниже приведен пример того, как мой проект настроен
View : На данный момент это просто простой ListBox с ItemSource, привязанный к моим наблюдаемым моделям наблюдаемой чатовой коллекции
Модель : у меня есть несколько символов, которые могут быть зарегистрированы в одно время, и каждый символ имеет class чата. В чат-classе начинается фоновый рабочий, который захватывает и следующую строку чата из игры и запускает событие под названием IncomingChat с этой строкой.
- Конфликты Datacontext
- Отображение видимости столбцов Bind datagrid MVVM
- Как обнаружить сломанные привязки данных WPF?
- Как периодически выполнять метод из клиентского приложения WPF с помощью streamовой передачи или таймера
- Как отключить эффекты MouseOver для кнопки в WPF?
public event Action IncomingChat;
Я использую фона рабочего, чтобы запустить событие в моем backgroundworkers progresschaged событие, потому что, когда я использовал таймер, я продолжал получать проблемы с streamами. Сначала я исправил это, изменив мой таймер на DispatchTimer, но мне не показалось, что у меня есть DispatchTimer в моей модели.
ViewModel : Поскольку у меня есть несколько символов, я создаю несколько ChatViewModels. Я передаю персонажа в конструктор ChatViewModels и подписываюсь на событие чата. Я создаю ObservableColleciton, чтобы держать мои строки чата, когда это событие получено. Теперь я получаю проблему с streamами на моем модуле viewModel, когда пытаюсь добавить строку, которую я получаю от своего события чата, к моему наблюдаемому коллекциям.
Я обошел это, сделав так, чтобы мои viewmodels входящего обработчика событий в чате выглядели так
public ObservableCollection<Game.ChatLine) Chat {get; private set;} void Chat_Incoming(Game.ChatLine line) { App.Current.Dispatcher.Invoke(new Action(delegate { Chat.Add(line) }), null); }
Однако это мне не нравится. Хотя это работает, использование Диспетчера в моей модели просмотра, как это кажется мне неуместным.
- Как можно добавить разделитель между элементами в ItemsControl
- Как я могу создать границу и строку заголовка windows в WPF?
- Выберите несколько элементов из DataGrid в проекте MVVM MVPM
- Изменение размера windows WPF и содержимого, зависящего от разрешения экрана
- Передача значения enums в качестве параметра команды из XAML
- Как правильно реализовать BackgroundWorker с обновлениями ProgressBar?
- Похоже, что привязки данных не обновляются
- Есть ли эквивалент MessageBox в WPF?
Хотя это работает, использование Диспетчера в моей модели просмотра, как это кажется мне неуместным.
Это не совсем необоснованный подход, и это подход, который принимают многие люди. Лично, если вы используете WPF (или Silverlight 5) и имеете доступ к TPL, я предпочитаю использовать TPL для этого.
Предполагая, что ваша ViewModel построена на streamе пользовательского интерфейса (то есть: по представлению или в ответ на связанное с View событие), что почти всегда относится к IMO, вы можете добавить это в свой конструктор:
// Add to class: TaskFactory uiFactory; public MyViewModel() { // Construct a TaskFactory that uses the UI thread's context uiFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()); }
Затем, когда вы получите свое событие, вы можете использовать его для его маршала:
void Chat_Incoming(Game.ChatLine line) { uiFactory.StartNew( () => Chat.Add(line) ); }
Обратите внимание, что это немного отличается от вашего оригинала, поскольку он больше не блокирует (это больше похоже на использование BeginInvoke
вместо Invoke
). Если вам нужно это заблокировать, пока пользовательский интерфейс не завершит обработку сообщения, вы можете использовать:
void Chat_Incoming(Game.ChatLine line) { uiFactory.StartNew( () => Chat.Add(line) ).Wait(); }
Модель просмотра – хорошее место для синхронизации streamов. Удалите DispatcherTimer из вашей модели и дайте VM обработать его.
Я обожаю ответ Рида и соглашусь с вашими опасениями, что что-то не так с вашим использованием Dispatcher
. Ваше App
VM ссылается на App
, которое, на мой взгляд, ссылается на артефакт пользовательского интерфейса (или элемент управления). Используйте Application
или, еще лучше, вставьте правильный экземпляр Dispatcher
в вашу виртуальную машину, что позволяет избежать необходимости создания экземпляра вашей виртуальной машины в streamе пользовательского интерфейса.