Один ViewModel для UserControl и Window или отдельные ViewModels

У меня есть MainWindow и AddEdit UserControl . Внутри MainWindow я создаю этот AddEdit как , ранее это пространство имен добавляется в элемент Window:

 xmlns:Views="clr-namespace:MyProject.WPF.Views" +++++++++++++++ ++++++++++++++++ ListOfData + + DataDetails + + + + DataOne + + Name: txtBox1+ DataTwo + + + DataThree + + + + + Save data + +++++++++++++++ ++++++++++++++++ 

Когда пользователь выбирает данные с левой стороны (например, DataTwo), я хочу отобразить его свойства (для простоты – только свойство Name) внутри пользовательского элемента управления AddEdit (панель DataDetails).

Поскольку этот UserControl хранится отдельно от MainWindow, я должен использовать тот же MainWindowViewModel и один и тот же файл данных или я должен создать отдельный ViewModel для AddEdit UserControl ?

Надеюсь, это звучит ясно, если не спросить подробности.

Part 1. Display the properties of the control in MVVM

Как я уже сказал в комментариях:

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

ViewModel напрямую не связан с представлением, поэтому просто ссылаться на имя элемента управления будет неправильно. Было бы лучше установить свойство в Model и связать его с View через ViewModel , но имя свойства не поддерживает привязку (цитата из MSDN ):

Связывание данных с именем является технически возможным, но является чрезвычайно необычным сценарием, поскольку связанное с данными имя не может служить основной назначенной цели свойства: предоставить точку соединения идентификатора для кода.

поэтому я предлагаю использовать свойство Tag или Uid . В моем примере (дайте ниже) я использую свойство Uid для этих целей.

Part 2. Communication via ViewModels (pattern Mediator)

Существует несколько вариантов осуществления шаблона посредника , но мне больше нравится реализация XAML Guy , это просто и понятно – шаблон посредника .

Implementation code

 public static class Mediator { static IDictionary>> pl_dict = new Dictionary>>(); static public void Register(string token, Action callback) { if (!pl_dict.ContainsKey(token)) { var list = new List>(); list.Add(callback); pl_dict.Add(token, list); } else { bool found = false; foreach (var item in pl_dict[token]) if (item.Method.ToString() == callback.Method.ToString()) found = true; if (!found) pl_dict[token].Add(callback); } } static public void Unregister(string token, Action callback) { if (pl_dict.ContainsKey(token)) { pl_dict[token].Remove(callback); } } static public void NotifyColleagues(string token, object args) { if (pl_dict.ContainsKey(token)) { foreach (var callback in pl_dict[token]) callback(args); } } } 

Чтобы продемонстрировать свою работу, я создал небольшой пример, состоящий из двух Views , каждый из которых имеет свой собственный ViewModel и Model .

Структура проекта показана ниже:

Структура проекта

Output

Пример использования

Когда вы нажимаете кнопку «Button», ListOfData ViewModel обменивается через посредник с помощью DataDetails ViewModel , таким образом:

 Mediator.NotifyColleagues("ShowDetails", true); Mediator.NotifyColleagues("SetSelectedFruit", ListOfDataModel.FruitGreen); 

Все процедуры, которые взаимодействуют со свойствами, должны регистрировать их ViewModel следующим образом:

 private void ShowDetails_Mediator(object args) { bool showDetails = (bool)args; if (showDetails == true) { DataDetailsModel.IsVisible = true; } else { DataDetailsModel.IsVisible = false; } } private void SetSelectedFruit_Mediator(object args) { string selectedFruit = (string)args; DataDetailsModel.SelectedFruit = selectedFruit; } public DataDetailsViewModel() { DataDetailsModel = new DataDetailsModel(); Mediator.Register("ShowDetails", ShowDetails_Mediator); Mediator.Register("SetSelectedFruit", SetSelectedFruit_Mediator); } 

В примере я использовал DataTemplate вместо UserControl . Ниже представлена ​​основная часть проекта:

MainWindow.xaml

           

Models

