2013-05-06 17 views
5

IntroduzioneViewModels che parlano tra di loro senza un quadro

Ho un'applicazione che importa i dati degli strumenti di laboratorio mentre è in esecuzione. Questi dati vengono importati e quindi visualizzati in un ListView a un intervallo impostato dall'utente finale secondo i suoi requisiti di test. Quando un valore di interesse appare in questo ListView da loro guardato, quindi premere un pulsante Start e l'applicazione inizia a eseguire calcoli su quel dato e sui dati successivi finché non viene premuto un pulsante Stop. Quindi sul lato sinistro dello schermo c'è una vista per la visualizzazione dei dati importati e sul lato destro c'è un'altra vista per osservare i valori e le statistiche mentre vengono calcolati e visualizzati.

il codice corrente

The View che consente di visualizzare il ListView in cui i dati vengono importati è l'ImportProcessView.xaml e pone la sua DataContext al ImportProcessViewModel.cs. La VM che ho appena introdotto ha una proprietà ObservableCollection<IrData> che il ListView, ho anche appena descritto, si lega a. Ora per la parte interessante ...

Il ImportProcessView ha un ContentControl che imposta dinamicamente il contenuto di un UserControl che rappresenta i controlli e i campi specifici per il tipo di Fase scelto dall'utente finale.

<StackPanel Background="White" Margin="5"> 
    <ContentControl Content="{Binding CurrentPhaseView}"/> 
</StackPanel> 

ci sono tre PhaseViews, ciascuno nel proprio controllo utente e ogni imposta è DataContext al ImportProcessViewModel. Di conseguenza sto ottenendo un rigonfiamento della VM grave per un totale di 2000 linee. Ridicolo. Lo so. Il motivo per il rigonfiamento è dovuto al fatto che lo ImporProcessViewModel sta mantenendo lo stato tramite le proprietà per ciascuna delle tre PhaseViews e non solo, ma contiene metodi per eseguire calcoli i cui dati sono memorizzati e visualizzati in queste "PhaseViews".

Quello che sto cercando di realizzare

Ovviamente prima della ImportProcessViewModel diventa più pesante, ho bisogno di disgregare in modo che ogni PhaseView ha il suo ViewModel, ma anche in modo tale che ogni ViewModel mantiene un rapporto di nuovo a ImportProcessViewModel a causa della dipendenza imposta dalla ObservableCollection di IrData.

R & D

ho fatto la mia ricerca su ViewModels comunicanti tra loro, ma la maggior parte del results coinvolgere le applicazioni che sono state scritte con un quadro MVVM specifica. Non sto usando un framework e, a questo punto del progetto, sarebbe troppo tardi per refactoring per iniziare a usarne uno.

Tuttavia, ho trovato questo article e la risposta offerta da "hbarck" suggerisce qualcosa di semplice come la composizione per ottenere il risultato desiderato, ma poiché non ho molta esperienza con DataTemplates, non capisco cosa sia significa quando lui/lei suggerisce di esporre "ViewModel di UserControl come una proprietà sul ViewModel principale, e associare un ContentControl a questa proprietà, che quindi istanziare la Vista (es.l'UserControl) attraverso una DataTemplate"

In particolare, non capisco cosa si intende per "associare un ContentControl a questa proprietà, che sarebbe poi un'istanza Vista attraverso una DataTemplate".

Can qualcuno chiarire a titolo di esempio di codice cosa si intende per un'istanza di una vista attraverso un DataTemplate nel contesto di questo esempio?

Inoltre, questo è un buon approccio (come suggerito da 'hbarck')?

Come si può vedere, sto già impostando la proprietà Content di un ContentControl nella Phase View che deve essere istanziata. Solo che non so come si presenterebbe un DataTemplate.

+1

Benjamin già detto everthing c'è a questo argomento. ma ecco un altro esempio: http://www.codeproject.com/Articles/35277/MVVM-Mediator-Pattern. – Egi

risposta

4

Non capisco cosa si intende quando lui/lei suggerisce di esporre "ViewModel il UserControl come una proprietà sulla principale ViewModel, e si legano un ContentControl a questa proprietà, che sarebbe poi un'istanza View (vale a dire l'UserControl) attraverso una DataTemplate"

un DataTemplate permette di specificare una relazione tra una vista (ad esempio un controllo utente) e un modello di vista.

<DataTemplate DataType="{x:Type myApp:MyViewModel}"> 
    <myApp:MyUserControl /> 
</DataTemplate> 

Questo dice un ContentPresenter per visualizzare MyUserControl quando la proprietà di contenuto è impostato un'istanza di MyViewModel. Il modello di visualizzazione verrà utilizzato mentre l'utente controlla DataContext. In genere, lo DataTemplate viene aggiunto alle risorse dell'applicazione.

Ciò che l'autore di questa risposta sta dicendo è che si potrebbe avere un viewModel che ha una proprietà di un altro tipo viewModel associato alla proprietà Content di ContentPresenter.

<ContentPresenter Content="{Binding ParentViewModel.ChildViewModelProperty}"/> 

Fornendo avete un DataTemplate che specifica una relazione tra il tuo ChildViewModel e il controllo utente, WPF caricherà automaticamente il controllo utente nel vostro punto di vista.

This answer Ho fornito a un'altra domanda potrebbe anche fornire un aiuto.

ho bisogno di rompere in modo che ogni PhaseView ha il suo ViewModel, ma anche in modo che ogni ViewModel mantiene un rapporto di nuovo al ImportProcessViewModel.

