UserControlViewModel不会更新UserControl中的绑定属性

时间:2015-10-22 13:57:03

标签: c# wpf mvvm data-binding user-controls

我希望能够从用户控件视图模型中更改主窗口中的属性。

这是连接

  • 主窗口
    • 我将我的属性从其视图模型绑定到我的usercontrol
  • MainWindowViewModel
    • 我的属性在于此处,当用户控件属性更改时它会更新
  • 的UserControl1
    • 绑定到主窗口视图模型的依赖项属性从UserControlViewModel返回值
  • UserControl1ViewModel
    • 更改属性(应该更新MainWindowViewModel)的逻辑就在这里。

我可以在所有这些之间进行绑定,但问题是当我从底层更新我的属性(UserControlViewModel)时,它不会在UserControl或我的MainWindowViewModel中更新我的属性。

这是我的所有代码(我还上传了项目on my google drive

MainWindow.xaml

<Window x:Class="WpfApplicationViewToViewModel.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:WpfApplicationViewToViewModel"
        mc:Ignorable="d"
        Title="MainWindow" Height="367" Width="624">
    <StackPanel>
        <local:UserControl1 TextInUserControl="{Binding DataContext.TextInMainWindowViewModel,
            Mode=TwoWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">
        </local:UserControl1>


        <Button Content="Test MainWindow VM" Command="{Binding CommandTestMWVM}" ></Button>
        <Separator></Separator>

    </StackPanel>
</Window>

MainVindow.xaml.cs

using System.Windows;

namespace WpfApplicationViewToViewModel
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainWindowViewModel();
        }


    }
}

MainWindowViewModel.cs

using System;
using System.Windows;
using System.Windows.Input;

namespace WpfApplicationViewToViewModel
{
    class MainWindowViewModel : ViewModelBase
    {
        public string TextInMainWindowViewModel
        {
            get
            {
                return _textInMainWindowViewModel;
            }
            set
            {
                _textInMainWindowViewModel = value;
                RaisePropertyChanged("TextInMainWindowViewModel");
            }
        }
        private string _textInMainWindowViewModel { get; set; }

        //test button
        public MainWindowViewModel()
        {
            _commandTestMWVM = new RelayCommand(new Action<object>(TestMWVM));
        }

        #region [Command] CommandTestMWVM
        public ICommand CommandTestMWVM
        {
            get { return _commandTestMWVM; }
        }
        private ICommand _commandTestMWVM;
        private void TestMWVM(object obj)
        {
            TextInMainWindowViewModel = TextInMainWindowViewModel + "MWVM";
            MessageBox.Show("TextInMainWindowModel " + TextInMainWindowViewModel);
        }
        #endregion
    }
}

UserControl1.xaml (仅包含两个按钮用于测试目的)

<UserControl x:Class="WpfApplicationViewToViewModel.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:WpfApplicationViewToViewModel"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <StackPanel>
        <Button Content="Test UC" Click="Button_Click"></Button>
        <Button Content="Test UCVM" Command="{Binding CommandTestUCVM}" ></Button>
    </StackPanel>
</UserControl>