DataDetailsModel

 public class DataDetailsModel : NotificationObject { #region SelectedFruit private string _selectedFruit = ""; public string SelectedFruit { get { return _selectedFruit; } set { _selectedFruit = value; NotifyPropertyChanged("SelectedFruit"); } } #endregion #region IsVisible private bool _isVisible = false; public bool IsVisible { get { return _isVisible; } set { _isVisible = value; NotifyPropertyChanged("IsVisible"); } } #endregion } 

ListOfDataModel

 public class ListOfDataModel : NotificationObject { #region FruitGreen private string _fruitGreen = "Apple"; public string FruitGreen { get { return _fruitGreen; } set { _fruitGreen = value; NotifyPropertyChanged("FruitGreen"); } } #endregion #region FruitYellow private string _fruitYellow = "Limon"; public string FruitYellow { get { return _fruitYellow; } set { _fruitYellow = value; NotifyPropertyChanged("FruitYellow"); } } #endregion } 

ViewModels

DataDetailsViewModel

 public class DataDetailsViewModel { #region DataDetailsModel private DataDetailsModel _dataDetailsModel = null; public DataDetailsModel DataDetailsModel { get { return _dataDetailsModel; } set { _dataDetailsModel = value; } } #endregion #region ShowDetails_Mediator private void ShowDetails_Mediator(object args) { bool showDetails = (bool)args; if (showDetails == true) { DataDetailsModel.IsVisible = true; } else { DataDetailsModel.IsVisible = false; } } #endregion #region SetSelectedFruit_Mediator private void SetSelectedFruit_Mediator(object args) { string selectedFruit = (string)args; DataDetailsModel.SelectedFruit = selectedFruit; } #endregion #region DataDetailsViewModel Constructor public DataDetailsViewModel() { DataDetailsModel = new DataDetailsModel(); Mediator.Register("ShowDetails", ShowDetails_Mediator); Mediator.Register("SetSelectedFruit", SetSelectedFruit_Mediator); } #endregion } 

ListOfDataViewModel

 public class ListOfDataViewModel { #region ListOfDataModel private ListOfDataModel _listOfDataModel = null; public ListOfDataModel ListOfDataModel { get { return _listOfDataModel; } set { _listOfDataModel = value; } } #endregion #region GreenButtonCommand private ICommand _greenButtonCommand = null; public ICommand GreenButtonCommand { get { if (_greenButtonCommand == null) { _greenButtonCommand = new RelayCommand(param => this.GreenButton(), null); } return _greenButtonCommand; } } private void GreenButton() { Mediator.NotifyColleagues("ShowDetails", true); Mediator.NotifyColleagues("SetSelectedFruit", ListOfDataModel.FruitGreen); } #endregion #region YellowButtonCommand private ICommand _yellowButtonCommand = null; public ICommand YellowButtonCommand { get { if (_yellowButtonCommand == null) { _yellowButtonCommand = new RelayCommand(param => this.YellowButton(), null); } return _yellowButtonCommand; } } private void YellowButton() { Mediator.NotifyColleagues("ShowDetails", true); Mediator.NotifyColleagues("SetSelectedFruit", ListOfDataModel.FruitYellow); } #endregion #region ListOfDataViewModel Constructor public ListOfDataViewModel() { ListOfDataModel = new ListOfDataModel(); } #endregion } 

Views

DataDetailsView

          

ListOfDataView

         

Этот проект доступен по этой ссылке .

Поскольку UserControl поддерживается отдельно и не является частью содержимого Window. Я предлагаю иметь отдельную ViewModel .

Преимущества наличия отдельной модели ViewModel:

  1. Повторное использование. В будущем, если вы хотите внести некоторые изменения в данные, связанные с UserControl ( может быть, некоторые изменения в логике ), все, что вам нужно, перейти в ViewModel и обновить его, и оно будет отображаться во всех windowsх. Вам не нужно беспокоиться, чтобы перейти к каждой модели представления windows и обновить код.

  2. Возможность тестирования. Если вы хотите протестировать логику, связанную с вашим контролем ( часть данных, которую я здесь не рассматриваю , вы можете записать ее отдельно. Не нужно беспокоиться о тестировании кода модели windows.

  3. Loosely Coupled – более одного человека могут работать изолированно. Скажем, один разработчик должен обновить некоторый код, связанный с Главным окном, а другой должен обновить некоторый код, связанный с UserControl. С одним ViewModel на месте будет некоторое перекрытие, и они не могут работать изолированно, так как они зависят от другого человека, чтобы выполнить свою работу, прежде чем он сможет подключить его / ее код в ViewModel.

Также проверьте здесь связь между различными ViewModels, поскольку вам может потребоваться обмен данными между моделью windows и моделью управления User Control, чтобы передать выбранные данные в левом окне.

  • Связывающие элементыСвойства ComboBoxColumn в WPF DataGrid
  • Являются ли "{Binding Path =.}" И "{Binding}" действительно равными
  • Методология программирования WPF
  • ObservableCollection и резьба
  • WPF TextBox Validation C #
  • Событие ComboBox - SelectionChanged имеет старое значение, а не новое значение
  • VirtualizationStackPanel + MVVM + множественный выбор
  • Как установить событие MouseOver / триггер для границы в XAML?
  • Связывание свойства enum с ComboBox в WPF
  • Поиск конкретных цветов пикселей BitmapImage
  • Плоская кнопка wpf
  • Давайте будем гением компьютера.