在XAML中为Usercontrol创建复合DataContext

时间:2011-07-22 04:30:39

标签: wpf dependency-properties

我正在尝试为UserControl创建复合DataContext。基本上我有一个具有OrderPackage属性的控件,我想在XAML而不是代码中创建表示此数据源的复合对象。

这就是我尝试显示UserControl(并创建DataContext)的方法:

<views:PackageDetailsControl>
    <views:PackageDetailsControl.DataContext>
        <vm:OrderPackagePair Package="{Binding Package, Mode=OneWay}" 
                             Order="{Binding Order, Mode=OneWay}"/>                 
    </views:PackageDetailsControl.DataContext>
</views:PackageDetailsControl>  

OrderPackagePair对象是在XAML中创建的简单依赖项对象:

public class OrderPackagePair : DependencyObject
{
    public OrderDetails Order
    {
        get { return (OrderDetails)GetValue(OrderProperty); }
        set { SetValue(OrderProperty, value); }
    }

    public static readonly DependencyProperty OrderProperty =
        DependencyProperty.Register("Order", typeof(OrderDetails), typeof(OrderPackagePair), new UIPropertyMetadata(null));

    public PackageInfo Package
    {
        get { return (PackageInfo)GetValue(PackageProperty); }
        set { SetValue(PackageProperty, value); }
    }

    public static readonly DependencyProperty PackageProperty =
        DependencyProperty.Register("Package", typeof(PackageInfo), typeof(OrderPackagePair), new UIPropertyMetadata(null));
}

OrderPackage没有正确绑定,只是空。

是的我知道可能有更好的方法 - 但我无法理解为什么这不起作用。偶尔在Blend中它会工作然后再次变为空白。

1 个答案:

答案 0 :(得分:1)

这不起作用,因为DependencyObjectOrderPackagePair类)不监视其依赖项属性的内部更改。由于OrderPackagePair对象保持不变,因此DataContext被视为未更改。

在相反的站点上,类Freezable旨在通知订户当其中一个依赖项属性发生更改时实例已更改。

因此,请尝试将Freezable而不是DependencyObject声明为OrderPackagePair的基类。

-------------更新--------

是的,它有效。为了证明这一点,我已经实现了简单的例子。

OrderPackagePairClass代码:

public class OrderPackagePair : Freezable
{
    public OrderDetails Order
    {
        get { return (OrderDetails)GetValue(OrderProperty); }
        set { SetValue(OrderProperty, value); }
    }

    public static readonly DependencyProperty OrderProperty =
        DependencyProperty.Register("Order", typeof(OrderDetails), typeof(OrderPackagePair), new UIPropertyMetadata(null));

    public PackageInfo Package
    {
        get { return (PackageInfo)GetValue(PackageProperty); }
        set { SetValue(PackageProperty, value); }
    }

    public static readonly DependencyProperty PackageProperty =
        DependencyProperty.Register("Package", typeof(PackageInfo), typeof(OrderPackagePair), new UIPropertyMetadata(null));

    protected override Freezable CreateInstanceCore()
    {
        throw new NotImplementedException();
    }
}

XAML:

<Window x:Class="WindowTest.MainWindow"
        xmlns:self="clr-namespace:WindowTest"
        Name="RootControl">
    <StackPanel Margin="10" DataContextChanged="StackPanel_DataContextChanged">
        <StackPanel.DataContext>
            <self:OrderPackagePair Package="{Binding Path=DataContext.PackageInfo, Mode=OneWay, ElementName=RootControl}" 
                                   Order="{Binding Path=DataContext.OrderDetails, Mode=OneWay, ElementName=RootControl}"/>
        </StackPanel.DataContext>

        <Button Margin="10" Content="Change Package" Click="Button_Click"/>
    </StackPanel>
</Window> 

代码背后:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    private OrderDetails _orderDetails;
    public OrderDetails OrderDetails
    {
        get
        {
            return this._orderDetails;
        }
        set
        {
            this._orderDetails = value;
            this.OnPropertyChanged("OrderDetails");
        }
    }

    private PackageInfo _packageInfo;
    public PackageInfo PackageInfo
    {
        get
        {
            return this._packageInfo;
        }
        set
        {
            this._packageInfo = value;
            this.OnPropertyChanged("PackageInfo");
        }
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        this.PackageInfo = new PackageInfo(DateTime.Now.ToString());
    }

    private void StackPanel_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        Trace.WriteLine("StackPanel.DataContext changed");
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string name)
    {
        var safeEvent = this.PropertyChanged;
        if (safeEvent != null)
        {
            safeEvent(this, new PropertyChangedEventArgs(name));
        }
    }
}

当您单击按钮时,模型更改PackageInfo属性(为简单模型和视图在同一个类中实现)。依赖属性OrderPackagePair.Package对新值作出反应并覆盖其值。由于Freezable性质,OrderPackagePair会通知所有订阅者它已被更改并调用处理程序StackPanel_DataContextChanged。如果你回到DependencyObject作为OrderPackagePair的基类 - 将永远不会调用handler。

所以,我认为你的代码因其他错误而无效。您应该仔细使用DataContext。例如,您写道:

<views:PackageDetailsControl>
    <views:PackageDetailsControl.DataContext>
        <vm:OrderPackagePair Package="{Binding Package, Mode=OneWay}" 
                             Order="{Binding Order, Mode=OneWay}"/>                 
    </views:PackageDetailsControl.DataContext>
</views:PackageDetailsControl>

当然这是其中一个问题。绑定表达式面向当前的DataContext。但是您将DataContext设置为OrderPackagePair实例。所以你将OrderPackagePair.Package绑定到OrderPackagePair.Package(我想,你的目标是将OrderPackagePair.Package绑定到Model.Package)。这就是为什么没有发生的原因。

在我的绑定表达式示例中,我明确告诉我要绑定哪个DataContext:

 Package="{Binding Path=DataContext.PackageInfo, Mode=OneWay, ElementName=RootControl}"