2

I have a window and an usercontrol used within this window.

And I have done the following:

  1. Bound the window property to window's viewmodel property.
  2. Bound the usercontrol property to usercontrol's viewmodel property.
  3. Bound the usercontrol's property to window's viewmodel property.

In my opinion, I think if I set window property by specific value, usercontrol's viewmodel's property will be set to the same value.

But usercontrol's viewmodel's property was not set to the same value.

Here is whole codes.

MainWindow.xaml.cs

 public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { Window1 w = new Window1(); w.Mode = RegisterMode.Update; w.Show(); } } 

Window1.xaml

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:MVVMTest2" x:Name="window" x:Class="MVVMTest2.Window1" Title="Window1" Height="300" Width="300"> <Window.DataContext> <local:WindowViewModel></local:WindowViewModel> </Window.DataContext> <Grid> <local:UserControl1 x:Name="uc1" HorizontalAlignment="Left" Height="100" Margin="77,116,0,0" VerticalAlignment="Top" Width="100" Mode="{Binding DataContext.Mode, ElementName=window, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> <Button Content="Button" HorizontalAlignment="Left" Margin="156,43,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/> </Grid> </Window> 

Window1.xaml.cs

 public partial class Window1 : Window { public RegisterMode Mode { get { return (RegisterMode)GetValue(ModeProperty); } set { SetValue(ModeProperty, value); } } // Using a DependencyProperty as the backing store for Mode. This enables animation, styling, binding, etc... public static readonly DependencyProperty ModeProperty = DependencyProperty.Register("Mode", typeof(RegisterMode), typeof(Window1), new PropertyMetadata(RegisterMode.None)); public Window1() { InitializeComponent(); Binding modeBinding = new Binding(); modeBinding.Source = this.DataContext; modeBinding.Path = new PropertyPath("Mode"); modeBinding.Mode = BindingMode.TwoWay; modeBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; this.SetBinding(Window1.ModeProperty, modeBinding); } private void Button_Click(object sender, RoutedEventArgs e) { MessageBox.Show((uc1.DataContext as UCViewModel).Mode.ToString()); } } 

WindowViewModel.cs

 public class WindowViewModel : INotifyPropertyChanged { RegisterMode mode = RegisterMode.None; public event PropertyChangedEventHandler PropertyChanged; public RegisterMode Mode { get { return this.mode; } set { this.mode = value; RaisePropertyChanged("Mode"); } } void RaisePropertyChanged(string propertyName = "") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } 

UserControl1.xaml

<UserControl x:Class="MVVMTest2.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" xmlns:local="clr-namespace:MVVMTest2" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <UserControl.DataContext> <local:UCViewModel></local:UCViewModel> </UserControl.DataContext> <Grid> </Grid> </UserControl> 

UserControl1.xaml.cs

 public partial class UserControl1 : UserControl { public RegisterMode Mode { get { return (RegisterMode)GetValue(ModeProperty); } set { SetValue(ModeProperty, value); } } // Using a DependencyProperty as the backing store for Mode. This enables animation, styling, binding, etc... public static readonly DependencyProperty ModeProperty = DependencyProperty.Register("Mode", typeof(RegisterMode), typeof(UserControl1), new PropertyMetadata(RegisterMode.None)); public UserControl1() { InitializeComponent(); Binding modeBinding = new Binding(); modeBinding.Source = this.DataContext; modeBinding.Path = new PropertyPath("Mode"); modeBinding.Mode = BindingMode.TwoWay; modeBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; this.SetBinding(UserControl1.ModeProperty, modeBinding); } } 

UCViewModel.cs

 public class UCViewModel : INotifyPropertyChanged { RegisterMode mode = RegisterMode.None; public event PropertyChangedEventHandler PropertyChanged; public RegisterMode Mode { get { return this.mode; } set { this.mode = value; RaisePropertyChanged("Mode"); } } void RaisePropertyChanged(string propertyName = "") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } 

What point am I doing wrong? Please let me know about it.

9
  • 5
    You should never explicitly set a UserControl's DataContext, because doing so effectivly prevents that the DataContext is inherited from the UserControl's parent. See e.g. here: stackoverflow.com/a/28982771/1136211 Commented Apr 4, 2017 at 7:56
  • @Clemens So how do I change binding code in constructor? Please show me something. Commented Apr 4, 2017 at 8:30
  • 1
    There shouldn't be any binding code in the constructors of your UserControl and Window1. Instead, their properties should be bound in XAML when an instance of them is declared, e.g. <local:UserControl1 Mode="{Binding Path=Mode, Mode=TwoWay}" .../>. The binding source would be the DataContext inherited from the Window. Commented Apr 4, 2017 at 9:29
  • @YHKim I just want to clarify what you want to happen, you want the UCViewModel.Mode to get the value set in WindowViewModel.Mode? Commented Apr 4, 2017 at 9:44
  • @janonimus I want to send Mode value from Window1 to UCViewModel. So MessageBox.Show((uc1.DataContext as UCViewModel).Mode.ToString()); should display "Update". Commented Apr 4, 2017 at 10:15

1 Answer 1

3

Try this solution using composition between your ViewModels.

Window1.xaml

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:MVVMTest2" x:Name="window" x:Class="MVVMTest2.Window1" Title="Window1" Height="300" Width="300" Mode={Binding UcViewModel.Mode}> <!-- Added this binding --> <!-- MOVED THIS TO THE CODE-BEHIND <Window.DataContext> <local:WindowViewModel></local:WindowViewModel> </Window.DataContext> --> <Grid> <local:UserControl1 x:Name="uc1" HorizontalAlignment="Left" Height="100" Margin="77,116,0,0" VerticalAlignment="Top" Width="100" Mode="{Binding UcViewModel.Mode}"/> <Button Content="Button" HorizontalAlignment="Left" Margin="156,43,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/> </Grid> </Window> 

Window1.xaml.cs

public partial class Window1 : Window { public RegisterMode Mode { get { return (RegisterMode)GetValue(ModeProperty); } set { SetValue(ModeProperty, value); } } // Using a DependencyProperty as the backing store for Mode. This enables animation, styling, binding, etc... public static readonly DependencyProperty ModeProperty = DependencyProperty.Register("Mode", typeof(RegisterMode), typeof(Window1), new PropertyMetadata(RegisterMode.None)); public Window1() { InitializeComponent(); /* THIS IS NOT NEEDED Binding modeBinding = new Binding(); modeBinding.Source = this.DataContext; modeBinding.Path = new PropertyPath("Mode"); modeBinding.Mode = BindingMode.TwoWay; modeBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; this.SetBinding(Window1.ModeProperty, modeBinding) */ // Use the following instead: this.DataContext = new WindowViewModel(); } private void Button_Click(object sender, RoutedEventArgs e) { MessageBox.Show((uc1.DataContext as UCViewModel).Mode.ToString()); } } 

UserControl1.xaml

<UserControl x:Class="MVVMTest2.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" xmlns:local="clr-namespace:MVVMTest2" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <!-- THIS PART IS NOT NEEDED <UserControl.DataContext> <local:UCViewModel></local:UCViewModel> </UserControl.DataContext> --> <Grid> </Grid> </UserControl> 

UserControl1.xaml.cs

public partial class UserControl1 : UserControl { public RegisterMode Mode { get { return (RegisterMode)GetValue(ModeProperty); } set { SetValue(ModeProperty, value); } } // Using a DependencyProperty as the backing store for Mode. This enables animation, styling, binding, etc... public static readonly DependencyProperty ModeProperty = DependencyProperty.Register("Mode", typeof(RegisterMode), typeof(UserControl1), new PropertyMetadata(RegisterMode.None)); public UserControl1() { InitializeComponent(); /* THIS IS NOT NEEDED Binding modeBinding = new Binding(); modeBinding.Source = this.DataContext; modeBinding.Path = new PropertyPath("Mode"); modeBinding.Mode = BindingMode.TwoWay; modeBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; this.SetBinding(UserControl1.ModeProperty, modeBinding); */ } } 

WindowViewModel.cs

public class WindowViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; /* Remove this: RegisterMode mode = RegisterMode.None; public RegisterMode Mode { get { return this.mode; } set { this.mode = value; RaisePropertyChanged("Mode"); } } */ // Use composition instead UCViewModel _ucViewModel = null; public UCViewModel UcViewModel { get { if (_ucViewModel == null) { _ucViewModel = new UCViewModel(); } return _ucViewModel; } } void RaisePropertyChanged(string propertyName = "") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } 

And one more thing, you might want to rename the ViewModel property Mode to another name like RegMode. The reason is that it's confusing in the XAML code during binding.

From this: {Binding Mode, Mode=TwoWay} Or, this: {Binding Path=Mode, Mode=TwoWay} To this less confusing one: {Binding RegMode, Mode=TwoWay}

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your answer. This is close to what I want.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.