UserControl1.xaml.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace WpfApplicationViewToViewModel
{
    /// <summary>
    /// Interaction logic for UserControl1.xaml
    /// </summary>
    public partial class UserControl1 : UserControl
    {
        private  UserControl1ViewModel VM = new UserControl1ViewModel();

        public UserControl1()
        {
            InitializeComponent();
            this.DataContext = VM;

            //http://stackoverflow.com/questions/15132538/twoway-bind-views-dependencyproperty-to-viewmodels-property
            //does not work because breaks binding somewhere
            //string propertyInViewModel = "TextInUserControlViewModel";
            //var bindingViewMode = new Binding(propertyInViewModel) { Mode = BindingMode.TwoWay };
            //this.SetBinding(TextInUserControlProperty, bindingViewMode);

        }

        //dependency property declaration
        public static DependencyProperty TextInUserControlProperty =
            DependencyProperty.Register("TextInUserControl",
                typeof(string),
                typeof(UserControl1)

                );


        public string TextInUserControl
        {
            get {

                return (DataContext as UserControl1ViewModel).TextInUserControlViewModel;
            }
            set
            {
                (DataContext as UserControl1ViewModel).TextInUserControlViewModel = value;
                this.SetValue(TextInUserControlProperty, value);
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            TextInUserControl = TextInUserControl + "UC";
            MessageBox.Show("TextInUserControl : " + TextInUserControl);
        }
    }
}

UserControl1ViewModel.cs

using System;
using System.Windows;
using System.Windows.Input;

namespace WpfApplicationViewToViewModel
{
    class UserControl1ViewModel : ViewModelBase
    {

        private string _textInViewModel;
        public string TextInUserControlViewModel
        {
            get { return _textInViewModel; }
            set {
                _textInViewModel = value;
                RaisePropertyChanged("TextInUserControlViewModel");
            } }

        //test button
        public UserControl1ViewModel()
        {
            _commandTestUCVM = new RelayCommand(new Action<object>(TestUCVM));
        }

        #region [Command] CommandTestUCVM
        public ICommand CommandTestUCVM
        {
            get { return _commandTestUCVM; }
        }
        private ICommand _commandTestUCVM;
        private void TestUCVM(object obj)
        {
            TextInUserControlViewModel = TextInUserControlViewModel + "UCVM";
            MessageBox.Show("TextInUserControlViewModel : " + TextInUserControlViewModel);
        }
        #endregion
    }
}

我真的非常感谢任何帮助,因为我一直试图找出这个系统(从主窗口读取usercontrols viewmodel)将近一周。

让我的问题更清楚:

TextInUserControl&lt; =&gt; TextInMainWindowViewModel:成功地工作

TextInUserControl =&gt; TextInUserControlViewModel有效,但当我更改TextInUserControlViewModel时,TextInUserControl不会自动更新。

无论如何我可以让TextInUserControl知道TextInUserControlViewModel已被更改了吗?

3 个答案:

答案 0 :(得分:1)

您正在将UserControl的DataContext设置为UserControl1ViewModel个实例,然后将TextInUserControl属性绑定到DataContext.TextInMainWindowViewModel,这会导致它查找属性UserControl1ViewModel.DataContext.TextInMainWindowViewModel,不存在。

使用WPF / MVVM的首要规则之一:永远不要在用户控件后面的代码中设置this.DataContext = x;,除非您打算永远不要将该控件传递给任何外部值。

相反,您可能想要的是将UserControl1ViewModel的实例添加到MainWindowViewModel,并将UserControl.DataContext绑定到该实例。

例如,

class MainWindowViewModel : ViewModelBase
{
    // add this property
    public UserControl1ViewModel UserControlData { ... }

    public string TextInMainWindowViewModel { ... }
    public ICommand CommandTestMWVM { ... }
}
<!-- change binding to this -->
<local:UserControl1 DataContext="{Binding UserControlData}" />

并删除UserControl构造函数中的以下内容

this.DataContext = VM;

答案 1 :(得分:0)

您应该在MainWindowViewModel中调用RaisePropertyChanged("TextInMainWindowViewModel");

答案 2 :(得分:0)

我通过使用&#34;桥接属性&#34;来解决问题。我复制了可能有助于其他人遇到同样问题的解决方案:

UserControl1.xaml.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace WpfApplicationViewToViewModel
{
    /// <summary>
    /// Interaction logic for UserControl1.xaml
    /// </summary>
    public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();
            this.DataContext = new UserControl1ViewModel(); 
            /*
            [Bridge Binding ©]
            It's not possible to bind 3 properties.
            So this bridge binding handles the communication
            */
            string propertyInViewModel = "TextInUserControlViewModel";
            var bindingViewMode = new Binding(propertyInViewModel);
            bindingViewMode.Mode = BindingMode.TwoWay;
            this.SetBinding(BridgeBetweenUCandVWProperty, bindingViewMode);


        }
        #region Bridge Property
        public static DependencyProperty BridgeBetweenUCandVWProperty =
            DependencyProperty.Register("BridgeBetweenUCandVW",
        typeof(string),
        typeof(UserControl1),
        new PropertyMetadata(BridgeBetweenUCandVWPropertyChanged)
        );

        public string BridgeBetweenUCandVW
        {
            get
            {
                return (string)GetValue(BridgeBetweenUCandVWProperty);
            }
            set
            {
                this.SetValue(BridgeBetweenUCandVWProperty, value);
            }
        }

        private static void BridgeBetweenUCandVWPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((UserControl1)d).TextInUserControl = (string)e.NewValue;
        }
        #endregion

        #region TextInUserControl Property
        public static DependencyProperty TextInUserControlProperty =
            DependencyProperty.Register("TextInUserControl",
                typeof(string),
                typeof(UserControl1),
                new PropertyMetadata(OnTextInUserControlPropertyChanged)
                );

        private static void OnTextInUserControlPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((UserControl1ViewModel)((UserControl)d).DataContext).TextInUserControlViewModel = (string)e.NewValue;
        }

        public string TextInUserControl
        {
            get {
               return (string)GetValue(TextInUserControlProperty);
            }
            set
            {
                this.SetValue(TextInUserControlProperty, value);
            }
        }
        #endregion
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            TextInUserControl += "[UC]";
            MessageBox.Show("TextInUserControl : " + TextInUserControl);
        }
    }
}