У меня есть какая-то фоновая задача, которая выполняется периодически или если кто-то запускает ее вручную.
Теперь мне нужен какой-то вид прогресса/результата, который показывает обработанные данные. Окно должно отображаться постоянно.
Проблема в том, что каждый раз при запуске фоновой задачи создается новый экземпляр модели данных. Итак, как сохранить привязку модели -> ViewModel, даже если модель будет воссоздана?
Я создал действительно простой пример в качестве демонстрации:
Вид:
<Window x:Class = "View.MainWindow"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local = "clr-namespace:View"
mc:Ignorable = "d"
Title = "MainWindow" Height = "200" Width = "300" Background = "Black">
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Column = "0" Grid.ColumnSpan = "2" Grid.Row = "0" Foreground = "White" HorizontalAlignment = "Center" Content = "{Binding MainModelText}"/>
</Grid>
</Grid>
ViewModel:
public class ViewModel : INotifyPropertyChanged
{
MainModel _MainModel;
string _MainModelText;
public string MainModelText
{
get { return this._MainModelText; }
set
{
this._MainModelText = value;
OnNotifyPropertyChanged("MainModelText");
}
}
public ViewModel(MainModel mainModel)
{
this._MainModel = mainModel;
this._MainModel.PropertyChanged += _MainModel_PropertyChanged;
}
private void _MainModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (string.Equals(e.PropertyName, "SomeText"))
{
this.MainModelText = _MainModel.SomeText + new Random().Next(1000);
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnNotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Модель:
public class MainModel : INotifyPropertyChanged
{
string _SomeText;
public string SomeText
{
get { return this._SomeText; }
set
{
this._SomeText = value;
OnNotifyPropertyChanged("SomeText");
}
}
public MainModel()
{
this.SomeText = "Its MainModel!";
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnNotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Бизнес-логика
public class Logic
{
MainModel _MainModel;
View.MainWindow _Window;
public Logic()
{
this._MainModel = new MainModel();
_Window = new View.MainWindow(new ViewModel(_MainModel));
}
public void Start()
{
_Window.ShowDialog();
}
public void NewAll()
{
this._MainModel = new MainModel();
//working...
this._MainModel.SomeText = "Finished";
}
}
Таким образом, очевидно, что «Готово» не отображается в окне, поскольку оно установлено в другой экземпляр MainModel.
Так как же обновить Modelreference в ViewModel? Какова лучшая практика для чего-то подобного?
Обновлено:
public class Logic
{
MainModel _MainModel;
ViewModel _ViewModel;
View.MainWindow _Window;
public Logic()
{
this._MainModel = new MainModel();
this._ViewModel = new ViewModel(this._MainModel);
_Window = new View.MainWindow(this._ViewModel);
}
public void Start()
{
_Window.ShowDialog();
}
public void NewAll()
{
this._MainModel = new MainModel();
this._ViewModel.Reload(this._MainModel);
//working...
this._MainModel.SomeText = "Finished";
}
}
Добавлено в ВМ:
internal void Reload(MainModel mainModel)
{
this._MainModel = mainModel;
this._MainModel.PropertyChanged -= _MainModel_PropertyChanged;
this._MainModel.PropertyChanged += _MainModel_PropertyChanged;
}
Ваша модель представления должна иметь общедоступное свойство Model INPC. Когда вы дадите ему новый Model, поднимите PropertyChanged("Model") в сеттере. В представлении привязать к {Binding Model.SomeText}.
И удалите свой Logic класс. Это не имеет никакой роли во всем этом. Ваша основная модель просмотра "это программа". MainWindow создает экземпляр основной модели представления в своем конструкторе (или в XAML), и с этого момента главная модель представления становится ответственной. Основная виртуальная машина отвечает за фоновый процесс, который основная виртуальная машина использует для обновления некоторых своих свойств. Любые другие виртуальные машины являются дочерними по отношению к основной виртуальной машине.
Класс Logic является основной частью этого, представление должно быть просто удобным для пользователя взаимодействием (также возможно взаимодействие через консоль). Насколько я понимаю, виртуальная машина находится между моделью и представлением и управляет представлением и отвечает только за это, и ни за что другое, поэтому основная программа - это то, что я назвал в этом примере, класс Logic и выполняет основную работу. . Виртуальная машина -> Представление просто уведомляется, если начинается новое выполнение, позволяющее некоторым взаимодействиям с пользователем. Таким образом, ВМ получает новую Модель и при необходимости что-то показывает пользователю — не более того. Или я ошибаюсь?
Пожалуйста, смотрите редактирование. Это работает, но не знаю, правильный ли это подход.
Я не буду спорить. Удачи.





Если у вас нет веских причин не предоставлять MainModel через свойство VM, я бы рекомендовал это сделать. Затем вы можете просто пропустить свойство SomeText и напрямую привязаться к Model.SomeText, поскольку ваша модель уже реализует INotifyPropertyChanged.
Если вы хотите загрузить совершенно новый экземпляр модели, вы можете установить его непосредственно в свойстве виртуальной машины.
Если вы не хотите предоставлять полную модель для представления, тогда ваш подход работает. Но я бы также вызвал ReloadModel из конструктора модели, чтобы вы разместили код в одном месте. Также вы должны отменить регистрацию события PropertyChanged из oldModel не после того, как вы уже обновили его до новой модели (сначала проверьте _mainModel == null).
В вашем вызове PropertyChanged рассмотрите возможность использования nameof(SomeProperty) вместо фиксированной строки «SomeProperty», как вы делаете сейчас. Тогда код не сломается, если вы в какой-то момент переименуете свойство.
Взгляните на MVVM Light, эта структура избавит вас от написания шаблонного кода снова и снова, а Messenger, который они предоставляют, — это хороший способ передачи сигналов от модели к виртуальной машине. Это упрощает этот образец до нескольких строк.
разве вы не можете просто обновить свойства модели вместо того, чтобы воссоздавать всю модель представления?