2010-02-27 4 views
6

솔루션이 표시되지 않거나 MVVM 사용시 함정을 발견했습니다.마스터 - 세부 시나리오를 사용하는 MVVM 함정

class Customer 
{ 
    int CustomerID {get;set} 
    string Name {get;set} 
    ObservableCollection<Order> Orders {get;set} 
} 

class Order 
{ 
    int OrderID {get;set} 
    int Quantity {get;set} 
    double Discount {get;set} 
} 

... =를 통해보기에 바인딩 내 CustomerOrdersViewModel 내 ObservableCollection에 고객의 가정 수 있습니다 "{고객 바인딩 ▣"고객이 변경되는 경우 :

나는이 샘플 마스터 - 세부가 관련된 Orders가 ItemsSource = "{Binding SelectedItem.Orders, ElementName = comboboxCustomer}"를 통해 DataGrid에 표시됩니다.

이 MVVM 가능합니다 :

내가 Customers.Add(new Customer(){...}); 전화 (단순성을 위하여) 간단하여 새로운 고객을 추가 할 수 있습니다.

내가 추가 한 후 : this.RaisePropertyChanged("Customers");. 그러면보기가 업데이트되고 Customer-Combobox에 고객이 즉시 표시됩니다.

이제 MVVM에서는 불가능한 부분이 있습니다.

나는 SelectedCustomer.Orders.Add(New Order(){...});

하여 새 주문을 추가 할 수 있지만, 주문이 속성은 대중 접근을 통해보기에 바인딩되지 않기 때문에 나는 주문 지금 전에 고객과 같은 CollectionChanged /하여 PropertyChanged 이벤트를 발생시킬 수 없습니다 . 내가보기에 바인딩 특성 주문 노출 될 수

하더라도, 뷰 자체는 가능 마스터를 만드는 것이 얼마나

질문

...하지 뷰 모델을 전환 마스터 - 세부 사항에 대한 관심 세부 정보 목록의 Add/Del 개체에 대한 세부 작업 및보기의 즉시 업데이트?

답변

4

마스터 - 디테일보기로 작업 할 때 항상 어렵습니다. 그러나 일반적으로 INotifyPropertyChanged 및 INotifyCollectionChanged를 활용하고 ViewModel에서이 옵션을 추적하는 옵션이 있습니다. 개체에서 이러한 속성을 추적하면 알림을 올바르게 처리 할 수 ​​있습니다.

I blogged about a similar issue 여기서 세부 정보 창의 값을 기준으로 "마스터"목록에서 집계가 발생합니다 (즉, 총 주문 수를 표시하면 항상 최신 상태로 유지됩니다). 문제는 동일합니다.

working code up on the Expression Code Gallery이 트래킹을 어떻게 처리 할 수 ​​있는지 보여 주며 모든 것을 실시간으로 최신 상태로 유지하면서 MVVM 용어로 "순수"상태를 유지합니다.

+1

나는 귀하의 코드를 점검했으며 너무 복잡하다고 말해야합니다. WPF가 좋습니다. MVVM은 WinForms에서 작업을 너무 복잡하게 만듭니다. 엔티티 프레임 워크는 MVVM에서 농담 할 수 없습니다. MVVM을 사용하여 Eager 로딩을 시도한 다음 의미를 알 수 있습니다. 모든 LOB에는 많은 Master-Detail이 있습니다. 이제 왜 모든 MVVM 샘플이 바보이며 일반인인지 알 수 있습니다. 모든 고객 목록 데모보기 ... 다른 MVVM 마스터 - 디테일 샘플을 알고 있다면 링크를 부탁드립니다 :) 그 문제에 대한 관심도 너무 높습니다. http://www.codeproject.com/KB/WPF/WpfNhibernateToolkit.aspx – msfanboy

+1

