自定义UserControl的DataBinding问题

时间:2013-08-05 16:47:56

标签: wpf data-binding custom-controls

在自定义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正常工作,我该怎么做?

1 个答案:

答案 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}"/>