UserControlViewModel 不更新 UserControl 中的绑定 属性
UserControlViewModel doesn't update binded property in UserControl
我希望能够从我的用户控件视图模型中更改主 window 中的 属性。
这是连接
- 主要Window
- 我将我的 属性 从它的视图模型绑定到我的用户控件
- 主要WindowViewModel
- 我的 属性 在这里,它会在用户控件 属性 更改时更新
- UserControl1
- 它的依赖项 属性 绑定到 Main Window View Model returns 来自 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;
//
//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
}
}
非常感谢任何帮助,因为我已经尝试弄清楚这个系统(从 mainwindow 读取 usercontrols viewmodel)将近一个星期了。
为了让我的问题更清楚:
TextInUserControl
<=> TextInMainWindowViewModel
: 工作成功
TextInUserControl
=> TextInUserControlViewModel
:有效,但是当我更改 TextInUserControlViewModel
时,TextInUserControl
不会自动更新。
有没有办法让 TextInUserControl
知道 TextInUserControlViewModel
已更改?
您应该在 MainWindowViewModel
中调用 RaisePropertyChanged("TextInMainWindowViewModel");
您正在将 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;
我已经使用 "bridge property" 解决了这个问题。我复制了可能对其他有同样问题的人有帮助的解决方案:
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);
}
}
}
我希望能够从我的用户控件视图模型中更改主 window 中的 属性。
这是连接
- 主要Window
- 我将我的 属性 从它的视图模型绑定到我的用户控件
- 主要WindowViewModel
- 我的 属性 在这里,它会在用户控件 属性 更改时更新
- UserControl1
- 它的依赖项 属性 绑定到 Main Window View Model returns 来自 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;
//
//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
}
}
非常感谢任何帮助,因为我已经尝试弄清楚这个系统(从 mainwindow 读取 usercontrols viewmodel)将近一个星期了。
为了让我的问题更清楚:
TextInUserControl
<=> TextInMainWindowViewModel
: 工作成功
TextInUserControl
=> TextInUserControlViewModel
:有效,但是当我更改 TextInUserControlViewModel
时,TextInUserControl
不会自动更新。
有没有办法让 TextInUserControl
知道 TextInUserControlViewModel
已更改?
您应该在 MainWindowViewModel
中调用RaisePropertyChanged("TextInMainWindowViewModel");
您正在将 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;
我已经使用 "bridge property" 解决了这个问题。我复制了可能对其他有同样问题的人有帮助的解决方案:
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);
}
}
}