MVVM은 View와 ViewModel 사이의 관계를 많이 다루지 만 VM과의 관계는 모델을 완전히 조용하게 추측합니다. DAL에서 데이터를 가져 와서 ObservableCollection 의 관련 고객, 주문, 제품을 읽습니다. 이것은 일종의 entityViewModel 컨텍스트를 만드는 큰 노력입니다 ... VS2010 wpf로 플레이 한 개발자가 너무 많습니다. 실제 LOB 앱을 구현하는 RAD 디자이너 도구는 훨씬 더 많이 필요합니다 ... – msfanboy

+0

아 -하지만 여기에는 동의하지 않습니다. 예, 코드는 복잡하지만 완전히 재사용 할 수 있습니다. 그것을 사용하면 동작을 마스터 목록으로 끌어 오면 "그냥 작동합니다". –

0

우리는 최근 비슷한 문제에 직면했지만 모델은 일반 바보 같은 POCO로 구성된다는 추가 요구 사항이 있습니다.

우리의 솔루션은 잔인하게 모델 - 뷰 모델 분리를 적용하는 것입니다. 모델 또는 ViewModel에는 ObservableCollection<ModelEntity>이 포함되어 있지 않으며 모델에는 POCO 컬렉션이 포함되어 있으며 ViewModel에는 ObservableCollection<DetailViewModel>이 포함되어 있습니다.

이렇게하면 쉽게 추가, 가져 오기 및 업데이트를 해결할 수 있습니다. 또한 마스터 만이 컬렉션에서 세부 정보를 삭제하면 적절한 이벤트가 발생합니다. 그러나 세부 정보 요청을 삭제하려면 반드시 마스터 (컬렉션 소유자)에게 신호를 보내야합니다.

class MasterViewModel { 
    private MasterModel master; 
    private ISomeService service; 
    private ObservableCollection<DetailViewModel> details; 

    public ObservableCollection<DetailViewModel> Details { 
    get { return this.details; } 
    set { return this.details ?? (this.details = LoadDetails()); } 
    } 

    public ObservableCollection<DetailViewModel> LoadDetails() { 
    var details = this.service.GetDetails(master); 
    var detailVms = details.Select(d => 
     { 
     var vm = new DetailViewModel(service, d) { State = DetailState.Unmodified }; 
     vm.PropertyChanged += this.OnDetailPropertyChanged; 
     return vm; 
     }); 

    return new ObservableCollection<DetailViewModel>(detailVms); 
    } 

    public void DeleteDetail(DetailViewModel detailVm) { 
    if(detailVm == null || detailVm.State != DetailState.Deleted || this.details == null) { 
     return; 
    } 

    detailVm.PropertyChanged -= this.OnDetailPropertyChanged; 

    this.details.Remove(detailVm); 
    } 

    private void OnDetailPropertyChanged(object s, PropertyChangedEventArgs a) { 
    if(a.PropertyName == "State" & (s as DetailViewModel).State == DetailState.Deleted) { 
     this.DeleteDetail(s as DetailViewModel); 
    } 
    } 
} 

class DetaiViewModel : INotifyPropertyChanged { 
    public DetailState State { get; private set; } // Notify in setter.. 

    public void Delete() { 
    this.State = DetailState.Deleted; 
    } 

    public enum DetailState { New, Unmodified, Modified, Deleted } 
} 

대신 당신이 DetailViewModelpublic event Action<DetailViewModel> Delete;을 소개 수, 바인드 직접에 MasterViewModel::Delete

이 방법의 단점은 것입니다 :

PropertyChanged 이벤트를 남용하여 수행 할 수 있습니다 당신은 그들의 이름보다 더 많은 것을 위해 결코 필요 없을지도 모르는 많은 ViewModel을 생성해야한다. 그래서 당신은 ViewModels의 구축을 싼 값으로 유지해야하고리스트가 폭발하지 않도록해야한다.

위쪽에서 볼 때 UI가 ViewModel 객체에만 바인딩되도록하고 모델간에 INotifyPropertyChanged 힙을 많이 유지하여 레이어 사이를 깨끗하게 잘라낼 수 있습니다.

관련 문제