DataContext пользовательского контроллера

#wpf #xaml #datacontext

#wpf #xaml #datacontext

Вопрос:

Я создаю UserControl Я хочу использовать что-то вроде этого:

 <controls:ColorWithText Color="Red" Text="Red color" />
  

До сих пор я реализовывал аналогичные элементы управления, подобные этому:

 <UserControl x:Class="Namespace.ColorWithText" Name="ThisControl">
    <StackPanel Orientation="Horizontal" >
        <Border Width="15" Height="15" Background="{Binding Color, ElementName=ThisControl}" />
        <TextBlock Text="{Binding Text, ElementName=ThisControl}" />
    </StackPanel>
</UserControl>
  

где Color и Text — свойства зависимости элемента управления, определенные в коде. Это работает, но указывать ElementName каждый раз кажется ненужным.

Другой вариант, который работает, — это использование

 <UserControl x:Class= DataContext="{Binding ElementName=ThisControl}" Name="ThisControl">
  

и не указывающий ElementName s, но мне это тоже не кажется чистым решением.

У меня есть два вопроса:

  1. Почему не <UserControl DataContext="{RelativeSource Self}"> работает?
  2. Каков наилучший способ сделать что-то подобное?

Ответ №1:

Для первого попробуйте :

 <UserControl DataContext="{Binding RelativeSource={RelativeSource Self}}">
  

И что касается второго вопроса, я думаю, что использование ElementName or AncestorBinding — лучший способ привязки к UserControl свойствам.

Комментарии:

1. Вопрос. Если вы установите RelativeSource подобным образом, как он узнает, какая виртуальная машина этого элемента управления? Как вы это настраиваете?

2. Я знаю, что это старый пост, но для всех, кто еще придет к ней… Вы не настраиваете виртуальную машину для отдельного элемента управления. Вы устанавливаете свойства для своего элемента управления, и этих свойств должно быть достаточно, чтобы заставить его «работать». Если элемент управления зависит от некоторой виртуальной машины или тесно связан / зависит от помещения в определенный контекст для работы, то это не «элемент управления». Вы нарушили принцип разделения интересов.

3. Насколько я могу судить, это нарушает любой UserControl с DependencyProperty . См nikolalukovic.com/programming /… и / или ответ от @jdawiz.

Ответ №2:

Почему вы не можете использовать <UserControl DataContext="{RelativeSource Self}"> ?

Вот как вы могли бы использовать элемент управления

 <Grid DataContext="{StaticResource ViewModel}">
    <!-- Here we'd expect this control to be bound to -->
    <!-- ColorToUse on our ViewModel resource          -->
    <controls:ColorWithText Color="{Binding ColorToUse}" />
</Grid>
  

Теперь, поскольку мы жестко запрограммировали наш контекст данных в элементе управления, вместо этого он попытается выполнить поиск свойства ColorToUse в объекте ColorWithText, а не в вашей ViewModel, что, очевидно, приведет к сбою.

Вот почему вы не можете установить DataContext в пользовательском элементе управления. Спасибо Брандуру за то, что помог мне это понять.

Каков наилучший способ сделать что-то подобное?

Вместо этого вы должны установить DataContext в первом дочернем элементе пользовательского интерфейса вашего элемента управления.

В вашем случае вы хотите

 <StackPanel 
  DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
  Orientation="Horizontal" >
  

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

Комментарии:

1. Это, безусловно, лучшее решение! Он сохраняет привязки элементов управления и не требует какого-либо конкретного именования элемента.

Ответ №3:

Я знаю, что на этот вопрос был дан ответ, но ни одно из объяснений не дает понимания того, DataContext как это работает. Эта ссылка отлично подходит для этого.

ВСЕ, ЧТО ВЫ ХОТЕЛИ ЗНАТЬ О ПРИВЯЗКЕ ДАННЫХ В WPF, SILVERLIGHT И WP7 (ЧАСТЬ ВТОРАЯ)

В ответ на ваш вопрос # 1

Почему не <UserControl DataContext="{RelativeSource Self}"> работает?

Это краткое изложение приведенной выше ссылки. DataContext не должно быть установлено значение Self на UserControl уровне элемента. Это потому, что это нарушает наследование DataContext . Если вы установите для него значение self и поместите этот элемент управления в Window или другой элемент управления, он не унаследует Windows DataContext .

DataContext наследуется для всех нижестоящих элементов XAML и для всех XAML UserControl -ов, если только он где-то не перезаписан. Устанавливая UserControl DataContext значение для себя, это перезаписывает DataContext и прерывает наследование. Вместо этого вложите его на один элемент глубоко в XAML, в вашем случае StackPanel . Поместите DataContext привязку сюда и привяжите ее к UserControl . Это сохраняет наследование.

Смотрите также эту ссылку ниже для подробного объяснения этого.

ПРОСТОЙ ШАБЛОН ДЛЯ СОЗДАНИЯ ПОВТОРНО ИСПОЛЬЗУЕМЫХ ПОЛЬЗОВАТЕЛЬСКИХ ЭЛЕМЕНТОВ УПРАВЛЕНИЯ В WPF / SILVERLIGHT

В ответ на ваш вопрос № 2
каков наилучший способ сделать что-то подобное?

Смотрите пример кода ниже.

 <UserControl x:Class="Namespace.ColorWithText" Name="ThisControl">
    <StackPanel Orientation="Horizontal" DataContext="{Binding ElementName=ThisControl}">
        <Border Width="15" Height="15" Background="{Binding Color" />
        <TextBlock Text="{Binding Text}" />
    </StackPanel>
</UserControl>
  

Обратите внимание, что, как только вы сделаете это, вам не понадобится ElementName для каждой привязки.

Комментарии:

1. Единственное элегантное решение, которое сохраняет внешние привязки UserControl. Спасибо.

2. Хорошее объяснение!

3. Это был, безусловно, самый полезный ответ здесь, поскольку он не нарушает наследование datacontext

Ответ №4:

Вы должны использовать

 {Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=Color}
  

при сомнениях, связанных с привязкой данных, всегда обращайтесь к этому листу.
http://www.nbdtech.com/Blog/archive/2009/02/02/wpf-xaml-data-binding-cheat-sheet.aspx

Комментарии:

1. Я должен писать это каждый раз? Я не хочу привязываться ни к чему другому в этом элементе управления, и я думаю, что повторяющийся код — это плохо.

Ответ №5:

Вы можете установить datacontext в значение self в самом конструкторе.

 public ColorWithText()
{
 InitializeComponent();
 DataContext = this;
}
  

Теперь вы можете просто сказать

 <UserControl x:Class="Namespace.ColorWithText" Name="ThisControl">
    <StackPanel Orientation="Horizontal" >
        <Border Width="15" Height="15" Background="{Binding Color}" />
        <TextBlock Text="{Binding Text}" />
    </StackPanel>
</UserControl>
  

Комментарии:

1. или даже в загруженном событии this. Загружено = (отправитель, e) => { это. DataContext = this; };

2. Это очень просто и элегантно. Мне это нравится.

Ответ №6:

Для отчаявшихся душ, которые пытаются заставить работать ответ pdross и не могут:

В нем отсутствует важная деталь — Path=DataContext . Нижний сегмент кода начинает работать, когда вы добавляете его туда, результатом чего является:

     <StackPanel DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext}">