2010-02-27 11 views
6

O no veo la solución o encuentro una trampa al usar MVVM.Una falla de MVVM con el escenario de detalles maestros

que tienen esta muestra Maestro-Detalle:

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} 
} 

Asumamos en mis CustomerOrdersViewModel mis clientes ObservableCollection está obligado a la vista a través de ... = "{Binding} Clientes" y cuando el cliente está cambiado de el usuario los Pedidos relacionados se muestran en DataGrid a través de ItemsSource = "{Binding SelectedItem.Orders, ElementName = comboboxCustomer}".

Esto es posible con MVVM:

que pueda añadir un nuevo cliente simplemente (para simplificar) llamando Customers.Add(new Customer(){...});.

Después de la adición hago esto: this.RaisePropertyChanged("Customers");. Esto actualizará la vista e inmediatamente mostrará al Cliente en el Combobox del Cliente.

Ahora viene la parte imposible con MVVM.

puedo añadir una nueva Orden por SelectedCustomer.Orders.Add(New Order(){...});

PERO no puede plantear un evento CollectionChanged/PropertyChanged como antes con los clientes ahora a las órdenes debido a las órdenes de la propiedad no está unida a la vista a través de descriptor de acceso público .

Incluso si iba a exponer a órdenes propiedad que puede vincularse a la vista, la vista en sí se preocupa por el Maestro-Detalle de conmutación no es el modelo de vista ...

PREGUNTA

¿Cómo es posible hacer Maestro -Trabajo detallado con objetos Add/Del en Details-List y actualización inmediata en la vista?

Respuesta

4

Esto siempre es difícil cuando se trabaja con vistas de detalles maestros. Sin embargo, una opción suele ser aprovechar INotifyPropertyChanged e INotifyCollectionChanged, y rastrearlos usted mismo en ViewModel. Al rastrear estas propiedades en sus objetos, puede manejar las notificaciones correctamente.

I blogged about a similar issue, donde quería que ocurriera la agregación en la lista "maestra" basada en valores en el panel de detalles (es decir, mostrar un n. ° total de pedidos, que siempre estarían actualizados). Los problemas son idénticos.

Puse un poco de working code up on the Expression Code Gallery demostrando cómo puede manejar este seguimiento, y hacer que todo permanezca actualizado en tiempo real, sin dejar de ser "puro" en términos de MVVM.

+1

He revisado su código y debo decir que es demasiado complicado. WPF es bueno. MVVM simplifica mucho las cosas en WinForms. Entity Framework no es posible con MVVM, es una broma si me preguntas. Intenta hacer una carga ansiosa con MVVM, entonces ya sabes a qué me refiero. Cada LOB tiene un montón de detalles maestros. Ahora sé por qué cada muestra de MVVM es una demostración estúpida y sencilla de la lista Mostrar todos los clientes ... Si conoce otra muestra MVVM Master-Detail, le agradecería un enlace :) Para aquellos interesados ​​en ese asunto también: http://www.codeproject.com/KB/WPF/WpfNhibernateToolkit.aspx – msfanboy

+1

MVVM se ocupa mucho de la relación entre View y ViewModel pero acerca de la relación VM to Model es totalmente silencioso. ¿Por qué? ¿Obtiene Datos de su DAL y lee clientes relacionados, pedidos, productos en 3 ObservableCollection ? Este es un gran esfuerzo para crear un tipo de contexto de entityViewModel ... parece que demasiados desarrolladores han jugado con las herramientas de diseño VS2010 wpf RAD para realizar aplicaciones LOB reales que necesitan mucho más ... – msfanboy

+0

Ahh, pero no estoy de acuerdo aquí. Sí, el código es complicado, pero es completamente reutilizable. Usarlo es simplemente arrastrar un comportamiento a su lista maestra, y "simplemente funciona". –

0

Recientemente nos hemos enfrentado a un problema similar, pero con el requisito adicional de que el Modelo consiste en simples POCOs estúpidas.

Nuestra solución es aplicar brutalmente la separación Model-ViewModel. Ni el modelo ni ViewModel contienen un ObservableCollection<ModelEntity>, en su lugar el modelo contiene una colección POCO y el modelo de vista contiene un ObservableCollection<DetailViewModel>.

Eso resuelve fácilmente Agregar, Obtener y Actualizar. Además, si solo el Máster elimina un detalle de su colección, se desencadenan los eventos correctos. Sin embargo, si el detalle solicita eliminarse, necesariamente debe indicar al maestro (el propietario de la colección).

Esto se puede hacer por abusar de un evento PropertyChanged:

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 } 
} 

su lugar se podría introducir un public event Action<DetailViewModel> Delete; en el DetailViewModel, se unen directamente a que MasterViewModel::Delete, etc.

La desventaja de este enfoque es que tienes que construir muchos ViewModels que quizás nunca necesiten más que su Nombre, por lo que realmente necesitas mantener la construcción de ViewModels a un precio económico y asegurarte de que la lista no explote.

Al revés, puede asegurarse de que la interfaz de usuario solo se una a los objetos de ViewModel y puede mantener una gran cantidad de goop INotifyPropertyChanged fuera de su modelo, que le da un corte limpio entre las capas.

Cuestiones relacionadas