我希望能够从用户控件视图模型中更改主窗口中的属性。
这是连接
我可以在所有这些之间进行绑定,但问题是当我从底层更新我的属性(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
已被更改了吗?
答案 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);
}
}
}