I have a view model in wpf application, which requires high usage of XML. For this I'm using XMLDataProvider also there are some other properties that are not dependent upon the XML.
So my viewmodel looks like this:
public class ViewModel : ViewModelBase
{
private XmlDocument _xmlDataProvider;
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
base.RaisePropertyChangedEvent("Name");
}
}
public XmlDocument XmlDataProvider
{
get { return _xmlDataProvider; }
set
{
_xmlDataProvider = value;
base.RaisePropertyChangedEvent("XmlDataProvider");
}
}
}
And my XAML code Looks like this:
<UserControl x:Name="ctrlTemplate" x:Class= "CtrlTemplate"
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:WPFControl.UI"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:xckt="clr-namespace:Xceed.Wpf.Toolkit;assembly=Xceed.Wpf.Toolkit"
mc:Ignorable="d"
DataContext="{DynamicResource ViewModel}">
<UserControl.Resources>
<local:ViewModel x:Key="ViewModel" />
</ResourceDictionary>
</UserControl.Resources>
I want to set the xml provider to an internal grid Like this:
<Grid x:Name="grdFill" Margin="0" RenderTransformOrigin="0.502,0.492" HorizontalAlignment="Left">
<Grid.DataContext>
<XmlDataProvider x:Name="template" Document="{Binding XmlDataProvider}"/> **This is not possible as Document is not a Dependency property**
</Grid.DataContext>
<StackPanel>
<Button ="{Binding XPath=Some Xpath}"/>
<Button Content="{Binding Name, Source={StaticResource ViewModel}}"/>
</StackPanel>
</Grid>
So my question is:
How can I set the Document property of XmlDataProvider, without violating MVVM rules?
I also am using commands in place of events, but The Document property would not be available in the ViewModel.
#Ishan,
Here are two tracks to solve your question.
You could make the XmlDataProvider not to be instanciated in XML, but in ViewModel Code.
So you 'd have a XmlDataProvider property in you ViewModel Code
The DataContext of your UserControl would be the View Model
<UserControl.DataContext>
<local:ViewModel />
</UserControl.DataContext>
Then your DataBinding on a button could be made with a "more" focused DataContext :
<Button DataContext="{Binding Path=XmlDataProvider}"
Content="{Binding XPath=Some Xpath}"/>
Personnaly I would not expose an XmlDocument or XmlDataProvider property from the View Model, but I'd prefer to expose some .Net objects instanciated from Linq to XML
Regards
Related
Im not sure what I am doing wrong here.
Lets say, I have two UserControls BoxAand BoxB. Both have a DependencyProperty called Text
BoxB wraps BoxA which has a regular TextBox.
Binding should work like this BoxB.Text <=> BoxA.Text <=> TextBox.Text
Xaml BoxA:
<UserControl x:Class="SandBoxWpf.BoxA"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></TextBox>
</UserControl>
Xaml BoxB:
<UserControl x:Class="SandBoxWpf.BoxB"
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:SandBoxWpf"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<local:BoxA Text="{Binding Text, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></local:BoxA>
</UserControl>
Codebehind of both BoxA and BoxB
using System.Windows;
using System.Windows.Controls;
namespace SandBoxWpf
{
/// <summary>
/// Interaktionslogik für BoxA.xaml
/// </summary>
public partial class BoxX : UserControl
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(BoxX),
new PropertyMetadata(default(string)));
public string Text
{
get => (string) GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public BoxX()
{
InitializeComponent();
}
}
}
MainWindow
<Window x:Class="SandBoxWpf.MainWindow"
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:SandBoxWpf"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<local:BoxB Width="100" Height="20" Text="{Binding Title}"></local:BoxB>
</Grid>
</Window>
As soon as I type something into the BoxB i get a StackoverflowException.
If I remove the Mode=TwoWay or the UpdateSourceTrigger the StackOverflow is gone, but the binding doesnt work either.
If you are building a UserControl with bindable properties (i.e. dependency properties), you must under no circumstances explicitly set the UserControl's DataContext, be it to the control instance or to any private view model.
If you do that, a Binding like
<local:BoxB Text="{Binding Title}">
will no longer work. That Binding expects a Title property in the object in the current DataContext. The DataContext property value is usually inherited from the parent element of the UserControl, e.g. the Window. However, since you've explicitly set the DataContext, this mechanism is avoided.
This becomes particularly confusing with equally named properties in UserControls. When you write
<local:BoxA Text="{Binding Text, ...}"/>
in UserControl BoxB, your expectation is that the Binding source property is BoxB.Text. In fact it is BoxA.Text, because BoxA's DataContext is the BoxA instance.
So remove any
DataContext="{Binding RelativeSource={RelativeSource Self}}"
lines and write the Bindings in the UserControl's XAML with RelativeSource like this:
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay},
RelativeSource={RelativeSource AncestorType=UserControl}"/>
<local:BoxA Text="{Binding Text, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay},
RelativeSource={RelativeSource AncestorType=UserControl}"/>
With any form of Change Notificaiton, one danger is what I call the "Ping Pong" problem. Example:
Property A changes.
Property B is changed to match A.
Property B changed.
Property A is changed to match B.
Recurse to 1
In order to avoid that, the exampel code for Properties with Change notificaiton looks like this:
public string PhoneNumber
{
get
{
return this.phoneNumberValue;
}
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged();
}
}
}
If the input is the same as output, nothing is done. The squence goes:
Property A changes
Property B is chagned to match A
Proeprty B changes
Property A notices it already has that value anyway, so nothing is done.
My best guess is that WPF Elements have no such protection. It is one of those cases were "trying to be smart could result in being really dumb".
I want to bind an element in a page to dependency property in code behind with compiled binding and same time bind another element to ViewModel with usual binding. But it gives a runtime error.
Here is my xaml code.
<Page
x:Class="XbindingProblem.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XbindingProblem"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
DataContext="{Binding Main, Source={StaticResource Locator}}"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="UserDataTemplate" x:DataType="local:User">
<StackPanel>
<TextBlock Text="{x:Bind Name}" />
<TextBlock Text="{x:Bind Age}" />
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<TextBlock Text="{Binding Title}"/>
<ContentPresenter ContentTemplate="{StaticResource UserDataTemplate}" Content="{x:Bind CurrentUser, Mode=OneWay}"/>
</StackPanel>
</Grid>
Here CurrentUser is dependency property which is initially null and then change in run time. This gives following runtime error.
Incorrect type passed into template. Based on the x:DataType global::XbindingProblem.User was expected.
The problem is it passes the ViewModel into UserDataTemplate instead of CurrentUser dependency property when CurrentUser is null.
Can anyone have a good explanation on this problem?
If you remove DataContext="{Binding Main, Source={StaticResource Locator}}", it will work. Why? because {x:Bind CurrentUser} is looking for a property called CurrentUser sitting inside your MainPage.xaml.cs. Since the CurrentUser is indeed a dependency property of your page, it will just work.
However, by specifying the DataContext of your page, the x:Bind is now excepting a CurrentUser property inside your MainViewModel instance, and of course it's not going to find it, so a compile-time error will be thrown.
One possible fix is to set the this.CurrentUser really early, even before calling InitializeComponent.
this.CurrentUser = new User();
InitializeComponent();
But this is IMHO not the the right way of doing things, as it's basically a racing game - it tries to populate the ContentPresenter before the DataContext gets updated, and in the end you will end up having the TextBlock (of which Text binds to Title) and the ContentPresenter attached to different contexts!
So ask yourself why you need to create a dependency property for CurrentUser inside a Page object, instead of having a normal property (with INotifyPropertyChanged implementation) sitting inside your MainViewModel? I'd prefer the latter, 'cause it's more semantically correct.
The question is interesting, what I have done is just remove the datacontext and this is the code behind is similar to yours:
public sealed partial class BlankPage1 : Page
{
public User CurrentUser
{
get { return (User)GetValue(CurrentUserProperty); }
set { SetValue(CurrentUserProperty, value); }
}
// Using a DependencyProperty as the backing store for CurrentUser. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CurrentUserProperty =
DependencyProperty.Register("CurrentUser", typeof(User), typeof(BlankPage1), new PropertyMetadata(null));
public BlankPage1()
{
this.InitializeComponent();
CurrentUser = new User() { Name = "Hello", Age = "20" };
}
}
public class User
{
public String Name { get; set; }
public String Age { get; set; }
}
might be you have the User class in another namespace or have another class in the typeof(...) of the dependency property. Because I tested that and works. The DataContext of the page can be whatever you want it won't affect.
Then I added the datacontext just to test:
<Page.DataContext>
<local:Main/>
</Page.DataContext>
and the code just for testing:
public class Main
{
public String Title { get; } = "title";
public User MainUser { get; set; }
}
And it does not throws any exception, appears the Main data and the CurrentUser data.
UPDATE. The error happens when the User is null so it is like the x:Bind is null it propagates to the Binding,To solve that (it was tough):
<Page x:Name="Root"
x:Class="Deletetb.BlankPage1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Deletetb"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" >
<Page.DataContext>
<local:Main/>
</Page.DataContext>
<Page.Resources>
<DataTemplate x:Key="UserDataTemplate" x:DataType="local:User">
<StackPanel>
<TextBlock Text="{x:Bind Name}" />
<TextBlock Text="{x:Bind Age}" />
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel DataContext="{x:Null}">
<TextBlock Text="{Binding DataContext.Title, ElementName=Root}" />
<ContentPresenter ContentTemplate="{StaticResource UserDataTemplate}" Content="{x:Bind CurrentUser, Mode=OneWay}"/>
</StackPanel>
</Grid>
</Page>
Where is binding defined (TextBlock) I set the datacontext to null in the parent container (StackPanel) and bind by element name, and it does not crash, also I added a wait by code to test and set the Current User and it works. That was a challenge. hope it also works for you.
Although it breaks the idea of MVVM a bit, you can add a property to your page like this:
public MainViewModel viewModel => DataContext as MainViewModel;
And then in the XAML code reference the page property
<ContentPresenter Content="{x:Bind viewModel.CurrentUser, Mode=OneWay}" />
I'm developing a Windows 8.1 Store app with C# and .Net Framework 4.5.1.
I'm trying to bind Password.SecurePassword to a ViewModel, and reading this SO answer I found a way to do it: Put the PasswordBox in my ViewModel.
But I don't know how to do it. I know how to bind Dependency Properties, but I don't know how to put that control on my ViewModel. This is my XAML:
<Page
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"
DataContext="{Binding MainViewModel, Source={StaticResource Locator}}"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<PasswordBox x:Name="userPassword" />
</Grid>
</Page>
What do I have to do?
You have several options but I'll just give you the basic option without third party libraries.
In your Page constructor. You can do something like this.
public Page()
{
var mainViewModel = this.DataContext as MainViewModel;
if(mainViewModel != null)
{
mainViewModel.PasswordBox = userPassword;
}
}
You can also set it on the Loaded event of the View and set the PasswordBox to the ViewModel.
I'm trying to find the best way to communicate between two User Controls. I have a main XAML window which contains two User Controls which in turn contain various controls. The Code behind of each User Control simply sets the DataContext to a View Model that is associated to it. The View Model contains objects that are bound to the controls.
What I'd like to do is capture when a list box in User Control 1 changes selection, the new selected item be displayed in an edit box in User Control 2. As I'm using View Models I can't declare Dependency Properties so I was wondering what is the accepted way to perform this?
I've attached some basic code to show how I'm setting the controls.
Main Window XAML
<Window x:Class="CommsTest.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CommsTest.View"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:UserControl1 />
<local:UserControl2 />
</Grid>
UserControl1 XAML
<UserControl x:Class="CommsTest.View.UserControl1"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<ComboBox Height="23" HorizontalAlignment="Left" Margin="50,110,0,0" Name="comboBox1" VerticalAlignment="Top" Width="199" ItemsSource="{Binding Combo1}" />
</Grid>
UserControl1ViewModel.cs
class UserControl1ViewModel
{
private ObservableCollection<string> combo1 = new ObservableCollection<string>();
public ObservableCollection<string> Combo1
{
get { return combo1; }
}
}
UserControl2.XAML
<UserControl x:Class="CommsTest.View.UserControl2"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBox Height="23" HorizontalAlignment="Left" Margin="63,84,0,0" Name="textBox1" VerticalAlignment="Top" Width="170" Text="{Binding Path=Text1}" />
</Grid>
UserControl2ViewModel.cs
class UserControl2ViewModel
{
private string text1;
public string Text1
{
get { return text1; }
set { text1 = value; }
}
}
How do I get UserControl2.Text1 to be the selected value of UserControl2.Combo1?
Thanks
While I understand that you are asking how to communicate between UserControls, I would suggest that the answer is to communicate between the view models. This can be easily achieved using delegate objects. In general, you'd need to have a parent view model that is common to the two child view models.
I recently answered a similar question, so I won't duplicate answers. Instead, I would ask you to take a look at the answer from the Passing parameters between viewmodels post here on StackOverflow which explains the solution with code examples.
UPDATE >>>
When I said that you need a common parent to your child view models, I don't mean anything to do with inheritance. I just mean that the parent holds a variable instance of each of the child view models... the parent instantiates the child view models.
Instead of creating the view model instance in the view code behind, you can do it in the parent view model and connect the view models to the views like this:
In Resources:
<DataTemplate DataType="{x:Type ViewModels:MainViewModel}">
<Views:MainView />
</DataTemplate>
...
<DataTemplate DataType="{x:Type ViewModels:UsersViewModel}">
<Views:UsersView />
</DataTemplate>
Then you just need to display an instance of the view model and the appropriate view will be displayed:
In ParentView:
<ContentControl Content="{Binding ViewModel}" />
In ParentViewModel:
public BaseViewModel ViewModel { get; set; } // Implement INotifyPropertyChanged
Then when you want to display a new view:
ViewModel = new UsersViewModel();
If your child views do not have a BaseViewModel and/or are not interchangable, then you could just add a property for each of them:
public MainViewmodel MainViewModel { get; set; } // Implement INotifyPropertyChanged
public UsersViewmodel UsersViewModel { get; set; } // properly for these properties
Either way, you'll need access to these view models from the parent view if you are going to be able to 'connect them together' with handlers.
I would suggest you, to have only one ViewModel and bind the DataContext to MainWindow.xaml, instead of doing it to each UserControl.
You should also implement INotifyPropertyChanged in your ViewModel to notify the UI whenever you change the value from the code or ViewModel.
Maybe you should think about your self-imposed restriction of not having dependency properties in user controls. MVVM is nice for the overal architecture, but you can overdo it if you put it into every class and control you plan to do.
If your user controls are just controls for the user, they should behave as such. I have never had to communicate with a TextBoxViewModel or ButtonViewModel, those are controls I simply use. Maybe yours is simple, too and does not need it's own viewmodel. Then you could communicate by using dependency properties as all other controls do.
I have created a UserControl with some DependencyProperties (in the example here only one string property). When I instantiate the Usercontrol, I can set the property of the UserControl and it is shown as expected. When I am trying to replace the static text by Binding, nothing is displayed.
My UserControl looks as follows:
<User Control x:Class="TestUserControBinding.MyUserControl"
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"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="100">
<Grid>
<Label Content="{Binding MyText}"/>
</Grid>
</UserControl>
The Code Behind is:
namespace TestUserControBinding {
public partial class MyUserControl : UserControl {
public MyUserControl() {
InitializeComponent();
this.DataContext = this;
}
public static readonly DependencyProperty MyTextProperty =
DependencyProperty.Register(
"MyText",
typeof(string),
typeof(MyUserControl));
public string MyText {
get {
return (string)GetValue(MyTextProperty);
}
set {
SetValue(MyTextProperty, value);
}
}// MyText
}
}
When I try this in my MainWindow, everything is as expected:
<Window x:Class="TestUserControBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestUserControBinding"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<local:MyUserControl MyText="Hello World!"/>
</StackPanel>
</Window>
But this doesn't work:
<Window x:Class="TestUserControBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestUserControBinding"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<local:MyUserControl MyText="{Binding Path=Text}"/>
<Label Content="{Binding Path=Text}"/>
</StackPanel>
</Window>
The behaviour of the label is correct, so there is no Problem with the Property "Text"
What is my mistake?
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.
There is a misunderstanding of how DataContexts 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.