2009-04-29 8 views

risposta

14

Ci sono alcune opzioni. L'importante è che inizi a progettarlo all'inizio del progetto. Cercando di aggiungerlo a un progetto esistente può essere molto costoso se non è stato progettato con questa capacità in mente.

ci sono alcuni modelli fondamentali che si vorrà usufruire di:

  1. MVC o Observer modello. La prima chiave non è tanto l'implementazione religiosa o zelotica della tua architettura di alto livello. Che cosa è è importante è che il software riconosce la differenza tra il suo stato corrente e lo stato visualizzato e disaccoppia in modo appropriato. È necessario un accoppiamento comune e chiaramente definito tra lo stato visivo e lo stato dell'applicazione. Questo ti fornisce l'architettura comune di cui hai bisogno per creare un comando (vedi n. 2).

  2. Il modello Command. Si ottiene molta qualità con lo schema di comando (anche se può comportare il costo di un codice che dovrebbe essere generato da una procedura guidata). L'approccio specifico che si ottiene con il modello di comando può variare (una classe per comando con implementazione tramite sostituzioni rispetto a una classe per molti comandi con implementazione tramite gestori di eventi, ad esempio), ma i comandi sono la classe "action" che @Ralph suggerisce di strutturare uno stack in giro.

    Questo può essere un po 'complicato, ma l'approccio generale sarebbe quello di ascoltare un evento che "impegni" i dati dallo stato visivo allo stato dell'app. L'evento Validated potrebbe essere un buon hook per uno Textbox. L'evento Click avrebbe più senso per un Button. Quando si verifica questo commit, si crea il comando associato a quel controllo, si tenta di eseguire il comando e si aggiunge il comando allo stack di annullamento se il comando viene completato correttamente. Ora stai monitorando esattamente cosa sta succedendo e esattamente i dati con cui sta accadendo.

  3. Il modello Memento. @JP Tirato fuori l'ultimo pezzo del puzzle. È possibile utilizzare un memo sul comando salvato per memorizzare lo stato del controllo interessato prima dell'esecuzione del comando. Questo, combinato con un membro UnExecute() sull'interfaccia del comando, dovrebbe essere l'ultimo elemento fondamentale di progettazione necessario per eseguire l'attività.

La cosa bella di un approccio strutturato come questo è che ora avete un punto di estensione naturale per comportamento aggiuntivo che deve accadere su un comando-base. Le transazioni sono un adattamento naturale, per esempio. Nel mio progetto attuale, sto utilizzando l'interfaccia WPF ICommand (nel mio progetto winforms) per fornire un feedback sull'eventuale comando CanExecute() in qualsiasi momento. Questo mi consente di abilitare e disabilitare i widget dell'interfaccia utente in modo appropriato in modo guidato. :)

La cosa sfortunata è che non c'è molto supporto per questa struttura integrata in Winforms (per quanto ne so), quindi è necessario crearne la maggior parte da zero. Nessun singolo pezzo è particolarmente complicato, ma puoi trovarti generando una buona quantità di codice per lo più di caldaia per ogni comando. È anche una tecnica di design pervasiva. Perché sia ​​efficace, deve essere utilizzato in modo coerente in tutte le parti appropriate dell'applicazione. Ciò rende piuttosto costoso l'aggiornamento della funzionalità, specialmente se il codice originale è strettamente accoppiato e non è incoinente.

+0

Sono Ovviamente per alcuni dettagli qui si parla di dettagli, ma penso che tutti i pezzi importanti siano lì. :) –

0

Dipende dal numero di livelli di annullamento desiderati.

È possibile memorizzare i valori prima che vengano "commessi" in un certo senso in una raccolta a livello di modulo per ciascuno dei controlli, che è possibile "Ripristinare" facendo clic su un pulsante per consentire all'utente di tornare indietro.

3

Non sono sicuro che WinForms/.Net disponga di un tipo di funzionalità di annullamento incorporata che è possibile sfruttare. Ma quello che stai veramente cercando è una infrastruttura di Stack per aiutarti a gestire un elenco di azioni. Dovrai creare un qualche tipo di oggetto "azione" per rappresentare le azioni che un utente potrebbe fare e mentre progrediscono attraverso l'applicazione dovrai spingere queste azioni nello Stack. Quando premono il pulsante Annulla, o Ctrl-Z o qualsiasi altro metodo per avviare l'azione di annullamento, compaiono l'azione corrente e ripristinano lo stato dell'applicazione all'azione precedente.

Questa è una panoramica di base e di alto livello su come funzionerebbe, ma immagino che l'implementazione di tale funzione possa diventare piuttosto complessa. Immagina solo come deve funzionare per un programma come Adobe Photoshop. : O

1

CTRL + Z funziona su singoli controlli.

Se si lavora con i dati e un BindingSource, è possibile "annullare" le modifiche non persistenti ai record chiamando la funzione CancelEdit o in alternativa è possibile ricaricare i dati per il database.

2

Questo potrebbe non essere il modo migliore per farlo a seconda di ciò che si sta tentando di realizzare, ma è possibile utilizzare un richtextbox e chiamare il metodo di annullamento incorporato in tale controllo.

Ad esempio:

1

Il mio suggerimento è quello di determinare la specifica annulla requisiti e dei suoi benefici pratici prima di iniziare la progettazione e realizzazione. Ho ereditato un'app WinForms che utilizzava un annullamento sequenziale con più operazioni tramite una pila di oggetti generici di "azione" internamente. Tuttavia, è risultato che nessuno degli utenti dell'app con cui ho parlato né ha richiesto la funzione! E il modo in cui funziona questa particolare app, se fossi un utente dell'app, non mi vedrei nemmeno usare la funzione.

La funzionalità di annullamento avrebbe potuto essere più utile in questo caso se si trattasse di un annullamento "selettivo"; dove l'utente può selezionare qualsiasi singola operazione/modifica di diverse modifiche precedenti effettuate prima del commit dei dati e ripristinare quella singola modifica al suo stato originale, invece di essere solo in grado di annullare prima l'ultima operazione, seguita dalla penultima operazione ecc., ecco come è stato implementato.

In ogni caso, l'app in questo modo contiene la complessità e la reindirizzamento non necessarie, rendendo più difficile e lento "ingombrare" e apportare modifiche e miglioramenti alle funzionalità esistenti, in questo caso per un beneficio reale minimo o nullo. Da quando ho ereditato il progetto, ho implementato nuove funzionalità senza annullare e nessuno ha mai ricevuto lamentele.

Problemi correlati