Как (правильно) обновить M в MVVM приложения WPF?
Пройдя серию вопросов Edward Tanguay упоминается использование MVVM для приложения WPF, которое можно найти на боковой панели "Связанные" его Толстых моделей, тощих моделей ViewModel и тупых представлений, лучший подход MVVM?, Я немного смущен его окончательным приложением WPF в Big smart ViewModels, немыми представлениями и какой-либо моделью, лучшим подходом MVVM?
Его М (Модель) - это класс Клиента:
//model
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime TimeOfMostRecentActivity { get; set; }
public static Customer GetCurrentCustomer()
{
return new Customer
{ FirstName = "Jim"
, LastName = "Smith"
, TimeOfMostRecentActivity = DateTime.Now
};
}
}
который возвращает текущего пользователя. Вид, потому что он возвращает дубликаты вновь созданного "текущего" пользователя...
Но где данные М хранятся и обновляются в случае необходимости?
Предположим, я хочу изменить текущего пользователя модели FirstName
к "Геннадию"?
Я добавил кнопку для обновления модели с помощью этого обработчика события нажатия кнопки:
private void button1_Click(object sender, RoutedEventArgs e)
{
}
с целью изменения данных модели, которые будут отражены в графическом интерфейсе.
Как я могу это сделать, нажав эту кнопку... извините, поместив код в этот button1_Click()
?
Или что-то не так с моим желанием сделать это?
Затем. как правильно обновить / изменить M в MVVM?
Обновить:
Кажется, все ответы относятся к тому, что я должен вносить изменения не в M, а в VM.
Хотя я специально спросил о ссылочной реализации MV-VM с помощью:
public CustomerViewModel()
{
_timer = new Timer(CheckForChangesInModel, null, 0, 1000);
}
private void CheckForChangesInModel(object state)
{
Customer currentCustomer = CustomerViewModel.GetCurrentCustomer();
MapFieldsFromModeltoViewModel(currentCustomer, this);
}
public static void MapFieldsFromModeltoViewModel
(Customer model, CustomerViewModel viewModel)
{
viewModel.FirstName = model.FirstName;
viewModel.LastName = model.LastName;
viewModel.TimeOfMostRecentActivity = model.TimeOfMostRecentActivity;
}
Так, например, после реализации кода из изменений ответа Адольфо Переса, TextBox
содержимое меняется с "Джим" на "Геннадий" только на период времени, установленный в _timer = new Timer(CheckForChangesInModel, null, 0, 1000);
,
Вся логика, на которую ссылаются мной MV-VM в подходе WPF, такова, что это "M" должно быть обновлено, чтобы VM подхватила эти изменения, но не "VM".
Более того, я не могу понять, если вносить изменения в ВМ, как они могут быть отражены в М, если ВМ знает о М, но - не наоборот - Модель не знает о ViewModel).
4 ответа
В MVVM вы должны избегать использования кода. Причина в том, что вы хотите получить тестируемые классы, в этом случае ваши виртуальные машины полностью независимы от вашего V. Вы можете запустить набор модульных тестов на вашей виртуальной машине без участия V. Вы также можете подключать различные типы представлений, не влияя на вашу бизнес-логику.
Ваша кнопка свяжет свое свойство Command с ICommand
имущество выставлено в вашей ВМ. Эта команда в вашей виртуальной машине будет обрабатывать ваше событие щелчка указанным вами способом.
По вашему мнению:
<Button Content="Change FirstName"
Command="{Binding Path=ChangeFirstNameCommand"}/>
В вашей ViewModel:
//Define your command
public ICommand ChangeFirstNameCommand {get;set;}
//Initialize your command in Constructor perhaps
ChangeFirstNameCommand = new RelayCommand(OnChangeFirstName,CanChangeFirstName);
private void OnChangeFirstName()
{
//Your FirstName TextBox in your V will be updated after you click the Button
this.FirstName = "Gennady";
}
private bool CanChangeFirstName()
{
//Add any validation to set whether your button is enabled or not.
// WPF internals take care of this.
return true;
}
Очень важно помнить, что в этом шаблоне ваш V знает о вашей виртуальной машине, а ваша виртуальная машина знает о вашем M, но не наоборот.
В вашем примере, если вы хотите изменить свойство Model FirstName, вы должны сделать следующее:
- Создать виртуальную машину, которая реализует
INotifyPropertyChanged
- Укажите ваше свойство M FirstName в вашей виртуальной машине, уведомляя об изменениях
Создайте TextBox в вашем представлении XAML и привяжите его свойство Text к вашей виртуальной машине. Параметр irstName Binding Mode=TwoWay.
<TextBox Text= "{Binding Path=FirstName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
При вводе в TextBox ваше FirstName будет непосредственно заполнено в VM-M. Кроме того, благодаря двухсторонней привязке, если вы измените свойство FirstName в своей виртуальной машине, это изменение будет автоматически отражено в вашей виртуальной машине.
- Установите View.DataContext для вашей виртуальной машины. Это то, что устанавливает контекст для всех ваших привязок данных, если вы не укажете другой источник привязки.
- Если вы хотите сохранить изменения в БД, внедрите класс обслуживания в вашей ВМ, который позаботится о операциях CRUD
Взгляните на этот простой пример:
http://www.codeproject.com/Articles/126249/MVVM-Pattern-in-WPF-A-Simple-Tutorial-for-Absolute
Ваша модель - это ваши доменные (бизнес) объекты. Есть несколько способов получить их. Например, у вас может быть класс репозитория, который предоставляет вам ваши данные, когда вы запрашиваете их, и обрабатывает персистентность, когда вы хотите сохранить их.
Ваша модель представления - это класс, который обрабатывает логику пользовательского интерфейса, например, обновление полей, реагирование на действия пользователя и т. Д. В вашем случае вы можете передать экземпляр класса CustomerRepository в вашу модель представления. Затем в коде модели представления вы получаете экземпляр Customer из репозитория и заполняете свойства модели представления, с которыми связаны ваши элементы пользовательского интерфейса.
Ваше мнение - это просто набор правил того, как вы хотите показать информацию пользователю. Он должен быть как можно более декларативным и логически свободным.
Имея такой код:
private void button1_Click(object sender, RoutedEventArgs e)
{
}
с вашей точки зрения (или, что еще хуже, с вашей модели), это огромная ошибка, которая нарушает модель и может (и, несомненно, приведет) к проблемам. Вы должны связать поля ICommand ViewModel с кнопками. Вы не должны пытаться написать приложение WPF в стиле событий WinForm.
Так работает mvvm в целом, и его основная цель - поддержка многоуровневой архитектуры в вашем приложении.
Прежде всего, вам нужно работать над V
а также VM
,
Если вы используете Click
событие для кнопки, вы определенно не следуете этой архитектуре.
Вам нужно использовать WPF
а также XAML
по вашему мнению, привязать к вашему ViewModel
, ваш ViewModel
должно быть подмножеством конкретной или потенциально многих моделей и представлять свойства View
что позволяет для привязки.
Я также хотел бы рассмотреть исследования:
RelayCommand
а такжеICommand
для привязки ваших кнопок.Repository
шаблон для обмена моделями и создания способаCRUD
Учебное пособие, которому вы следовали, кажется не очень хорошим в том смысле, что концепции не были правильно представлены или вы их не поняли.
Если у вас чистое приложение WPF, было бы интересно исследовать "обратный" шаблон MVVM, ViewModel-First.
Для веб-разработки обычно используют MVVM, потому что веб-страницы загружаются через браузер, и создается представление, которое создает ViewModel.
Пользователи WPF не переходят на страницы (если вы не используете навигацию по страницам), поэтому становится интереснее следовать VM-V-VM-M:
interface IMyView
Show();
//view implementations in different assemblies:
//silverlight
class SilverlightMyView:IMyView
Show();
//wpf
class WpfMyView:IMyView
Show();
class MyViewModel
IMyView _view;
MyModel _model;
//ex. view resolved by DI (Unity, ..)
MyViewModel(IMyView view)
_view = view
Show(model as MyModel)
_model = model;
_view.DataContext = this;
_view.Show();