Window中的ViewModel与UserControl中的ViewModel之间的WPF数据绑定

时间:2017-04-04 07:54:21

标签: c# wpf xaml mvvm data-binding

我在此窗口中使用了一个窗口和一个用户控件。

我做了以下事情:

  1. 将window属性绑定到window的viewmodel属性。
  2. 将usercontrol属性绑定到usercontrol的viewmodel属性。
  3. 将usercontrol的属性绑定到window的viewmodel属性。
  4. 在我看来,我认为如果我按特定值设置window属性,usercontrol的viewmodel的属性将被设置为相同的值。

    但是usercontrol的viewmodel的属性没有设置为相同的值。

    这是完整的代码。

    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));                
                }
            }
        }
    

    我做错了什么? 请让我知道。

1 个答案:

答案 0 :(得分:2)

使用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));
        }
    }
}

还有一件事,您可能希望将ViewModel属性Mode重命名为另一个名称,如RegMode。原因是它在绑定期间在XAML代码中引起了混淆。

从这个: {Binding Mode, Mode=TwoWay} 或这个: {Binding Path=Mode, Mode=TwoWay} 对于这个不那么令人困惑的人: {Binding RegMode, Mode=TwoWay}