0

在自定义 UserControl 上使用 DataBinding 时遇到一个奇怪的问题。我的 UserControl“UserControl1”有一个依赖属性 LabelText,它在我的 UserControl1 中设置标签的内容。此外,它还有一个绑定命令“MyCommand”的按钮。此命令仅显示一个消息框并在 UserControl1ViewModel 中实现。

当我在我的 MainWindow 中使用 UserControl1 并且也有它的视图模型 (MainWindowViewModel) 时,我想使用绑定到 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 正常工作,我需要做什么?

4

1 回答 1

1

默认控制inherits DataContext from its parent除非你设置它explicitly。在您的情况下,您将 UserControl 的 DataContext 显式设置为

DataContext = vm = new UserControl1ViewModel();

这使得 UserControl 上的所有绑定都在类UserControl1ViewModel中而不是在MainWindowViewModel.

这就是为什么您必须使用RelativeSource获取 Window 的 DataContext ieyou 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。

或者

您可以为窗口命名并使用ElementName-

<Window x:Class="WpfApplication1.MainWindow"
        x:Name="MainWindow"> <--- HERE
<Grid>
        <my:UserControl1 LabelText="{Binding
                                       Path=DataContext.LabelTextFromMainWindow, 
                                       ElementName=MainWindow}"/>
于 2013-08-05T17:07:11.870 回答