Answer, including clarifying your decision: I finally found a solution.
Think about the problem a little deeper.
Binding a property of an element is part of the functionality of the element itself, not of its container.
Therefore, if you change the DataContext of an element, the default bindings are interpreted relative to it, not the DataContext of its container.
For this reason, you should not assign the DataContext inside a UserControl, as the default bindings behavior will change in a way that is unexpected for the user (programmer).
Now think about how ElementName bindings work.
For example, in the XAML that creates a new class from UserControl, you have defined named elements.
And then create multiple instances of UserControl.
In this case, if all elements with the same names were in the same visual tree, this would create a conflict, since it is not known which of them is being accessed.
(In this case, the "visual tree" is an oversimplification. It actually has to do with the system for registering the names of the UI elements).
To avoid such conflicts, when using XAML for ANY element (not only UserControl), its own local visual tree is created that is not associated with the main visual tree.
BUT!
I already wrote above, Bindings are part of the functionality of the element itself.
And therefore, the binding of a property of type ElementName will search for an element by name not higher in the container, but in the UserControl itself.
The same goes for Templates.
Therefore, the names of elements inside Templates are not visible outside of it.
Default elements are not implemented as UserControl, but as Custom Control.
In this case, there is a separate Sharp class with element logic.
And a separate default Template for this class.
In this implementation, since no XAML is used to create the element, the element does not have its own internal local visual tree.
And all its bindings (of the ElementName type) are interpreted in relation to the general visual tree.
To some extent, you partially implemented this in the Descriptor: ContentControl
class.
Only instead of regregistering the default Template for the type, you set it up in the XAML App.
This implementation will work for an example, but in many cases it can create other problems.
Therefore, it is better to use the "standard" Custom Control for which the theme is created and the default Template is registered.
In the case of DescriptorTwo
, you specify x: Class = "EmbedContent.UserControls.DescriptorTwo"
and this automatically creates its own local visual tree, relative to which the ElementName bindings will work.
Slightly modified example for the solution you found.
using System.Windows;
using System.Windows.Controls;
namespace EmbedContent.CustomControls
{
public class Descriptor : ContentControl
{
#region Registering a default template
static Descriptor()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Descriptor), new FrameworkPropertyMetadata(typeof(Descriptor)));
}
#endregion
#region Ctor
public Descriptor() : base() { }
#endregion
#region Dependency-Properties
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(nameof(Text), typeof(string), typeof(Descriptor), new PropertyMetadata("Descriptor's default Text"));
#endregion
}
}
Theme with default templates - file Themes/Generic.xaml
:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:customcontrols="clr-namespace:EmbedContent.CustomControls">
<Style TargetType="{x:Type customcontrols:Descriptor}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type customcontrols:Descriptor}">
<StackPanel>
<TextBlock x:Name="tblock" Text="Example Text in UserControl"/>
<TextBlock Text="{TemplateBinding Text}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
DescriptorTwo.xml:
<UserControl x:Class="EmbedContent.UserControls.DescriptorTwo"
x:Name="PART_Main"
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:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:EmbedContent.UserControls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel>
<TextBlock x:Name="tblock" Text="Example Text in UserControl"/>
<TextBlock Text="{Binding Text, ElementName=PART_Main}"/>
</StackPanel>
</UserControl>
Examle Window:
<Window x:Class="EmbedContent.ExampleWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:EmbedContent"
xmlns:customcontrols="clr-namespace:EmbedContent.CustomControls"
xmlns:usercontrols="clr-namespace:EmbedContent.UserControls"
mc:Ignorable="d"
Title="ExampleWindow" Height="450" Width="800">
<StackPanel>
<TextBlock x:Name="tblock" Text="Example Text in Parent UIElement"/>
<Border Background="LightBlue" Margin="20" Padding="10"
BorderBrush="Blue" BorderThickness="2">
<customcontrols:Descriptor Text="{Binding Text, ElementName=tblock}"/>
</Border>
<Border Background="LightGreen" Margin="20" Padding="10"
BorderBrush="Green" BorderThickness="2">
<usercontrols:DescriptorTwo Text="{Binding Text, ElementName=tblock}"/>
</Border>
</StackPanel>
</Window>
Result:


As you can see in the XAML Designer and when launched at runtime, such bindings (in the UserControl) also work differently, which causes additional problems.
Therefore, the main purpose of the UserControl is to represent the Data coming through the DataContext.
Also consider the moment with the assignment of Content.
In UserControl in XAML, you set a value for this property.
And assigning it when used does not complement the presentation, but replace it.
In CustonControl, you can explicitly specify in the Template where to insert additional content.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:customcontrols="clr-namespace:EmbedContent.CustomControls">
<Style TargetType="{x:Type customcontrols:Descriptor}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type customcontrols:Descriptor}">
<StackPanel>
<TextBlock x:Name="tblock" Text="Example Text in UserControl"/>
<TextBlock Text="{TemplateBinding Text}"/>
<ContentPresenter Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
<Window x:Class="EmbedContent.ExampleWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:EmbedContent"
xmlns:customcontrols="clr-namespace:EmbedContent.CustomControls"
xmlns:usercontrols="clr-namespace:EmbedContent.UserControls"
mc:Ignorable="d"
Title="ExampleWindow" Height="250" Width="400">
<StackPanel>
<TextBlock x:Name="tblock" Text="Example Text in Parent UIElement"/>
<Border Background="LightBlue" Margin="20" Padding="10"
BorderBrush="Blue" BorderThickness="2">
<customcontrols:Descriptor Text="{Binding Text, ElementName=tblock}">
<TextBlock Text="{Binding Text, ElementName=tblock}"/>
</customcontrols:Descriptor>
</Border>
<Border Background="LightGreen" Margin="20" Padding="10"
BorderBrush="Green" BorderThickness="2">
<usercontrols:DescriptorTwo Text="{Binding Text, ElementName=tblock}">
<TextBlock Text="{Binding Text, ElementName=tblock}"/>
</usercontrols:DescriptorTwo>
</Border>
</StackPanel>
</Window>
Result:
