Binding UserControl and MVVM Dependency Properties

I have a MainWindow containing a UserControl, both implemented in an MVVM sample. MainWindowVM has properties that I want to bind to properties in UserControl1VM. But that does not work.

Here is some code (viewmodels use some kind of mvvm-framework that implement INotifyPropertyChanged in the ViewModelBase class, but hopefully not a problem):

MainWindow.xaml:

<Window x:Class="DPandMVVM.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:DPandMVVM" Title="MainWindow" Height="300" Width="300"> <Grid> <local:UserControl1 TextInControl="{Binding Text}" /> </Grid> </Window> 

CodeBehind MainWindow.xaml.cs:

 using System.Windows; namespace DPandMVVM { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new MainWindowVM(); } } } 

MainWindow-ViewModel MainWindowVM.cs:

 namespace DPandMVVM { public class MainWindowVM : ViewModelBase { private string _text; public string Text { get { return _text; } } public MainWindowVM() { _text = "Text from MainWindowVM"; } } } 

And here is UserControl1.xaml:

 <UserControl x:Class="DPandMVVM.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> <TextBlock Text="{Binding TextInTextBlock}" /> </Grid> </UserControl> 

Codebehind UserControl1.xaml.cs:

 using System.Windows.Controls; namespace DPandMVVM { /// <summary> /// Interaction logic for UserControl1.xaml /// </summary> public partial class UserControl1 : UserControl { public UserControl1() { InitializeComponent(); DataContext = new UserControl1VM(); } } } 

And Viewmodel UserControl1VM.cs:

 using System.Windows; namespace DPandMVVM { public class UserControl1VM : DependencyObject { public UserControl1VM() { TextInControl = "TextfromUserControl1VM"; } public string TextInControl { get { return (string)GetValue(TextInControlProperty); } set { SetValue(TextInControlProperty, value); } } public static readonly DependencyProperty TextInControlProperty = DependencyProperty.Register("TextInControl", typeof(string), typeof(UserControl1VM)); } } 

With this constellation, DP cannot be found in MainWindow.xaml.

What am I doing wrong?

+6
source share
5 answers

The first of all that you want DependencyProperty TextInControl be declared inside UserControl1 if you want to link it outside.

Move the DP declaration inside UserControl1 .

 public partial class UserControl1 : UserControl { public UserControl1() { InitializeComponent(); } public string TextInControl { get { return (string)GetValue(TextInControlProperty); } set { SetValue(TextInControlProperty, value); } } public static readonly DependencyProperty TextInControlProperty = DependencyProperty.Register("TextInControl", typeof(string), typeof(UserControl1)); } 

Second , you have implemented the DataContext UserControl on UserControl1VM ,

  public UserControl1() { InitializeComponent(); DataContext = new UserControl1VM(); <-- HERE (Remove this) } 

So, the WPF binding mechanism looks for the Text property in UserControl1VM instead of MainWindowVM . Remove the DataContext setting and update the XAML UserControl1 to this:

 <UserControl x:Class="DPandMVVM.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" x:Name="userControl1"> <Grid> <TextBlock Text="{Binding TextInTextBlock, ElementName=userControl1}" /> </Grid> </UserControl> 

Bind the DP using ElementName by setting x:Name to UserControl.


UPDATE

If you want to have the ViewModel unchanged for the UserControl , you need to update the binding in MainWindow. Explicitly tell the WPF binding engine to search for the property in the MainWindow DataContext using the ElementName in the binding, like this:

 <local:UserControl1 TextInControl="{Binding DataContext.Text, ElementName=mainWindow}" /> 

To do this, you need to set x:Name="mainWindow" to the root level of the window.

+6
source

Now, the XAML of your control refers to the TextInTextBlock property through the DataContext, which in turn "points" to your main window model. A link to the control data and you are done (btw do not set the DataContext for this reason - the binding will no longer work):

 <UserControl x:Class="DPandMVVM.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" x:Name="self"> <Grid> <TextBlock Text="{Binding TextInTextBlock, ElementName=self}" /> </Grid> </UserControl> 
+1
source

This is how I do UserControls with MVVM and DP binding. This is similar to Rohit's answer, but with a few changes. Basically, you need to set the Control internal control model as the DataContext of the root container in the UserControl, not the UserControl itself, so it will not interfere with DP bindings.

eg.

UserControl XAML

 <UserControl x:Class="DPandMVVM.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" x:Name="userControl1"> <Grid x:Name="Root"> <TextBlock Text="{Binding TextFromVM}" /> </Grid> 

UserControl Management Code

 public partial class UserControl1 : UserControl { public UserControl1() { InitializeComponent(); this.ViewModel = new UserControlVM(); } public UserControlVM ViewModel { get { return this.Root.DataContext as UserControlVM ; } set { this.Root.DataContext = value; } } public string TextFromBinding { get { return (string)GetValue(TextFromBindingProperty); } set { SetValue(TextFromBindingProperty, value); } } public static readonly DependencyProperty TextFromBindingProperty = DependencyProperty.Register("TextFromBinding", typeof(string), typeof(UserControl1), new FrameworkPropertyMetadata(null, OnTextBindingChanged)); private static void OnTextBindingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var uc = d as UserControl1; uc.ViewModel.TextFromVM = e.NewValue as string; } } 

This means that the control gets the values ​​from the Root DataContext element, which is our ViewModel, but the ViewModel can be updated using DP binding from outside the control (in your case, binding to the parent ViewModel window, see below)

XAML window

 <Window x:Class="DPandMVVM.Window1" 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:DPandMVVM" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" x:Name="window1"> <Grid x:Name="Root"> <local:userControl1 TextFromBinding="{Binding TextFromWindowVM}" /> </Grid> 

0
source

Here a working solution is possible for you. However, I noticed in the comment above that this will work in the code and perhaps (like my situation) will display as an Object Not Found error in the designer:

 <local:UserControl1 TextInControl="{Binding DataContext.Text, Source={x:Reference <<Your control that contains the DataContext here>>}}" /> 

I would rather have a cleaner solution, but without the mistakes of a designer. I want to learn how to properly bind the dependency property in a user element to a value obtained from the window in which it is contained. What I find is what I'm trying to do (except for what I showed above), for example using ElementName and / or AncestorType / Level, etc., the debugger complains that it cannot find the source and shows that he is looking for a source in the context of a user control! This is similar to the fact that I cannot escape from the context of user control when executing the binding logic when using this control (except for the “design solution” described above).

UPDATE: I noticed that this may not work for you, as your situation may cause a problem, which I just noticed if I change my own source to refer to this window instead of a control that has a data context. If I turn to the window, I get cyclic redundancy. You may understand how to use the original version of the binding, which will work well for you.

I should also add that my situation is probably a bit more complicated since my usercontrol is used in the context of a popup.

0
source

I have a method that I think is much simpler and probably more faithful to MVVM.

In the main XAML window:

 <myNameSpace:myUserControl DataContext="{Binding Status}"/> 

In your main view model (main window data context:

 public myUserControlViewModel Status { set; get; } 

Now you can in the constructor (or whenever you want to create it):

 Status = new myUserControlViewModel(); 

then if you want to set the text property:

 Status.Text = "foo"; 

and make sure that you have a binding to the Text property in the myUserControlViewModel class:

 <TextBox Text="{Binding Text}"/> 

and make sure the property starts PropertyChanged, of course.

Also, if you are using Resharper. You can create a Design instance of the UserControl object in your XAML so that it can bind the bindings and not tell you that the property is never used by doing this:

 <UserControl x:Class="myNameSpace.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" xmlns:myNameSpace="clr-namespace:myNameSpace" d:DataContext="{d:DesignInstance myNameSpace.myUserControl}" mc:Ignorable="d" ...> 

This part:

 xmlns:myNameSpace="clr-namespace:myNameSpace" d:DataContext="{d:DesignInstance myNameSpace.myUserControl}" 
0
source

All Articles