MVVM: привязка изменяемой модели

У меня есть какая-то фоновая задача, которая выполняется периодически или если кто-то запускает ее вручную.

Теперь мне нужен какой-то вид прогресса/результата, который показывает обработанные данные. Окно должно отображаться постоянно.

Проблема в том, что каждый раз при запуске фоновой задачи создается новый экземпляр модели данных. Итак, как сохранить привязку модели -> 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;
}

разве вы не можете просто обновить свойства модели вместо того, чтобы воссоздавать всю модель представления?

Steve 26.04.2019 20:51

Ваша модель представления должна иметь общедоступное свойство Model INPC. Когда вы дадите ему новый Model, поднимите PropertyChanged("Model") в сеттере. В представлении привязать к {Binding Model.SomeText}.

15ee8f99-57ff-4f92-890c-b56153 26.04.2019 20:59

И удалите свой Logic класс. Это не имеет никакой роли во всем этом. Ваша основная модель просмотра "это программа". MainWindow создает экземпляр основной модели представления в своем конструкторе (или в XAML), и с этого момента главная модель представления становится ответственной. Основная виртуальная машина отвечает за фоновый процесс, который основная виртуальная машина использует для обновления некоторых своих свойств. Любые другие виртуальные машины являются дочерними по отношению к основной виртуальной машине.

15ee8f99-57ff-4f92-890c-b56153 26.04.2019 21:05

Класс Logic является основной частью этого, представление должно быть просто удобным для пользователя взаимодействием (также возможно взаимодействие через консоль). Насколько я понимаю, виртуальная машина находится между моделью и представлением и управляет представлением и отвечает только за это, и ни за что другое, поэтому основная программа - это то, что я назвал в этом примере, класс Logic и выполняет основную работу. . Виртуальная машина -> Представление просто уведомляется, если начинается новое выполнение, позволяющее некоторым взаимодействиям с пользователем. Таким образом, ВМ получает новую Модель и при необходимости что-то показывает пользователю — не более того. Или я ошибаюсь?

hermgerm 26.04.2019 21:26

Пожалуйста, смотрите редактирование. Это работает, но не знаю, правильный ли это подход.

hermgerm 26.04.2019 21:32

Я не буду спорить. Удачи.

15ee8f99-57ff-4f92-890c-b56153 26.04.2019 21:36
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
47
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если у вас нет веских причин не предоставлять MainModel через свойство VM, я бы рекомендовал это сделать. Затем вы можете просто пропустить свойство SomeText и напрямую привязаться к Model.SomeText, поскольку ваша модель уже реализует INotifyPropertyChanged.

Если вы хотите загрузить совершенно новый экземпляр модели, вы можете установить его непосредственно в свойстве виртуальной машины.

Если вы не хотите предоставлять полную модель для представления, тогда ваш подход работает. Но я бы также вызвал ReloadModel из конструктора модели, чтобы вы разместили код в одном месте. Также вы должны отменить регистрацию события PropertyChanged из oldModel не после того, как вы уже обновили его до новой модели (сначала проверьте _mainModel == null).

В вашем вызове PropertyChanged рассмотрите возможность использования nameof(SomeProperty) вместо фиксированной строки «SomeProperty», как вы делаете сейчас. Тогда код не сломается, если вы в какой-то момент переименуете свойство.

Взгляните на MVVM Light, эта структура избавит вас от написания шаблонного кода снова и снова, а Messenger, который они предоставляют, — это хороший способ передачи сигналов от модели к виртуальной машине. Это упрощает этот образец до нескольких строк.

Другие вопросы по теме