Come posso applicare il modello MVC a un'applicazione WinForms C #?


11

Sono uno sviluppatore C ++ che da allora utilizza il modello MVC per progettare GUI.

Recentemente volevo tornare in C # e ho impostato un'applicazione Windows Form, ma ora mi sono un po 'perso su come spingerlo a una struttura conforme a MVC.

Quello che sto attualmente cercando di fare è "dichiarare" la classe che mi è stata assegnata per WinForms come Vista e aggiungere una classe per il Modello e il Controller in background. Tuttavia, non sono davvero sicuro su come interagire con gli eventi, come un clic sul pulsante. Solitamente reindirizzerei questi eventi al controller ed eseguivo un'azione sulla vista una volta terminato.

Questo sembra abbastanza insoddisfacente in questa costellazione però. Ad esempio, se volessi implementare un pulsante "Esci", dovrei reindirizzare l'evento dalla vista al controller, nonché implementare un metodo pubblico aggiuntivo nella mia vista che può quindi essere chiamato dal controller, mentre io potrebbe semplicemente chiamare Close () dalla vista nella prima istanza.

Hai qualche consiglio da darmi? La mia comprensione di Windows Form in C # non è ancora abbastanza buona per provare un'implementazione MVC? Sto assegnando alla classe dei moduli un ruolo sbagliato? MVC è semplicemente un'architettura inappropriata per questo caso d'uso?


1
Domanda OT. Ma perché WInforms? WPF doveva sostituire Winforms e supporta MVC (ben tecnicamente MVVM). Anche se dirò che la curva di apprendimento del WPF può essere ripida. (ma se lo fai male puoi far sembrare il codice WPF come Winforms)
Peter M,

1
@PeterM: perché anche dopo 10 anni WPF fa schifo ed è ancora lento.
whatsisname

3
Per parafrasare il commento di whatsisname, WPF è orientato verso applicazioni più grandi. Le applicazioni più piccole potrebbero essere meglio servite con Winforms perché Winforms è probabilmente più semplice. Tuttavia, se si desidera sostenere tale argomento, è possibile anche argomentare che l'applicazione Winforms è abbastanza piccola laddove probabilmente non necessita di MVP. MVVM è inserito in WPF. WPF ha una grafica vettoriale, quindi non presenta gli stessi problemi di dimensionamento di Winforms. WPF è molto compostabile (puoi facilmente inserire i controlli all'interno di altri controlli) e ha un'associazione dati killer. Cosa non va?
Robert Harvey,

3
@whatsisname WPF potrebbe fare schifo, ma fa schifo molto meno di Winforms per qualsiasi cosa al di là di un programma giocattolo
Peter M,

2
Per i programmi desktop interni, direi così. Ma molte aziende preferiscono le applicazioni basate su browser per le loro operazioni aziendali, semplicemente perché non è richiesta alcuna installazione.
Robert Harvey,

Risposte:


3

Per coincidenza, sto lavorando a un progetto WinForms che è modellato su MVC. Non la definirei una risposta perfetta, ma spiegherò il mio design generale e spero che questo possa aiutarti a inventare il tuo.

Sulla base della lettura che ho fatto prima di iniziare questo progetto, non sembra esserci un modo "giusto" per implementarlo. Ho seguito semplici principi di progettazione OOP e MVC e il resto è stato tentativi ed errori mentre ho sviluppato un flusso di lavoro.

MVC è semplicemente un'architettura inappropriata per questo caso d'uso?

No ..? Non c'è abbastanza contesto nella tua domanda per fornire una risposta diretta a questo. Perché stai usando MVC in primo luogo? Quali sono i requisiti non funzionali del tuo progetto? Il tuo progetto sarà molto pesante nell'interfaccia utente? Ti interessa di più la sicurezza e preferiresti un'architettura a più livelli? Quali sono i componenti principali del tuo progetto? Forse ogni componente necessita di un modello di progettazione diverso. Scopri perché vuoi utilizzare questo modello di progettazione in primo luogo e potresti rispondere alla tua domanda;)

La mia ragione per usare MVC: è un modello di progettazione abbastanza semplice da capire secondo me e il mio design si basa fortemente sull'interazione dell'utente. Il modo in cui MVC consente allo sviluppatore di separare le preoccupazioni è sufficiente anche per la mia applicazione. Questo rende il mio codice molto più gestibile e testabile.

