Как вспомнить изменения в моей иерархии ViewModel? - PullRequest
4 голосов
/ 08 июня 2009

My MainView.xaml содержит мой SmartForm View:

<Grid Margin="10">
    <views:SmartForm/>
</Grid>

представление SmartForm загружает ItemsControl

<Grid Margin="10">
    <ItemsControl
        ItemsSource="{Binding DataTypeViews}"/>
</Grid>

, который является наблюдаемой коллекцией DataTypeViews:

List<FormField> formFields = new List<FormField>();
formFields.Add(new FormField { IdCode = "firstName", Label = "First Name", Value = "Jim" });
formFields.Add(new FormField { IdCode = "lastName", Label = "Last Name", Value = "Smith" });
formFields.Add(new FormField { IdCode = "address1", Label = "Address 1", Value = "123 North Ashton Rd." });
formFields.Add(new FormField { IdCode = "address2", Label = "Address 2", Value = "Box 23434" });
formFields.Add(new FormField { IdCode = "city", Label = "City", Value = "New Haven" });
formFields.Add(new FormField { IdCode = "state", Label = "State", Value = "NM" });
formFields.Add(new FormField { IdCode = "zip", Label = "Zip Code", Value = "34234" });

foreach (FormField formField in formFields)
{
    DataTypeView dtv = new DataTypeView();
    DataTypeViewModel dtvm = new DataTypeViewModel(formField);
    dtv.DataContext = dtvm;
    DataTypeViews.Add(dtv);
}

и в каждом представлении отображается метка и текстовое поле, в котором создается форма:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="90"/>
        <ColumnDefinition Width="400"/>
    </Grid.ColumnDefinitions>
    <StackPanel Orientation="Horizontal" Grid.Column="0">
        <TextBlock Text="{Binding Label}" FontSize="14"/>
        <TextBlock Text=": " FontSize="14"/>
    </StackPanel>
    <TextBox Grid.Column="1" Text="{Binding Value}" FontSize="12"/>
</Grid>

Как передать изменения Textbox, которые происходят в DataTypeViewModel, в SmartFormViewModel?

Или другими словами: Если ViewModel A содержит коллекцию ViewModel B, и изменение происходит в ViewModel B, как я могу всплыть, что это изменение до ViewModel A?

Ответы [ 5 ]

8 голосов
/ 15 июня 2009

Я думаю, вам следует использовать шаблон-посредник , о котором вы можете прочитать здесь .

По сути, это статический класс, который позволяет ViewModels (или любому другому классу) взаимодействовать друг с другом и передавать аргументы туда и обратно.

В основном ViewModel A начинает прослушивание определенного типа сообщения (например, ViewModelBChanged), и всякий раз, когда происходит это событие, ViewModelB просто уведомляет любого, кто прослушивает этот тип сообщения, он также может передавать любую информацию, которую хочет.

Вот скелет посредника.

public static class MyMediator
{
    public static void Register(Action<object> callback, string message);

    public static void NotifyColleagues(string message, object args);
}

ViewModel A сделает это (вероятно, в конструкторе):

MyMediator.Register(ProcessMessage,"ViewModelBChanged")

и затем должен был бы объявить такую ​​функцию:

void ProcessMessage(object args)
{
    //Do some important stuff here
}

и ViewModel B будет вызывать это всякий раз, когда захочет сообщить ViewModel A

MyMediator.NotifyColleagues("ViewModelBChanged",this);

Класс-посредник будет отвечать за вызов функции обратного вызова viewModel A. И тогда все будут счастливы.

Лично мне нравится помещать эти строковые значения сообщений в статический класс, подобный этому

static class MediatorMessages
{
    public static string ViewModelBChanged= "ViewModelBChanged";
}

Чтобы вы могли сделать следующее (вместо вышеописанного):

 MyMediator.Register(ProcessMessage,MediatorMessages.ViewModelBChanged)
 MyMediator.NotifyColleagues(MediatorMessages.ViewModelBChanged,this);

Если это неясно, просто воспользуйтесь посредником Google MVVM и кликните на понравившийся контент:)

5 голосов
/ 08 июня 2009

Вы можете просто подключить родительскую виртуальную машину к событию PropertyChanged на дочерних виртуальных машинах. Это своего рода PITA, чтобы отслеживать детей, которые были добавлены / удалены и так далее, так что вы можете вместо этого рассмотреть вопрос о сохранении ваших дочерних виртуальных машин в моей ItemObservableCollection:

public sealed class ItemObservableCollection<T> : ObservableCollection<T>
    where T : INotifyPropertyChanged
{
    public event EventHandler<ItemPropertyChangedEventArgs<T>> ItemPropertyChanged;

    protected override void InsertItem(int index, T item)
    {
        base.InsertItem(index, item);
        item.PropertyChanged += item_PropertyChanged;
    }

    protected override void RemoveItem(int index)
    {
        var item= this[index];
        base.RemoveItem(index);
        item.PropertyChanged -= item_PropertyChanged;
    }

    protected override void ClearItems()
    {
        foreach (var item in this)
        {
            item.PropertyChanged -= item_PropertyChanged;
        }

        base.ClearItems();
    }

    protected override void SetItem(int index, T item)
    {
        var oldItem = this[index];
        oldItem.PropertyChanged -= item_PropertyChanged;
        base.SetItem(index, item);
        item.PropertyChanged -= item_PropertyChanged;
    }

    private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        OnItemPropertyChanged((T)sender, e.PropertyName);
    }

    private void OnItemPropertyChanged(T item, string propertyName)
    {
        ItemPropertyChanged.Raise(this, new ItemPropertyChangedEventArgs<T>(item, propertyName));
    }
}

Тогда ваша родительская виртуальная машина может просто прослушивать все изменения дочерних элементов с помощью:

_formFields.ItemPropertyChanged += (s, e) => Foo();
0 голосов
/ 09 июня 2009

Я решил это, просто передав саму ViewModel в содержащиеся в нем ViewModels, вот демонстрация, показывающая, как это делается .

0 голосов
/ 08 июня 2009

Хотя Кент прямо выше, не все изменения в дочерних моделях представления связаны со свойствами, некоторые могут быть немного более семантическими, чем это. В таких случаях целесообразно использовать вариант шаблона Цепочка ответственности .

Короче

  • Сообщите всем дочерним моделям представления об «главном объекте-обработчике», который имеет метод для обработки всех видов событий изменений. Связь с этим объектом может осуществляться через события или сообщения, в зависимости от сложности изменений.
  • Пусть этот главный объект-обработчик зарегистрирует коллекцию объектов-обработчиков, которые будут обрабатывать события изменения, по одному для каждого. Они могут быть объединены в цепочки, как в исходном шаблоне, или они могут быть зарегистрированы в быстрой коллекции (например, в словаре) для исполнения.
  • Заставить этот объект-обработчик отправить соответствующее изменение зарегистрированному обработчику.

«Главный» обработчик не обязательно должен быть Singleton, его реестр может зависеть от самой модели родительского представления.

Надеюсь, это достаточно ясно (извините, что не поставил код)

0 голосов
/ 08 июня 2009

Способ не-WPF - создать статическое событие в DataTypeViewModel. Это позволяет при необходимости вызывать событие из DataTypeViewModel (в установщике свойств или обработчике изменения свойств). Конечно, вам также нужно зарегистрировать слушателя события в SmartForm (для этого SmartForm необходимо знать тип DataTypeViewModel).

В качестве альтернативы, я думаю, вы могли бы создать свое собственное перенаправленное событие.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...