【问题标题】:MVVM sync List<string>MVVM 同步列表<字符串>
【发布时间】:2013-01-28 07:51:52
【问题描述】:

我认为this answer 是我的问题的解决方案,但我很难理解如何将其应用于我的问题。和其他帖子一样,我有两个想要保持同步的集合。我的模型对象有字符串集合:

public class Person {
    public int PersonId {get; set; }
    public string PersonName { get; set; }
    public List<string> PersonNicknames { get; set; }
}

我将此模型对象包装在它自己的 ViewModel (PersonViewModel) 中。为了允许编辑昵称,我还将它们包装在自己的 NicknameViewModel 中。 PersonViewModel 然后公开一个ObservableCollection&lt;NicknameViewModel&gt; NicknameViewModelCollection,它在构造时填充:

        foreach (string stringItem in _person.PersonNicknames)
        {
            var nicknameViewModel = new NicknameViewModel(stringItem);
            this.NicknameViewModelCollection.Add(nicknameViewModel);
        }

PersonViewModel.NicknameViewModelCollection 中添加、删除或更改字符串时,更改不会反映在模型集合中(即Person.Nicknames)。每当用户修改、编辑或删除字符串项时,我都需要更新模型集合。我不明白链接的答案是如何工作的或如何将其应用于这个问题。一个例子会很棒......我在这里不知所措。

【问题讨论】:

    标签: mvvm collections


    【解决方案1】:

    这是我针对您要搜索的内容的标准解决方案。它对您的场景有一些开销,因为它适用于具有 context 字段的 ViewModel 类型等。无论如何,解决方案应该变得显而易见。集合一般同步 OneWayToSource,如果模型集合本身是可观察的,则同步 TwoWay。这对你有帮助吗?如果没有,请询​​问...

    /// <summary>
    /// Observable collection of ViewModels that pushes changes to a related collection of models
    /// </summary>
    /// <typeparam name="TViewModel">Type of ViewModels in collection</typeparam>
    /// <typeparam name="TModel">Type of models in underlying collection</typeparam>
    
    public class VmCollection<TViewModel, TModel> : ObservableCollection<TViewModel>
        where TViewModel : class, IViewModel, new()
        where TModel : class
    
    {
        private readonly object _context;
        private readonly ICollection<TModel> _models;
        private bool _synchDisabled;
    
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="models">List of models to synch with</param>
        /// <param name="context"></param>
        /// <param name="autoFetch">
        /// Determines whether the collection of ViewModels should be
        /// fetched from the model collection on construction
        /// </param>
        public VmCollection(ICollection<TModel> models, object context = null, bool autoFetch = true)
        {
            _models = models;
            _context = context;
    
            // Register change handling for synchronization
            // from ViewModels to Models
            CollectionChanged += ViewModelCollectionChanged;
    
            // If model collection is observable register change
            // handling for synchronization from Models to ViewModels
            if (models is ObservableCollection<TModel>)
            {
                var observableModels = models as ObservableCollection<TModel>;
                observableModels.CollectionChanged += ModelCollectionChanged;
            }
    
    
            // Fecth ViewModels
            if (autoFetch) FetchFromModels();
        }
    
        /// <summary>
        /// CollectionChanged event of the ViewModelCollection
        /// </summary>
        public override sealed event NotifyCollectionChangedEventHandler CollectionChanged
        {
            add { base.CollectionChanged += value; }
            remove { base.CollectionChanged -= value; }
        }
    
        /// <summary>
        /// Load VM collection from model collection
        /// </summary>
        public void FetchFromModels()
        {
            // Deactivate change pushing
            _synchDisabled = true;
    
            // Clear collection
            Clear();
    
            // Create and add new VM for each model
            foreach (TModel model in _models)
                AddForModel(model);
    
            // Reactivate change pushing
            _synchDisabled = false;
        }
    
        private void ViewModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            // Return if synchronization is internally disabled
            if (_synchDisabled) return;
    
            // Disable synchronization
            _synchDisabled = true;
    
            // Synchronize collection of Models
            if (e.NewItems != null)
                foreach (var v in e.NewItems.OfType<IViewModel<TModel>>())
                    v.AddModelTo(_models);
            if (e.OldItems != null)
                foreach (var v in e.OldItems.OfType<IViewModel<TModel>>())
                    v.RemoveModelFrom(_models);
    
    
            //Enable synchronization
            _synchDisabled = false;
        }
    
        private void ModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (_synchDisabled) return;
    
            // Synchronize collection of ViewModels
            if (e.NewItems != null)
                foreach (TModel m in e.NewItems.OfType<TModel>()) this.AddIfNotNull(CreateViewModel(m));
            if (e.OldItems != null) foreach (TModel m in e.OldItems) this.RemoveIfContains(GetViewModelOfModel(m));
        }
    
        private TViewModel CreateViewModel(TModel model)
        {
            return ViewModelCache.Get<TViewModel>.ForExistingModel(model, _context);
        }
    
        private TViewModel GetViewModelOfModel(TModel model)
        {
            return Items.OfType<IViewModel<TModel>>().FirstOrDefault(v => v.IsViewModelOf(model)) as TViewModel;
        }
    
        /// <summary>
        /// Adds a new ViewModel for the specified Model instance
        /// </summary>
        /// <param name="model">Model to create ViewModel for</param>
        public void AddForModel(TModel model)
        {
            Add(CreateViewModel(model));
        }
    
        /// <summary>
        /// Adds a new ViewModel with a new model instance of the specified type,
        /// which is the ModelType or derived from the Model type
        /// </summary>
        /// <typeparam name="TSpecificModel">Type of Model to add ViewModel for</typeparam>
        public void AddNew<TSpecificModel>() where TSpecificModel : TModel, new()
        {
            var m = new TSpecificModel();
            Add(CreateViewModel(m));
        }
    }
    

    【讨论】:

    • 感谢您的回答。我认为 ViewModelCollectionChanged 是负责将更改推回模型集合的方法是否正确? AddModelTo 和 RemoveModelFrom 有什么作用?
    • 是的,ViewModelCollectionChanged 推送到模型集合。 AddModelTo 和 remove model from 是将模型模型集合作为参数并从该列表中删除模型的方法。这允许从模型集合中删除它们的模型,而无需在 ViewModel 中公开模型。这是可选的,但是,您可以直接更新模型集合。
    • 抱歉,还有一件事。到目前为止,如果添加或删除项目,我会同步集合。如果某个项目被编辑(即文本更改)并且我希望该更改反映在两个集合中怎么办?
    • 这实际上不是集合的问题,而是您同步的集合中的项目之间的关系。对于您的方案,模型集合中的项目需要实现 INotifyPropertyChanged。 ViewModel 可以订阅模型的 PropertyChanged 并对模型的更改做出反应(在标准情况下,它会为自己触发 PropertyChanged)。无论如何,ViewModel 中的更改都会转发给模型,因此包含在内...这是否回答了您的问题?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多