Questa cosa è rimasta in sospeso da tempo immemore ed è ora di dare un senso, perchè altrimenti un senso non ce l’ha… <semi-cit> :-)
La nostra applicazione funziona! ma effettivamente è poco più di un “Hello World”, però funziona. Prometto che diventerà qualcosa di più di un semplicissimo “proof of concept”.
Adesso però abbiamo un problema non da poco, ne abbiamo già parlato, e adesso cerchiamo di approfondire e nel limite del possibile dare una soluzione.
A volte si dice che un’immagine vale più di mille parole ma in questo caso non è proprio vero… non è che si capisca molto :-)
La necessità è quella al “doppio click” di visualizzare una Window con il dettaglio dell’item selezionato, abbiamo in questo caso 2 possibili problemi/necessità:
- Facciamo doppio click su un elemento:
- se c’è già aperta una window che visualizza quello specifico elemento dobbiamo portarla in foreground;
- altrimenti dobbiamo aprirne una nuova;
- Il secondo problema, già risolto con le “dynamic region”, è quello di poter sapere quale coppia view/region manager è stata aperta per poter iniettare i contenuti giusti nella Window giusta;
Il secondo problema è acqua passata. Possiamo occuparci del primo scoglio. In realtà il problema non è nulla di trascendentale e la soluzione è abbastanza semplice, Visual Studio docet.
n.d.r.
Per chi non lo sapesse Visual Studio, sin dalla primissima versione, è un’applicazione completamente basata su UI Composition e su IoC (Package/Services e IServiceProvider).
Running Documents Service
Abbiamo detto:
…se c’è già aperta una window che visualizza quello specifico elemento dobbiamo portarla in foreground…
Il problema vero sta tutto in quella frase e nelle due parole sottolineate, il nostro sistema di comunicazione interno, basato su messaggistica, è gestito dai ViewModel e dal Modulo stesso, nessuno di questi due attori sa nulla, e così è giusto che sia, di Window che visualizzano qualcosa. Quello che sappiamo al massimo è che ci potrebbe essere in esecuzione un ViewModel che ha come DataSource un determinato elemento ma anche una projection di quell’elemento...
Ecco che nasce l’esigenza di avere qualcuno che sia in grado di:
- Tenere traccia dei ViewModel/View aperti;
- Sapere se un certo ViewModel/View visualizza una certa cosa;
- Recuperare, nell’eventualità, un certo ViewModel/View;
Triviale direi:
public interface IRunningDocumentsService
{
void RegisterDocument( IDocument document );
void UnregisterDocument( IDocument document );
Boolean IsRegistered( IDocument document );
IEnumerable<T> Find<T>( Func<T, Boolean> criteria ) where T : IDocument;
Boolean TryGetDocument<T>( out T document ) where T : IDocument;
}
Dove IDocument è semplicemente questo:
public interface IDocument
{
String Title { get; }
}
Oppure questo:
public interface IDataDocument : IDocument
{
event EventHandler DirtyStateChanged;
Boolean IsDirty { get; }
Boolean CanSave { get; }
void Save();
Boolean CanUndo { get; }
void Undo();
void RejectChanges();
Boolean CanRedo { get; }
void Redo();
}
Quindi qualcosa di un po’ esaustivo per le nostre necessità.
Il segreto di pulcinella per automatizzare le cose è far si che la registrazione avvenga automaticamente, per arrivare a ciò è sufficiente che il ViewModel di base da cui ereditate tutto il vostro mondo abbia una dipendenza dal servizio e se l’istanza è IDocument procede con la registrazione/deregistrazione.
Aggiungo un’ultima considerazione, negli esempi e nei post fino ad ora ho parlato di finestre, intendendo proprio finestre separate ma questo non è un vincolo, il mio toolkit, come anche Prism (forse sarebbe meglio metterli in ordine inverso :-)), utilizzano il concetto di finestra in maniera più ampia parlando semplicemente di View. Io tendo a parlare di finestre per il semplice fatto che è più semplice comunicare concetti, probabilmente complessi, utilizzando esempi semplici. Quindi che la nostra UI sia Outlook based, che per inciso a me piace molto, quindi MainWindow+ChildWindows(s) o Visual Studio based, quindi MainWindow+Documents, non c’è differenza.
.m