Ciò consentirà di rompere viewModels in una vista più piccola e più gestibile. Modelli che si occupano di se stessi. Questo ti lascerà il problema di comunicare tra viewModels.

Se si annidano viewModels come suggerito, quindi viewModels secondari potrebbe esporre eventi che possono essere associati a viewModel padre in modo che venga notificato quando qualcosa cambia.Qualcosa del genere:

public class ParentViewModel // Derive from some viewModel base that implements INPC 
{ 
    public ParentViewModel() 
    { 
     childViewModel = new ChildViewModel(); 
     childViewModel.SomeEvent += someEventHandler; 
     // Don't forget to un-subscribe from the event at some point... 
    } 

    private void SomeEventHandler(object sender, MyArgs args) 
    { 
     // Update your calculations from here... 
    } 
} 

Questo è semplice e non richiede alcun framework aggiuntivo. Alcuni potrebbero obiettare contro questo metodo, ma è una soluzione valida che funziona. Il rovescio della medaglia è che i viewModels devono conoscere l'esistenza degli altri per iscriversi agli eventi in modo che possano essere strettamente accoppiati. È possibile utilizzare principi di progettazione orientati agli oggetti standard per aggirare questo problema (I.E. ricava il tuo bambino viewModel da un'interfaccia in modo che il genitore conosca solo l'interfaccia e non l'implementazione).

Se si vuole davvero utilizzare la comunicazione con accoppiamento lento, è necessario utilizzare una sorta di aggregazione di eventi o sistema di bus di messaggi. Questo è simile al metodo sopra, eccetto che esiste un oggetto che si trova tra i modelli di vista e funge da mediatore in modo che i viewModels non debbano conoscere l'uno dell'altro. Il mio answer here fornisce ulteriori informazioni.

Sono disponibili soluzioni preesistenti, ma ciò comporterebbe l'adozione di un quadro aggiuntivo. Ti consiglio di utilizzare Josh Smiths MVVM foundation in quanto è molto semplice e dovrai solo utilizzare una singola classe.

+0

Ottima risposta. Sto ancora digerendo la maggior parte di esso. Per iniziare, quando parli di "annidare le VM" puoi elaborare? Questa eredità è basata, composizione o altro? Chiedo a causa delle parole "Genitore" e "Bambino". –

+1

Nella mia risposta sto parlando della composizione di viewModels. Il genitore espone il bambino tramite una proprietà. –

+0

Nel codice, si afferma ''. Poiché questo codice si trova su ParentView.xaml, non sarebbe semplicemente '' poiché 'ParentView.xaml' imposta' DataContext' su 'ParentViewModel'? –

3

Mentre la risposta di Benjamin è davvero complessa e molto disponibile, mi piacerebbe chiarire come ciò che ho scritto in altri post si applicherebbe al vostro problema:

  • Avresti tre differenti PhaseViewModel classi per le tue diverse fasi, probabilmente derivate da una classe base comune, diciamo PhaseVMBase.
  • Invece di una proprietà CurrentPhaseView, probabilmente si dispone di una proprietà CurrentPhaseVM. Questo potrebbe essere di tipo Object o PhaseVMBase e restituire una delle tre classi PhaseViewModel, a seconda di ciò che l'utente ha scelto nel ViewModel principale.
  • PhaseVMBase avrebbe un metodo UpdateData, che verrà chiamato dal ViewModel principale ogni volta che riceve nuovi dati che devono essere elaborati dalla visualizzazione di fase. Il ViewModel principale chiamerebbe questo metodo su qualsiasi cosa accadesse per il CurrentPhaseVM al momento. PhaseViewModels implementerebbe INotifyPropertyChanged, in modo che le modifiche come risultato di UpdateData fossero visibili ai controlli associati.
  • I DataTemplates verranno dichiarati nelle risorse della vista principale, ad es. la finestra principale,

come questo:

<DataTemplate DataType="{x:Type my:Phase1VM}"> 
    <my:Phase1View/> 
</DataTemplate> 
<DataTemplate DataType="{x:Type my:Phase2VM}"> 
    <my:Phase2View/> 
</DataTemplate> 
<DataTemplate DataType="{x:Type my:Phase3VM}"> 
    <my:Phase3View/> 
</DataTemplate> 

Si noti che non v'è alcuna x: Key, solo il valore DataType. Se dichiarato in questo modo, WPF sceglierà il DataTemplate appropriato quando viene richiesto di visualizzare un oggetto di tipo Phase1VM, Phase2VM o Phase3VM, rispettivamente. Phase1View, Phase2View e Phase3View sarebbero UserControls che saprebbero come visualizzare i diversi ViewModels. Non istanziano i loro ViewModels da soli, ma si aspettano che DataContext sia impostato su un'istanza del rispettivo ViewModel dall'esterno.

Partendo dal presupposto che la ContentControl che dovrebbe mostrare la vista di fase viene dichiarato nella vista principale, e che il DataContext ci sarebbe il principale ViewModel, devi dichiarare il ContentControl in questo modo:

<ContentControl Content="{Binding CurrentPhaseVM}"/> 

A seconda del tipo effettivo di CurrentPhaseVM, questo sceglierà uno dei tre DataTemplate e visualizzerà l'UserControl appropriato. Il DataContext di UserControl sarebbe automaticamente il Contenuto di ContentControl, poiché questo sarebbe l'oggetto che ha determinato il DataTemplate da scegliere.

EDIT: Liste e formattazione del codice non vanno insieme, a quanto pare ...

+0

Questo è dannatamente utile. Sono felice che tu abbia colto il mio post e abbia scelto di rispondere. +1 –

Problemi correlati