Как правильно привязать свойство зависимостей usercontrol в рамках MVVM

Я не смог найти простой и простой пример того, как правильно реализовать usercontrol с WPF, который имеет свойство зависимостей в рамках MVVM. Мой код ниже не работает, когда я назначаю usercontrol datacontext.

Я пытаюсь:

  1. Задайте свойство зависимости из вызывающего элемента ItemsControl и
  2. Сделайте значение свойства зависимостей доступным для ViewModel вызываемого пользователя.

Мне еще многое предстоит узнать и искренне оценить любую помощь.

Это элемент ItemsControl в самом верхнем пользовательском controllerе, который вызывает вызов пользовательского управления InkStringView с использованием свойства зависимостей TextInControl (пример из другого вопроса).

                  

Вот пользовательский контроль InkStringView с свойством dependency.

  XAML:          

 Code-Behind file: namespace Nova5.UI.Views.Ink { public partial class InkStringView : UserControl { public InkStringView() { InitializeComponent(); this.DataContext = new InkStringViewModel(); <--THIS PREVENTS CORRECT BINDING, WHAT } --ELSE TO DO????? public String TextInControl { get { return (String)GetValue(TextInControlProperty); } set { SetValue(TextInControlProperty, value); } } public static readonly DependencyProperty TextInControlProperty = DependencyProperty.Register("TextInControl", typeof(String), typeof(InkStringView)); } 

}

Это одна из многих причин, по которым вы никогда не должны устанавливать DataContext непосредственно из самого UserControl .

Когда вы это сделаете, вы больше не сможете использовать с ним какой-либо другой DataContext потому что DataContext UserControl жестко привязан к экземпляру, к которому имеет доступ только пользовательский UserControl , какой вид выигрывает один из самых больших преимуществ WPF иметь отдельный интерфейс и слои данных.

Существует два основных способа использования UserControls в WPF

  1. Автономный UserControl который может использоваться везде, где не требуется конкретный DataContext .

    Этот тип UserControl обычно предоставляет DependencyProperties для любых значений, которые ему нужны, и будет использоваться следующим образом:

      

    Типичными примерами, о которых я могу думать, было бы нечто общее, такое как элемент управления календарем или всплывающее управление.

  2. UserControl который предназначен для использования только с определенной Model или ViewModel .

    Эти UserControls гораздо более распространены для меня, и, вероятно, это то, что вы ищете в своем случае. Пример того, как я буду использовать такой UserControl, будет следующим:

      

    Или чаще, он будет использоваться с неявным DataTemplate . DataType DataTemplate – это DataTemplate с DataType и без Key , и WPF будет автоматически использовать этот шаблон в любое время, когда он захочет отобразить объект указанного типа.

              

    При использовании этого метода не требуется ContentPresenter.ItemTemplate или ItemsControl.ItemTemplate .

Не смешивайте эти два метода, это не идет хорошо 🙂


Но в любом случае, чтобы объяснить вашу конкретную проблему немного подробнее

Когда вы создаете свой UserControl, как это

  

вы в основном говорите

 var vw = new InkStringView() vw.TextInControl = vw.DataContext.text; 

vw.DataContext не указан нигде в XAML, поэтому он наследуется от родительского элемента, что приводит к

 vw.DataContext = Strings[x]; 

поэтому ваша привязка, которая устанавливает TextInControl = vw.DataContext.text , действительна и TextInControl = vw.DataContext.text во время выполнения.

Однако, когда вы запускаете это в своем конструкторе UserControl

 this.DataContext = new InkStringViewModel(); 

DataContext устанавливается в значение, поэтому больше не будет автоматически унаследовано от родителя.

Итак, теперь код, который запускается, выглядит следующим образом:

 var vw = new InkStringView() vw.DataContext = new InkStringViewModel(); vw.TextInControl = vw.DataContext.text; 

и, естественно, InkStringViewModel не имеет свойства, называемого text , поэтому привязка не выполняется во время выполнения.

Похоже, вы смешиваете модель родительского представления с моделью UC.

Вот пример, который соответствует вашему коду:

MainViewModel:

 using System.Collections.Generic; namespace UCItemsControl { public class MyString { public string text { get; set; } } public class MainViewModel { public ObservableCollection Strings { get; set; } public MainViewModel() { Strings = new ObservableCollection { new MyString{ text = "First" }, new MyString{ text = "Second" }, new MyString{ text = "Third" } }; } } } 

MainWindow, который использует его:

                       

Ваш UC (нет набора DataContext):

 public partial class InkStringView : UserControl { public InkStringView() { InitializeComponent(); } public String TextInControl { get { return (String)GetValue(TextInControlProperty); } set { SetValue(TextInControlProperty, value); } } public static readonly DependencyProperty TextInControlProperty = DependencyProperty.Register("TextInControl", typeof(String), typeof(InkStringView)); } 

(Ваш XAML в порядке)

При этом я могу получить то, что, как я думаю, является ожидаемым результатом, список значений:

 First I am row 1 Second I am row 1 Third I am row 1 

Вам нужно сделать здесь 2 вещи (я предполагаю, что Strings – ObservableCollection ).

1) Удалить this.DataContext = new InkStringViewModel(); из конструктора InkStringView. DataContext будет одним из элементов коллекции ObservableCollection.

2) Изменение

  

в

  

Xaml, который у вас есть, ищет свойство Text для элемента ItemsControl для привязки значения TextInControl к. Xaml, который я поставил, используя DataContext (который является строкой) для привязки TextInControl к. Если Strings на самом деле является ObservableCollection со строкой Property of SomeProperty, которую вы хотите привязать, вместо этого вместо этого измените ее на это.

  

Ты почти там. Проблема в том, что вы создаете ViewModel для своего UserControl. Это запах.

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

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

Если у вас возникает соблазн спросить: «А как насчет логики в модели представления? Если я избавлюсь от нее, мне нужно будет ввести код в код!» Я отвечаю: «Это бизнес-логика? Это не должно быть встроено в ваш UserControl так или иначе. И MVVM! = No codebehind. Используйте codebehind для логики пользовательского интерфейса. Это то, где он принадлежит».

Давайте будем гением компьютера.