【问题标题】:DataBinding problems with custom UserControl自定义 UserControl 的数据绑定问题
【发布时间】:2013-08-05 16:47:56
【问题描述】:

在自定义 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}}}"

我必须做什么才能让简单的绑定正常工作?

【问题讨论】:

    标签: wpf data-binding custom-controls


    【解决方案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。

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

    <Window x:Class="WpfApplication1.MainWindow"
            x:Name="MainWindow"> <--- HERE
    <Grid>
            <my:UserControl1 LabelText="{Binding
                                           Path=DataContext.LabelTextFromMainWindow, 
                                           ElementName=MainWindow}"/>
    

    【讨论】:

    • 感谢您的解释,我自己找到了更好的解决方案。如果我的 UserControl1 没有 DataContext,但第一个子项(在本例中为名称为“DataContextHolder”的网格),我可以得到我正在寻找的内容。 公共部分类 UserControl1 : UserControl { public UserControl1() { DataContextHolder.DataContext = new UserControl1ViewModel() ; } }
    • 是的,这是一种 hack,但足以满足您的问题陈述。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多