Binding to UserControl DependencyProperty
There is a misunderstanding of how DataContext
s are set. This is working against you...
Ultimately the binding to MyText
on the user control, is not bound to the control's MyText
dependency property but to the page's DataContext
and there is no MyText
property.
Let me explain
Explanation When the user control is put on your main page, it inherits its controls parent's DataContext
(the StackPanel
). If the parent's DataContext
is not set, it will move up the chain to the StackPanel
's parent's DataContext
(ad Infinium) until it gets to the page's DataContext
(which in your example is set and valid).
When you bind on the main page such as <local:MyUserControl MyText="{Binding Path=Text}"/>
it looks for Text
property on the main pages DataContext and sets the dependency property MyText
to that value. Which is what you expect and it works!
Current State
So the state of the user control in your code is this, its DataContext
is bound to the page's DataContext
and MyText
dependency property is set. But the internal control's binding to MyText
fails. Why?
The user control has the parent's data context, and you are asking the control to bind to a MyText
property on that data context. There is no such property and it fails.
Resolution
To bind to the control's instance and get the value from MyText
property, just put a name (an element name) on the control such as
<User Control x:Class="TestUserControBinding.MyUserControl"
...
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
x:Name="ucMyUserControl"
and then properly path the binding away from the default DataContext
and to the elementnamed named instance called ucMyUserControl
. Such as:
<Label Content="{Binding MyText, ElementName=ucMyUserControl }"/>
Note that VS2017/2019 will actually intellisense the ElementName
after you have named the control.
Side Effect of Just Using The Parents Data Context
A side effect of the original situation without the resolution mentioned, is that you could just bind the user control's binding to Text
and it will work because the binding is defaulting to the page's datacontext. Subtle...
<User Control x:Class="TestUserControBinding.MyUserControl"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="100">
<Grid>
<Label Content="{Binding Text}"/>
That works and technically you could remove the dependency property. If the control is not used outside the project, it could be designed to bind to other named properties with no ill effect as well.
Then all usercontrols could become defacto sub controls of the main page, as if you just pasted the internal XAML onto the page.
With the following binding in your UserControl
:
<Label Content="{Binding MyText}"/>
I'm not sure how setting the text directly to the MyText property works. You must be setting the DataContext
on the UserControl
somewhere for this to work.
Regardless, this binding is the issue - as I understand your scenario, you don't want to bind to the DataContext
of the UserControl
because that will not necessarily have a MyText property. You want to bind to the UserControl
itself, and specifically the DependencyProperty
you created. To do that, you need to use a RelativeSource
binding, like the following:
<Label Content="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MyUserControl}}, Path=MyText}"/>
This will navigate up the visual tree to MyUserControl and then find the MyText property there. It will not be dependent on the DataContext
, which will change based on where you place the UserControl
.
In this case, local
refers to a namespace you'll need to define in the UserControl
:
<UserControl x:Class="TestUserControBinding.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestUserControBinding"
...>
And your second example should work at that point.