Suppongo anche che sto usando più di un design ibrido. Di solito, il concetto ideale presentato nell'ingegneria del software non si concretizza nella pratica. Puoi modificare il design in base alle esigenze del tuo progetto. Non c'è bisogno di lasciarsi coinvolgere da ciò che è giusto o sbagliato. Ci sono pratiche generali, ma le regole possono sempre essere piegate o infrante fintanto che non ti spari in un piede.

La mia implementazione è iniziata con un design di alto livello che mi ha dato un'idea di quali componenti avrò bisogno. È meglio iniziare in questo modo e scendere nell'architettura. Ecco il diagramma del pacchetto per il progetto (creato in StarUML): inserisci qui la descrizione dell'immagine

Notare che ogni singolo livello tranne quello di presentazione dipende dal sistema di messaggistica. Questo è un "linguaggio" comune che gli strati inferiori e i sottosistemi di quegli strati usano per comunicare tra loro. Nel mio caso, era una semplice enumerazione basata su operazioni che possono essere eseguite. Il che mi porta al punto successivo ...

Pensa alle operazioni o ai comandi come base per la tua implementazione. Cosa vuoi che faccia la tua applicazione? Suddividilo in operazioni fondamentali. Ad esempio: CreateProject, WriteNotes, SaveProject, LoadProject ecc. Alla GUI (o alle classi Form) si verificherà un evento (come la pressione di un pulsante). Ad ogni operazione è associato un metodo controller. In questo caso qualcosa come Exit è troppo semplice. L'applicazione può essere semplicemente chiusa dalla classe Form. Ma supponiamo che prima volessi conservare alcuni dati dell'applicazione in un file? Chiamerò il metodo "Salva" dalla rispettiva classe del controller all'interno del mio metodo di pressione del pulsante.

Da lì, il controller chiamerà il set corretto di operazioni dalle classi di servizio. Le classi di servizio nella mia applicazione fungono da interfaccia per il livello di dominio. Convalideranno l'input ricevuto dalla chiamata del metodo del controller (e quindi dalla GUI) e manipoleranno il modello di dati.

Una volta completata la convalida e la manipolazione dell'oggetto corrispondente, il metodo di servizio restituirà un codice messaggio al controller. Ad esempio MessageCodes.SaveSuccess,. Sia il controller che le classi di servizio erano basati sugli oggetti di dominio e / o sull'insieme generale di operazioni che possono essere raggruppate insieme.

Ad esempio: FileMenuController(operazioni: NewProject, SaveProject, LoadProject) -> ProjectServices(CreateProject, PersistProjectToFile, LoadProjectFromFile). Dove Projectsarebbe una classe di dominio nel tuo modello di dati. Le classi Controller e Service nel mio caso erano classi non istanziabili con metodi statici.

Quindi, il controller riconosce l'operazione come completata in modo non riuscito. Ora, il controller ha il proprio sistema di messaggistica che utilizza per interagire con il livello di presentazione, quindi la doppia dipendenza tra il livello di servizio e quello di presentazione. In questo caso, una classe chiamata ViewStatenel ViewModelspacchetto viene sempre restituita alla GUI dal controller. Questo stato contiene informazioni come: " lo stato in cui hai provato a mettere l'applicazione in corso di validità? ", " Un messaggio leggibile dall'uomo sull'operazione che hai tentato di eseguire e perché ha avuto o meno esito positivo (messaggi di errore) " e una ViewModelclasse.

La ViewModelclasse contiene dati rilevanti dal livello di dominio che la GUI utilizzerà per aggiornare la vista. Questi modelli di vista sembrano le classi di dominio ma nel mio caso ho usato oggetti molto magri . Fondamentalmente non hanno praticamente alcun comportamento, semplicemente trasmettono informazioni sullo stato di livello inferiore dell'applicazione. In altre parole, non regalerò MAI le mie classi di dominio al livello di presentazione. Questo è anche il motivo per cui i pacchetti Controllerse Servicesdividono il livello di servizio in due parti. I controller non gestiranno mai le classi di dominio o convalideranno il loro stato. Agiscono semplicemente come un limite per convertire i dati rilevanti della GUI in dati rilevanti per il dominio che i servizi possono usare e viceversa. Includere la logica di servizio nel controller porterebbe molto grasso controller, che sono più difficili da mantenere.

Spero che questo ti dia un punto di partenza.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.