Установка DataContext в UserControl влияет на привязки в родительском

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

<UserControl x:Class="MyControlLib.ChildControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 

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

</UserControl>

Это используется в родительском файле XAML следующим образом:

<UserControl x:Class="MyControlLib.ParentControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:ctrl="clr-namespace:MyControlLib">

             <ctrl:ChildControl x:Name="ChildName" 
                                PropertyOnChild="{Binding PropertyInParentContext}"/>             
</UserControl>

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

Ошибка System.Windows.Data: 40: Ошибка пути BindingExpression: свойство PropertyInParentContext не найдено в 'объекте' '' ChildControl '(Name=' ChildName ')'. BindingExpression: Path = PropertyInParentContext; DataItem = 'ChildControl' (Name= 'ChildName'); целевым элементом является "ChildControl" (Name= "ChildName" ); target - свойство 'PropertyOnChild' (тип 'whatever')

Почему "PropertyInParentContext" ищет в дочернем элементе управления, а не в родительском DataContext?

Если я удалю

DataContext="{Binding RelativeSource={RelativeSource Self}}

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

Я пропустил что-то очевидное здесь?

Ответ 1

Объявление вашего элемента управления и создания экземпляра в основном управляет одним и тем же объектом, все свойства, заданные в объявлении, также устанавливаются на каждом экземпляре. Поэтому, если свойства были "видимыми", так сказать:

<UserControl x:Class="MyControlLib.ParentControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:ctrl="clr-namespace:MyControlLib">
    <ctrl:ChildControl x:Name="ChildName" 
                       DataContext="{Binding RelativeSource={RelativeSource Self}}"
                       PropertyOnChild="{Binding PropertyInParentContext}"/>
</UserControl>

Вот почему вы не устанавливаете DataContext из UserControls, он переопределяет унаследованный DataContext (и даже обфускает тот факт, что существует другой контекст). Если вы хотите привязать к свойствам UserControl в своем объявлении, тогда назовите элемент управления и используйте вместо него ElementName или RelativeSource -bindings.

Ответ 2

Self означает UserControl, поэтому, когда вы устанавливаете DataContext на Self, вы устанавливаете DataContext в объект UserControl.

Правильный синтаксис для привязки к элементу управления DataContext будет {Binding RelativeSource={RelativeSource Self}, Path=DataContext}, однако, поскольку DataContext наследуется родителем, эта привязка абсолютно не нужна в любой ситуации.

Кроме того, если вы привязываете DataContext к Self.DataContext, вы, по существу, создаете цикл, в котором значение привязывается к самому себе.