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.

Rekshino
  • 6,954
  • 2
  • 19
  • 44
YH Kim
  • 31
  • 6
  • 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: http://stackoverflow.com/a/28982771/1136211 – Clemens Apr 04 '17 at 07:56
  • @Clemens So how do I change binding code in constructor? Please show me something. – YH Kim Apr 04 '17 at 08: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. ``. The binding source would be the DataContext inherited from the Window. – Clemens Apr 04 '17 at 09: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? – janonimus Apr 04 '17 at 09:44
  • @janonimus I want to send Mode value from Window1 to UCViewModel. So MessageBox.Show((uc1.DataContext as UCViewModel).Mode.ToString()); should display "Update". – YH Kim Apr 04 '17 at 10:15
  • @Clemens According to your solution, viewmodel should inherit DependencyObject. If I cannot inherit DependencyObject, is there an another solution? – YH Kim Apr 04 '17 at 10:17
  • @YHKim No, the view model should not be derived from DependencyObject. It's only the UserControl's property that is bound. – Clemens Apr 04 '17 at 10:23
  • @Clemens Thanks for your kindness. But though I did so, UserControl's property was not changed. – YH Kim Apr 04 '17 at 10:44
  • Did you remove the UCViewModel instance from the DataContext of your UserControl? It would probably make sense to edit the question with the code you have right now (or delete this question and write a new one). – Clemens Apr 04 '17 at 11:30

1 Answers1

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}

janonimus
  • 1,911
  • 2
  • 11
  • 11