Установка свойства в ViewModel из View в WPF - PullRequest
2 голосов
/ 22 июня 2010

У меня есть свойство зависимости в ViewModel, которое является DataContext для моего View.ViewModel не имеет ссылки на View.Свойство ViewModel будет ссылаться на элемент управления в представлении, но мне нужно иметь возможность установить это свойство в XAML.

Как это возможно?Одна мысль, которая у меня была, заключалась в разработке пользовательского элемента управления, который имеет свойство Property и свойство Value, чтобы вы могли сделать что-то подобное в View, чтобы установить свойство в ViewModel:

<PropertySetter Property="{Binding MyViewModelDependencyProperty}" Value="{Binding ElementName=aControlOnMyView" />

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


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

По сути, я пытаюсь установить фокус на текстовом поле, когда пользователь нажимает кнопку.Я написал прикрепленное свойство, которое вы можете прикрепить к элементу управления Button, указать, что такое событие триггера (в данном случае событие «Click»), а затем на каком элементе управления сосредоточиться.Это работает очень хорошо и сохраняет все в XAML.

Однако теперь у меня есть случай использования, когда фокус должен быть установлен на произвольное текстовое поле из события click на кнопке, которая является частью панели инструментов.Эта панель инструментов сама по себе является пользовательским элементом управления, который находится внутри другого пользовательского элемента управления, который находится внутри другого пользовательского элемента управления!Эту панель инструментов необходимо многократно использовать в различных формах, и каждый раз элемент управления, на который нужно фокусироваться после нажатия кнопки, будет отличаться для каждой формы.

Вот почему у меня возникла идея сделать элемент управления фокусом (т. Е. Текстовое поле) свойством самой модели представления (точнее, моей базы ViewModel) и иметь базовый код ViewModel (которым является панель инструментов).привязан к), установите фокус на элемент управления при нажатии кнопки (и, например, метод Add / Edit вызывается на базе ViewModel).

В модульном тестовом поле элемент управления, на котором следует сосредоточиться на свойстве, будетnull, так что метод .Focus () просто не будет вызываться.Так что я не вижу проблемы там.Моя проблема заключается в том, как вы устанавливаете свойство элемента управления фокусом из XAML, поэтому у меня возникла идея PropertySetter.

Мне не нравится тот факт, что ViewModel имеет какую-либо ссылку на элементы управления, сидящие в представлении, ноЯ не вижу другого способа добиться того, что мне нужно.Что, если логика, которая диктует, нужно ли фокусироваться на элементе управления, довольно сложна?Это будет сидеть во ViewModel, конечно?Следовательно, есть ли вред во ViewModel, имеющем это свойство UIElement?Он по-прежнему ничего не знает о конкретном представлении, с которым он связан, он просто знает, что существует элемент управления, на который необходимо установить фокус, когда какое-либо действие происходит в ViewModel.

Ответы [ 2 ]

7 голосов
/ 22 июня 2010

Моя первая реакция (и она сильная) так сказать "Не делай этого!"Давая вашей модели представления ссылку на часть вашего пользовательского интерфейса, вы нарушаете инкапсуляцию, которая делает модели представления такими мощными и полезными.

Например, что делать, если вы хотите выполнить модульное тестирование своей модели представления или сериализовать ее длядиск?В каждом случае часть вашего пользовательского интерфейса не будет присутствовать, потому что не будет никакого представления вообще.Ваши тесты будут пропускать покрытие, а ваше восстановление будет неполным.

Если ваша модель представления действительно нуждается в ссылках на объекты пользовательского интерфейса, и нет лучшего способа для ее построения, лучшее решение - это сделать так, чтобы сама модель представления построила их.контролирует это требует ссылки.Тогда ваше представление может включить этот элемент управления в качестве содержимого ContentPresenter посредством привязки и предоставить стиль для настройки элемента управления, включая ControlTemplate для предоставления его содержимого.Таким образом:

public class MyViewModel
{
  public ListBox SpecialControl { get; set; }
  public MyViewModel()
  {
    SpecialControl = new ListBox();
  }
}

и

<DataTemplate TargetType="{x:Type local:MyViewModel}">
  <DataTemplate.Resources>
    <Style TargetType="ListBox" ... />
  </DataTemplate.Resources>
  ...
  <ContentPresenter Content="{Binding SpecialControl}" />
</DataTemplate>

Другие возможности:

  1. Получить модель представления на самом деле из класса Control, затем переопределить OnApplyTemplate ()и используйте GetTemplateChild, чтобы найти элемент шаблона, имя которого начинается с «PART _»
  2. Реализуйте вложенное свойство, которое принимает имя свойства, находит это свойство в DataContext и устанавливает его в DependencyObject, к которому присоединено свойство.
  3. Реализация вашей идеи PropertySetter

Мой вариант # 2 будет выглядеть следующим образом:

<DataTemplate TargetType="{x:Type MyViewModel}">
  ...
  <TextBox local:PropertyHelper.SetViewModelToThis="SpecialControl" />
  ...
</DataTemplate>

Код в SetViewModelToThis PropertyChangedCallback будет получать модель представления изDataContext, поразмышляйте над ним, чтобы найти свойство «SpecialControl», затем установите его в TextBox.Обратите внимание, что реализация SetViewModelToThis должна учитывать возможность того, что DataContext не устанавливается сразу, и что он может быть изменен, требуя удаления старого параметра и создания нового.

2 голосов
/ 22 июня 2010

Прежде всего, DataContext элемента управления должен быть объектом ViewModel, а не его собственностью. Во-вторых, когда вы TwoWay привязываете свойство ViewModel к своему элементу управления, изменения в значении элемента управления обновят (в вашем случае, 'set') значение свойства ViewModel.

...