在自定义UserControl上使用DataBinding时,我遇到了一个奇怪的问题。 我的UserControl“UserControl1”有一个依赖项属性LabelText,用于设置UserControl1中标签的内容。此外,它有一个绑定命令“MyCommand”的按钮。此命令只显示一个消息框,并在UserControl1ViewModel中实现。
当我在MainWindow中使用UserControl1并且还有它的视图模型(MainWindowViewModel)时,我想使用Binding to LabelTextFromMainWindow在MainWindow.xaml中设置UserControl的LabelText属性,但是当我这样做时,我遇到了一个问题它使用错误的DataContext,除非您明确指定它。
这是我的代码:
public partial class MainWindow : Window
{
private MainWindowViewModel vm;
public MainWindow()
{
InitializeComponent();
DataContext = vm = new MainWindowViewModel();
vm.LabelTextFromMainWindow = "Hallo";
}
}
class MainWindowViewModel : System.ComponentModel.INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this,
new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
#endregion
private string myLabel;
public string LabelTextFromMainWindow
{
get { return myLabel; }
set
{
myLabel = value;
OnPropertyChanged("MyLabel");
}
}
}
/////////
<UserControl x:Class="WpfApplication1.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"
mc:Ignorable="d"
d:DesignHeight="224" d:DesignWidth="300">
<Grid>
<Button Command="{Binding MyCommand}" Content="Button" Height="55" HorizontalAlignment="Left" Margin="166,99,0,0" Name="button1" VerticalAlignment="Top" Width="104" />
<Label Margin="30,99,0,0" Name="label1" Height="55" VerticalAlignment="Top" HorizontalAlignment="Left" Width="101" />
</Grid>
</UserControl>
public partial class UserControl1 : UserControl
{
private UserControl1ViewModel vm;
private static UserControl1 instance;
public UserControl1()
{
InitializeComponent();
instance = this;
DataContext = vm = new UserControl1ViewModel();
}
public string LabelText
{
get { return (string)GetValue(LabelProperty); }
set { SetValue(LabelProperty, value); }
}
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register("LabelText", typeof(string), typeof(UserControl1), new UIPropertyMetadata(""), OnValidateValueProperty);
private static bool OnValidateValueProperty(object source)
{
if (instance != null)
{
instance.label1.Content = source;
}
return true;
}
}
public class UserControl1ViewModel
{
private DelegateCommand myCommand;
public ICommand MyCommand
{
get
{
if (myCommand == null)
myCommand = new DelegateCommand(new Action<object>(MyExecute),
new Predicate<object>(MyCanExecute));
return myCommand;
}
}
private bool MyCanExecute(object parameter)
{
return true;
}
private void MyExecute(object parameter)
{
MessageBox.Show("Hello World");
}
}
我的主窗口记录如下:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:my="clr-namespace:WpfApplication1">
<Grid>
<my:UserControl1 LabelText="{Binding
Path=DataContext.LabelTextFromMainWindow,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}}"
HorizontalAlignment="Left"
Margin="114,36,0,0"
x:Name="userControl11"
VerticalAlignment="Top" Height="236" Width="292" />
</Grid>
</Window>
我发现以下内容正常工作。
LabelText="{Binding Path=LabelTextFromMainWindow}"
但是,我必须写这个。
LabelText="{Binding Path=DataContext.LabelTextFromMainWindow,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}}"
为了让简单的Binding正常工作,我该怎么做?
答案 0 :(得分:1)
默认情况下控制inherits DataContext from its parent
,除非您将其设置为explicitly
。
在您的情况下,您明确将UserControl的DataContext设置为
DataContext = vm = new UserControl1ViewModel();
使得UserControl上的所有绑定都在UserControl1ViewModel
中查找MainWindowViewModel
中的绑定。
这就是为什么你必须使用RelativeSource
来获取Window的DataContext,即you explicitly asked binding to be found in window's DataContext
而不是它自己的DataContext,我认为使用RelativeSource没有问题。
但是,如果您希望像没有RelativeSource的简单绑定一样工作,首先要you need to get rid of explicitly setting DataContext
并移动MainWindowsViewModel
中的所有命令和属性,以便UserControl从MainWindow继承其DataContext。
OR
您可以为窗口指定名称并使用ElementName
-
<Window x:Class="WpfApplication1.MainWindow"
x:Name="MainWindow"> <--- HERE
<Grid>
<my:UserControl1 LabelText="{Binding
Path=DataContext.LabelTextFromMainWindow,
ElementName=MainWindow}"/>