MVP Passive View - la conservazione dei dati vista di dati e modello separato
-
10-10-2019 - |
Domanda
Ho implementato un MVP della triade usando la vista modello passivo - vale a dire la vista contiene getter e setter solo semplici. Comunque io sto avendo difficoltà che separa i dati visualizzazione di dati e di modello. In particolare nel trattamento di un cambiamento di stato di visualizzazione.
La triade viene utilizzato per consentire all'utente di selezionare una parte da un elenco. L'elenco dei pezzi è fornita dal modello con ciascuna parte univocamente identificati da un ID univoco.
Diciamo che le parti simile a questa:
class Part
{
int ID; // this code uniquely identifies the part within the model
String partCode;
String description;
double voltage;
}
la vista visualizza la lista all'utente e permette loro di selezionare una parte
L'elenco viene visualizzato in una DataGridView e una parte viene selezionato cliccando su una riga nella dataGridView.
L'ID non deve essere visualizzato all'utente e non è la tensione, quindi il modello crea un DataTable che contiene solo il partCode e la descrizione. Questo DataTable viene assegnato dal presentatore a una proprietà sulla considerazione che le mappe per la proprietà DataSource DataGridView.
class Presenter
{
IView _view;
IModel _model;
//...///
_view.Data = _model.GetFilteredData();
}
class Model
{
public DataTable GetFilteredData()
{
// create a DataTable with the partCode and Description columns only
// return DataTable
}
}
class View //winform
{
public DataTable Data
{
set
{
this.dataGridView.Source = value;
}
}
}
Fin qui tutto bene. The View Dislays i dati filtrati nel DataGridView.
Il problema che ho è restituire la parte selezionata dall'utente.
La vista non è a conoscenza della ID univoco in quanto non viene visualizzato e le altre informazioni non può essere garantita per essere unico -. Therfore non è possibile identificare in modo univoco la parte selezionata ??p>
Essenzialmente sto cercando di convertire i dati immagine (riga selezionata) ai dati del modello (la parte selezionata) senza un componente usando i dati altri.
Finora ho le seguenti soluzioni:
1) La visualizzazione viene passato un DataTable che contiene l'ID e poi filtra il display in modo che non venga visualizzato all'utente. È allora banale per restituire un ID per la riga selezionata. Il problema è che ho ora inquinato la vista con logica che è testato (il filtraggio del display).
2) La vista restituisce l'indice di riga e il modello corrisponde a questo indice per una riga nei dati originali. Ciò significherebbe garantire che l'ordine nella visualizzazione non cambia mai, che mentre possibile, restringe come la visualizzazione possono mostrare (e manipolare) i dati. Questo inquina anche il modello con dati vista (l'indice di riga).
public int RowIndexSelected { get; private set; }
//...//
private void gridParts_CellEnter(object sender, DataGridViewCellEventArgs e)
{
if (SelectedPartChangedEvent != null)
{
RowIndexSelected = e.RowIndex;
SelectedPartChangedEvent();
}
}
3) Una variazione (2). Creare un oggetto adattatore per sedersi tra il presentatore e la vista. Spostare la fila per ID codice di conversione dal modello all'adattatore. Il presentatore gestisce allora l'evento cambiato dataGridAdapters parte.
public PartSelectDataGridAdapter(IPartSelectView view, PartCollection data)
{
_view = view;
_data = data;
_view.SelectedPanelChangedEvent += HandleSelectedPartChanged;
}
void HandleSelectedPartChanged()
{
int id = _data[_view.RowIndexSelected].ID;
if (SelectedPartChanged != null)
{
SelectedPartChanged(id);
}
}
Attualmente im apprendimento verso 3 poiché è verificabile, mantiene logica dai dati immagine e vista dal modello e presentatore.
Come si affrontare questo -? C'è una soluzione migliore
Soluzione
L'ID è di non essere visualizzato al utente e né è la tensione, quindi il modello crea un DataTable che contiene solo il partCode e la descrizione.
Soluzione semplice: do creare una colonna ID nel DataTable e nasconderlo in nella vista datagrid .
Altri suggerimenti
ho postato una soluzione semplice in precedenza; questa è una risposta più dettagliata alla domanda
C'è una ragione per cui non si vuole passare un List<Part>
alla vista?
Si potrebbe configurare la griglia per nascondere la colonna id e la tensione. Si può semplicemente ottenere l'oggetto selezionato dalla fonte vincolante nella vista. Il presentatore ha potuto interrogare la vista per questa selezione, o la vista può chiamare un SelectionChanged(Part selected)
sul presentatore.
E 'significherebbe che si sta più seguendo rigorosamente la passivo-view modello , ma un < a href = "http://martinfowler.com/eaaDev/SupervisingPresenter.html" rel = "nofollow"> supervisione Controller , perché ora la visualizzazione conosce il modello.
Se non lo fai in questo modo, è possibile introdurre un vista del modello , che fai già implicitamente con il DataTable. (Questo non è necessariamente un male, btw).
Nel tuo esempio, le classi del modello sa sui modelli vista, perché avete metodi sul modello che li generano. Mi piacerebbe consiglio di Inverse questo rapporto: creare metodi sul vostro modello di opinione che dipendono dal vostro oggetti del modello. In questo modo, potrai mantenere il vostro modello classi bello e pulito e indipendente di tutti i dati UI necessarie nel livello di presentazione.
Quando si utilizza la vista del modello / supervisione vie, considerare cadere il concetto DataTable in favore delle classi semplici.
EDIT: un'alternativa per rendere la vista completamente all'oscuro del modello:
Costruire un'istanza di questa classe nel presentatore, in cui si conosce il modello e la vista del modello sia:
public class PartViewModel
{
object PartModel { get; set; }
string Name { get; set; }
string Description { get; set; }
}
Passa un List<PartViewModel>
come origine dati per il DataGridView.
È possibile restituire l'oggetto PartViewModel selezionato per il presentatore (o usando un evento o utilizzando un metodo). Il presentatore sa che può lanciare la proprietà torna PartModel a un'istanza di parte. La vista non ha bisogno di sapere nulla circa il modello, come dici tu sono preferendo. Ma è ancora possibile utilizzare semplice identità di un oggetto nella presentatore, evitando "complicato" ricerca inversa utilizzando l'id del.
Con un presentatore callback:
interface IPartListPresenter
{
// other methods
void SelectedPartChanged(PartViewModel nowSelected);
}
Supponendo partBindingSource è il BindingSource GridView è collegato, è possibile gestire l'evento CurrentChanged di partBindingSource in questo modo:
private void partBindingSource_CurrentChanged(object sender, EventArgs e)
{
_presenter.SelectedPartChanged(partBindingSource.Current as PartViewModel);
}
Nel presentatore:
public void SelectedPartChanged(PartViewModel nowSelected)
{
if(nowSelected == null)
{
return;
}
part myPart = (Part) nowSelected.Part;
// dos stuff
}
Spero che questo aiuti.
Credo che tu abbia frainteso il concetto un po 'qui!
E 'il presentatore che dovrebbe gestire questa situazione, non il modello. Il modello dovrebbe concentrarsi solo sulla sua esclusiva responsabilità, in caso contrario, si mantiene la vista e modello troppo vicino!
Il mio suggerimento è quello di mantenere una colonna nascosta nella vostra tabella di passare l'evento della riga selezionata al presentatore, e poi lasciare che la maniglia Presenter al lavoro!
Questa sarà l'uso corretto di MVP.