Шаблон MVVM, вопрос ViewModel DataContext

Мне нужно выяснить, как взаимодействовать между ViewModels. Я новичок в MVVM, поэтому будьте добры.

Вот упрощенный пример

определения классов (предположим, что я подключил событие Child.PropertyChanged в ParentViewModel):

public class ParentViewModel : ViewModelBase
{
    public ChildViewModel Child { get; set; }
}

public class ChildViewModel : ViewModelBase
{
    String _FirstName;
    public String FirstName 
    {
        get { return _FirstName; }
        set
        {
            _FirstName = value;
            OnPropertyChanged("FirstName");
        }
    }
}

Вот что вы видите в словаре ресурсов

<DataTemplate DataType="{x:Type vm:ParentViewModel}">
    <vw:ParentView/>
</DataTemplate>

<DataTemplate DataType="{x:Type vm:ChildViewModel}">
    <vw:ChildView/>
</DataTemplate>

и код программной части ChildView:

public partial class ChildView : UserControl
{
    public QueueView()
    {
        InitializeComponent();
        DataContext = new ChildViewModel();
    }
}

Очевидная проблема заключается в том, что при создании экземпляра ChildView (путем выбора из DataTemplate) он создает новый класс ChildViewModel, а ParentViewModel не имеет к нему доступа.

Итак, как я могу создать экземпляр DataContext представления как исходную модель представления, которая вызвала выбор DataTemplate?

Очевидное исправление - объединить свойства из ChildViewModel в ParentViewModel, но я бы предпочел разделить их, потому что для повторного использования.

Я уверен, что ответ банален, я просто хотел бы знать, что это такое. :)

Заранее спасибо.


person Jose    schedule 05.05.2009    source источник
comment
Кстати, я предполагаю, что вы изменили имена своих классов, чтобы упростить пример ... Вместо этого имя класса для ChildView в коде позади - QueueView.   -  person Josh G    schedule 05.05.2009
comment
Да, это была опечатка. простите :)   -  person Jose    schedule 07.05.2009


Ответы (3)


Вам следует просто удалить строку:

DataContext = new ChildViewModel();

DataContext представления будет автоматически установлен WPF. DataTemplates для контекста данных всегда заданы данные для шаблона (в данном случае ViewModel):

<DataTemplate DataType="{x:Type vm:ChildViewModel}">
    <vw:ChildView/>
</DataTemplate>

Конечным результатом является то, что вы можете создавать объекты модели представления отдельно (как родительский, так и дочерний классы), а затем отображать их позже, просто вставляя их в элементы управления содержимым.

person Josh G    schedule 05.05.2009
comment
Если намерение состоит в том, чтобы ParentView содержал ChildView, как только это устанавливает для DataContext для ChildView значение ParentViewModel.Child? - person lightw8; 06.05.2009
comment
Что ж, сценарий, вероятно, будет выглядеть примерно так: в методе factory или builder вы должны создать родительский и дочерний объекты. Этот объект будет передан на просмотр. - person Josh G; 06.05.2009
comment
Представление могло бы отображать родительский элемент в одном элементе управления содержимым и дочерний элемент в другом, или оно отображало бы как родительский, так и дочерний элемент в элементе управления HeaderedContent. - person Josh G; 06.05.2009

Самый простой способ связи между ViewModels с использованием подхода MVVM - использовать шаблон посредника (EventAggregator в Prism). Хороший пример такого подхода можно увидеть по следующим ссылкам:

  1. Шаблон посредника MVVM от Саши Барбер
  2. Посредник MVVM + от Марлона греч.

Также ознакомьтесь с MVVM образец концепции проекта.

person Soni Ali    schedule 06.05.2009

Допустим, у вас есть QueueView, который использует QueueViewModel.

public class QueueViewModel : INotifyPropertyChanged
{
    public ParentType Parent { get; set; }

    public QueueViewModel(ParentType parent)
    {
        this.Parent = parent;
        foreach (ChildType child in Parent)
        {
            child.PropertyChanged += delegate(object sender,
                PropertyChangedEventArgs e)
            {
                if (e.PropertyName != "IsSelected")
                    return;

                //do something like this:
                Parent.IsSelected = AllChildrenAreSelected();
            };
        }
    }

}

public class ParentType : INotifyPropertyChanged
{
    private bool _isSelected;

    public IList<ChildType> Children { get; set; }
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            _isSelected = value;
            OnPropertyChanged("IsSelected");
        }
    }
}

public class ChildType : INotifyPropertyChanged
{
    private string _name;
    private bool _isSelected;

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        }
    }

    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            _isSelected = value;
            OnPropertyChanged("IsSelected");
        }
    }
}

- Часть QueueView

<StackPanel>
<CheckBlock Text="{Binding Path=Parent.Name}" 
            IsChecked="{Binding Parent.IsSelected}"/>
<ItemsControl ItemsSource="{Binding Path=Parent.Children}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>                                    
            <CheckBox Content="{Binding Path=Name}"
                      IsChecked="{Binding Path=IsSelected, Mode=TwoWay}"/>
        </DataTemplate>
    <ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
person Soni Ali    schedule 05.05.2009
comment
Кажется, что ваши образцы кода не имеют отношения к вопросу. Один или оба из нас упустили суть? - person Josh G; 06.05.2009
comment
Я предполагал, что дочерняя модель была свойством родительской модели. (например, заказ + список деталей заказа). В этом случае вы можете использовать уведомления об изменении свойств для взаимодействия в модели представления. Но если это отдельные модели представления, вы можете использовать шаблон посредника для связи. - person Soni Ali; 06.05.2009