programing

MVVM 및 VM 컬렉션

muds 2023. 4. 16. 15:56
반응형

MVVM 및 VM 컬렉션

일반적인 세나리오:항목 모델 컬렉션이 있는 모델입니다.
예: 사람 컬렉션이 있는 집.

MVVM에 대해 올바르게 구성하는 방법 - 특히 모델 및 ViewModel 컬렉션을 추가 및 삭제하여 업데이트하는 방법에 대해 설명합니다.

모델House모델의 컬렉션을 포함합니다.People(예:List<People>).
모델 표시HouseVM축소된 House 개체와 관찰 가능한 뷰 모델 컬렉션이 포함됩니다.PeopleVM(ObservableCollection<PeopleVM>주의하세요. 우리는 결국 하원과 함께 하게 됩니다.2개의 컬렉션을 보유하고 있는 VM(동기화가 필요):
1. HouseVM.House.List<People>
2. HouseVM.ObservableCollection<PeopleVM>

House가 새로운 People(추가) 또는 People Leave(제거)로 업데이트되면 해당 이벤트는 Model House People(모델 하우스) People(피플) 컬렉션과 VM HouseVM Observable(VM 관찰 가능) 컬렉션 모두에서 처리되어야 합니다.

이 구조가 MVVM이 맞습니까?
추가 및 삭제에 대해 이중 업데이트를 하지 않아도 되는 방법이 있습니까?

일반적인 접근 방식은 완벽하게 양호한 MVVM입니다. View Model을 사용하여 다른 View Model 컬렉션을 표시하는 것은 매우 일반적인 시나리오이며, 저는 어디에서나 사용하고 있습니다.nicodemus13처럼 ViewModel에서 직접 아이템을 공개하는 것은 권장하지 않습니다.그 사이에 ViewModel이 없는 모델에 대한 뷰 바인딩이 이루어지기 때문입니다.첫 번째 질문에 대한 답변은 다음과 같습니다. 예, 이것은 유효한 MVVM입니다.

두 번째 질문에서 다루고 있는 문제는 주택 모델의 사람 모델과 주택 View Model의 사람 목록 간의 동기화입니다.이거 수동으로 해야 돼요.그래서 피할 방법이 없어요.

여기에 이미지 설명 입력

할 수 있는 일: 커스텀 구현ObservableCollection<T>,ViewModelCollection<T>기본 컬렉션에 대한 변경을 푸시합니다.양방향 동기화를 얻으려면 모델의 컬렉션을 Observable Collection <>으로 만들고CollectionChanged이벤트를 표시합니다.

이것이 나의 실장입니다.View Model Factory 서비스 등을 사용하고 있습니다만, 일반적인 프린서펄을 봐 주세요.도움이 됐으면 좋겠는데...

/// <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
    where TModel : class

{
    private readonly object _context;
    private readonly ICollection<TModel> _models;
    private bool _synchDisabled;
    private readonly IViewModelProvider _viewModelProvider;

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="models">List of models to synch with</param>
    /// <param name="viewModelProvider"></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, IViewModelProvider viewModelProvider, object context = null, bool autoFetch = true)
    {
        _models = models;
        _context = context;

        _viewModelProvider = viewModelProvider;

        // 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 (var 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;

        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                foreach (var m in e.NewItems.OfType<IViewModel>().Select(v => v.Model).OfType<TModel>())
                    _models.Add(m);
                break;

            case NotifyCollectionChangedAction.Remove:
                foreach (var m in e.OldItems.OfType<IViewModel>().Select(v => v.Model).OfType<TModel>())
                    _models.Remove(m);
                break;

            case NotifyCollectionChangedAction.Reset:
                _models.Clear();
                foreach (var m in e.NewItems.OfType<IViewModel>().Select(v => v.Model).OfType<TModel>())
                    _models.Add(m);
                break;
        }

        //Enable synchronization
        _synchDisabled = false;
    }

    private void ModelCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (_synchDisabled) return;
        _synchDisabled = true;

        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                foreach (var m in e.NewItems.OfType<TModel>()) 
                    this.AddIfNotNull(CreateViewModel(m));
                break;

            case NotifyCollectionChangedAction.Remove:
                    foreach (var m in e.OldItems.OfType<TModel>()) 
                        this.RemoveIfContains(GetViewModelOfModel(m));
                break;

            case NotifyCollectionChangedAction.Reset:
                Clear();
                FetchFromModels();
                break;
        }

        _synchDisabled = false;
    }

    private TViewModel CreateViewModel(TModel model)
    {
        return _viewModelProvider.GetFor<TViewModel>(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));
    }
}

이 상황에서 나는 단지 모델을 노출시킬 뿐이다.ObservableCollection보다 s가 아니라Lists. 그렇지 않은 특별한 이유는 없습니다.ObservableCollection에 있습니다.System.Collections.ObjectModel「」의 .System . 거의 확실히 있습니다.System★★★★★★★★★★★★★★★★★★.Listmscorlib하지만 그건 역사적 유물이야

하면 대폭 에 저는 모델-뷰모델의 을 사용하다List모델상의 s는 많은 불쾌한 보일러 플레이트 코드를 생성합니다.★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

왜 은 리고?? also?HouseVM wrappingObservableCollection<PeopleVM> 보다ObservableCollection<People> 것은 이든 간에, VM에 바인드 되어 있는 것은 이든 좋다고 ObservableCollection<PeopleVM> 이 있다People그렇지 않으면 바인딩 내 바인딩이 되거나 이 기능이 유용한 특별한 이유가 있습니까?일반적으로 VM이 다른 VM에 노출되는 일은 없지만 저만 노출될 수 있습니다.

라이브러리/WCF 정보 편집

라이브러리에 모델이 있거나 WCF 서버에 의해 공개되는 것이 이벤트를 발생시킬지 여부에 영향을 미치는 이유를 알 수 없습니다(WCF 서비스는 이벤트를 직접 공개하지 않습니다).에 들지 않으면,갱신을 안 만, 으로 하고 의문입니다.ObservableCollection제가 오해한 게 아니라면요

개인적으로 말씀드린 것처럼 VM을 단순하게 유지하고 다른 VM을 노출하지 않고 최소로 노출하도록 하겠습니다.일부 재설계가 필요하며 특정 부품은 다소 번거로울 수 있습니다(예:Converter단, 심플하고 관리하기 쉬운 디자인으로 끝부분이 쉽게 짜여집니다.

내가 보기엔 당신의 현재 행로가 매우 복잡해지고, 가장 중요한 것은, 따라가기가 어색할 것 같습니다.하지만 YMMV는 제 경험일 뿐입니다.

논리 중 일부를 명시적 서비스로 옮기는 것이 도움이 될 수 있을까요?

언급URL : https://stackoverflow.com/questions/15830008/mvvm-and-collections-of-vms

반응형