WPF Список ViewModels, привязанных к списку объектов Model - PullRequest
2 голосов
/ 18 мая 2010

В модели у меня есть:

public ObservableCollection<Item> Items { get; private set; }

В ViewModel у меня есть соответствующий список ItemViewModel. Я хотел бы, чтобы этот список был двусторонне связан со списком модели:

public ObservableCollection<ItemViewModel> ItemViewModels ...

В XAML я свяжу (в данном случае TreeView) со свойством ItemViewModels.

У меня вопрос, что входит в "..." в ViewModel, показанном выше? Я надеюсь, что одна или две строки кода будут связывать эти две ObservableCollections (предоставляя тип ViewModel для построения для каждого объекта модели). Тем не менее, я боюсь, что необходим набор кода для обработки события Items.CollectionChanged и ручного обновления списка ItemViewModels, при необходимости создавая ViewModels, и соответствующую противоположность, которая обновит коллекцию Items на основе изменений в ItemViewModels.

Спасибо!

Эрик

Ответы [ 2 ]

5 голосов
/ 18 мая 2010

Вы можете использовать следующий класс:

public class BoundObservableCollection<T, TSource> : ObservableCollection<T>
{
    private ObservableCollection<TSource> _source;
    private Func<TSource, T> _converter;
    private Func<T, TSource, bool> _isSameSource;

    public BoundObservableCollection(
        ObservableCollection<TSource> source,
        Func<TSource, T> converter,
        Func<T, TSource, bool> isSameSource)
        : base()
    {
        _source = source;
        _converter = converter;
        _isSameSource = isSameSource;

        // Copy items
        AddItems(_source);

        // Subscribe to the source's CollectionChanged event
        _source.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_source_CollectionChanged);
    }

    private void AddItems(IEnumerable<TSource> items)
    {
        foreach (var sourceItem in items)
        {
            Add(_converter(sourceItem));
        }
    }

    void _source_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                AddItems(e.NewItems.Cast<TSource>());
                break;
            case NotifyCollectionChangedAction.Move:
                // Not sure what to do here...
                break;
            case NotifyCollectionChangedAction.Remove:
                foreach (var sourceItem in e.OldItems.Cast<TSource>())
                {
                    var toRemove = this.First(item => _isSameSource(item, sourceItem));
                    this.Remove(toRemove);
                }
                break;
            case NotifyCollectionChangedAction.Replace:
                for (int i = e.NewStartingIndex; i < e.NewItems.Count; i++)
                {
                    this[i] = _converter((TSource)e.NewItems[i]);
                }
                break;
            case NotifyCollectionChangedAction.Reset:
                this.Clear();
                this.AddItems(_source);
                break;
            default:
                break;
        }
    }
}

Используйте его следующим образом:

var models = new ObservableCollection<Model>();
var viewModels =
    new BoundObservableCollection<ViewModel, Model>(
        models,
        m => new ViewModel(m), // creates a ViewModel from a Model
        (vm, m) => vm.Model.Equals(m)); // checks if the ViewModel corresponds to the specified model

BoundObservableCollection будет обновляться, когда ObservableCollection изменится, но не наоборот (для этого придется переопределить несколько методов)

2 голосов
/ 18 мая 2010

Да, ваши опасения верны, вам придется обернуть все функции ObservableCollection.

Мой ответ на вопрос: зачем вам уже иметь обертку для модели представления, которая кажется хорошей моделью? Модель представления полезна, если ваша модель данных основана на некоторой несвязываемой бизнес-логике. Обычно этот уровень бизнес / данных имеет один или два способа извлечения данных и уведомления внешних наблюдателей об их изменениях, которые легко обрабатываются моделью представления и преобразуются в изменения в ObservableCollection. Фактически, в .NET 3.5 ObservableCollection был частью WindowsBase.dll , поэтому обычно он не будет использоваться в моделях данных.

Мое предложение - либо логика, которая заполняет / изменяет ObservableCollection, должна быть перемещена из вашей модели данных в модель представления, либо вы должны просто привязаться непосредственно к слою, который вы в настоящее время называете моделью данных, и просто назвать его тем, чем он является. Модель вида.

Очевидно, что вы можете написать вспомогательный класс, который будет синхронизировать две коллекции с использованием некоторых лямбда-преобразователей (от Item до ItemViewModel и обратно) и использовать его повсюду, как это (хотя убедитесь, что вы обрабатываете уникальность элемента правильно) Однако, IMHO, этот подход порождает избыточное количество классов-оболочек, и каждый уровень снижает функциональность и добавляет сложность. Что совершенно противоположно целям MVVM.

...