Создайте составной DataContext в XAML для Usercontrol - PullRequest
0 голосов
/ 22 июля 2011

Я пытаюсь создать композитный DataContext для UserControl.В основном у меня есть элемент управления, который имеет свойства Order и Package, и я хотел создать составной объект, представляющий этот источник данных в 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));
}

Order и Package не связаныправильно и просто нулевые.

Да, я знаю, что, вероятно, есть лучший способ сделать это - но я не могу понять, почему это не работает.Иногда в Blend это работает, а затем снова становится пустым.

1 Ответ

1 голос
/ 22 июля 2011

Это не будет работать, потому что DependencyObject (OrderPackagePair class) не отслеживает внутренние изменения своих свойств зависимостей. Поскольку объект 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 - обработчик никогда не будет вызван.

Итак, я полагаю, ваш код не работает из-за других ошибок. Вы должны тщательно работать с 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}"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...