2010-01-21 3 views
5

Ho cercato di iniettare i moduli dal mio ModuleCatalog nel mio ViewModel della Shell, ma non sto avendo molta fortuna ...Prism 2.1 Moduli iniettando nel ViewModel

Sto creando la ModuleCatalog nel mio Bootstrapper e il mio modulo sta arrivando sullo schermo dal suo inizializzatore senza problemi. Tuttavia, mi piacerebbe essere in grado di legare la mia lista di moduli a un contenitore con un DataTemplate che permettesse loro di essere lanciati da un menu!

Ecco il mio file Boostrapper, sarò l'aggiunta di ulteriori moduli volte va avanti, ma per ora, contiene solo il mio piuttosto artificioso "ProductAModule":

public class Bootstrapper : UnityBootstrapper 
{ 
    protected override void ConfigureContainer() 
    { 
     Container.RegisterType<IProductModule>(); 

     base.ConfigureContainer(); 
    } 

    protected override IModuleCatalog GetModuleCatalog() 
    { 
     return new ModuleCatalog() 
      .AddModule(typeof(ProductAModule)); 
    } 

    protected override DependencyObject CreateShell() 
    { 
     var view = Container.Resolve<ShellView>(); 
     var viewModel = Container.Resolve<ShellViewModel>(); 
     view.DataContext = viewModel; 
     view.Show(); 

     return view; 
    } 
} 

A seguito di questo, ecco la mia Shell ViewModel:

public class ShellViewModel : ViewModelBase 
{ 
    public List<IProductModule> Modules { get; set; } 

    public ShellViewModel(List<IProductModule> modules) 
    { 
     modules.Sort((a, b) => a.Name.CompareTo(b)); 
     Modules = modules; 
    } 
} 

Come potete vedere, sto cercando di iniettare un elenco di IProductModule (a cui ProductAModule eredita alcune delle sue proprietà e metodi) in modo che possa poi essere associato a vista del mio Shell. C'è qualcosa VERAMENTE semplice mi manca o non può essere fatto usando Unity IoC? (L'ho visto fatto con l'estensione StructureMap per Prism)

Un'altra cosa ... Quando si esegue l'applicazione, nel momento in cui ShellViewModel viene risolto dal Contenitore in Bootstrapper, si riceve la seguente eccezione:

Risoluzione della dipendenza non riuscita, type = "PrismBasic.Shell.ViewModels.ShellViewModel", name = "". Il messaggio di eccezione è: L'attuale operazione di compilazione (chiave di build Chiave di compilazione [PrismBasic.Shell.ViewModels.ShellViewModel, null]) non riuscita: i moduli dei parametri non possono essere risolti quando si tenta di chiamare il costruttore PrismBasic.Shell.ViewModels.ShellViewModel (System.Collections .Generic.List`1 [[PrismBasic.ModuleBase.IProductModule, PrismBasic.ModuleBase, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null]] moduli). (Strategia tipo BuildPlanStrategy, indice 3)

Ad ogni modo, semplice eh ... sembra confuso ...

Qualsiasi aiuto sarebbe molto apprezzato!

Rob

risposta

0

Credo che ho incontrato un problema simile oggi, si scopre che PRISM crea il guscio prima di inizializzare i moduli, quindi non si può iniettare qualsiasi servizio dai moduli nella shell stessa.

Provare a creare un altro modulo che dipende da tutti gli altri e implementa la funzionalità desiderata, quindi è possibile aggiungerlo a una regione nella shell per visualizzare l'elenco di servizi. Purtroppo non ho ancora avuto la possibilità di provarlo, ma questa è la soluzione che ho intenzione di implementare.

Come nota a margine, penso che sia necessario contrassegnare la proprietà con un attributo per utilizzare l'iniezione di proprietà, ma potrei essere un errore (è stato un po 'di tempo da quando ho giocato direttamente con Unity).


Edit: È necessario applicare il DependencyAttribute di oggetti da utilizzare l'iniezione setter in Unity; puoi leggere a riguardo here.

+0

Ciao Rory, grazie per la tua risposta. Scusate, potrei essere stato un po 'troppo ambiguo con la mia domanda ... Mi piacerebbe che i veri moduli stessi in ShellViewModule, io affronterò servizi da qualche parte lungo la linea! L'ordine di marcia di inizializzazione nel programma di avvio automatico è la seguente: - ConfigureContainer - GetModuleCatalog - CreateShell così ho sicuramente ottenuto il mio allineamento ModuleInfo a portata di mano, sono abbastanza semplicemente non utile per il legame contro senza la proprietà aggiuntive che contiene il mio IProductModule. Grazie Rory! –

+0

Ciao di nuovo, ho usato l'attributo [Dipendenza] in precedenza (per Iniezione di proprietà).Sto cercando di iniettare nel costruttore qui, immagino che anche se stavo usando Property Injection, non sarebbe un proiettile d'argento! –

+0

Ehi, Robert, mi dispiace per l'incomprensione. Ho appena notato che i documenti per quell'attributo sono comunque scaduti, quindi rimuoverò il collegamento tra un momento. Se si dà un'occhiata all'origine dell'UnityBootloader, si noterà che il contenitore è configurato con ModuleCatalog, ma non con i singoli moduli. La soluzione migliore è probabilmente quella di ignorare ConfigureContainer e registrare i moduli con il contenitore da soli, ma non dimenticare di chiamare l'implementazione di base. – Rory

2

Penso che si potrebbe probabilmente solo fare questo:

public class Bootstrapper : UnityBootstrapper 
{ 
    protected override void ConfigureContainer() 
    { 
     Container.RegisterType<IProductModule>(); 

     base.ConfigureContainer(); 
    } 

    private static ObservableCollection<IProductModule> _productModules = new Obser...(); 
    public static ObservableCollection<IProductModule> ProductModules 
    { 
     get { return _productModules; } 
    } 
    protected override IModuleCatalog GetModuleCatalog() 
    { 
     var modCatalog = new ModuleCatalog() 
      .AddModule(typeof(ProductAModule)); 
     //TODO: add all modules to ProductModules collection 

     return modCatalog; 
    } 

    ... 
} 

allora si avrebbe una proprietà statica che qualche cosa potrebbe legarsi a direttamente, o potrebbe essere utilizzato dal ViewModel.


Ecco come ottenere un elenco di nomi di moduli che sono stati registrati con il catalogo moduli.

public class MyViewModel : ViewModel 
{ 

     public ObservableCollection<string> ModuleNames { ... } 
     public MyViewModel(IModuleCatalog catalog) 
     { 
      ModuleNames = new ObservableCollection<string>(catalog.Modules.Select(mod => mod.ModuleName)); 
     } 
} 

Questo è praticamente tutto. IModuleCatalog e IModuleManager sono le uniche cose che vengono configurate nel contenitore per l'accesso in termini di moduli. Come ho detto, però, non si otterranno dati di istanza perché questi moduli (si spera) devono ancora essere creati. È possibile accedere solo ai dati di tipo.

Spero che questo aiuti.

+0

Ciao Anderson, ci sono due ragioni per cui non lo sto facendo: 1) Lo sto scrivendo nella speranza di demistificare Prism con MVVM, quindi spero di mantenere tutti i miei binding nel View Model (ovvero il ShellViewModel). 2) Se dovessi intraprendere // TODO: aggiungi ... Non userei il ModuleCatalog che è registrato con UnityContainer ... Vedo da dove vieni e ti ringrazio per il tuo suggerimento! –

+0

Il problema che penso di affrontare è la traduzione di ModuleInfo in IProductModule. Essi ereditano da IProductModule che eredita da IModule. Il che rende l'intera situazione un po 'più irritante! –

+0

@Robert Reid: Non c'è nulla che non sia MVVM sulla soluzione che ho proposto. Questa statica è appena resa disponibile per un ViewModel da usare ed esporre alla sua vista OPPURE che la Vista usi direttamente usando {x: Statico ...}. Per quanto riguarda i moduli, l'API non memorizza un'istanza di IModule (o IProductModule, nel tuo caso) solo il Type (ModuleInfo.ModuleType) come stringa. A questo punto ha già eliminato ModuleName (il nome del tipo senza spazi dei nomi o il nome specificato nell'attributo). Puoi richiedere come dipendenza un IModuleCatalog o IModuleManager, ma questo è tutto. –

0
var modules = new IProductModule[] 
{ 
    Container.Resolve<ProductAModule>() 
    //Add more modules here... 
}; 
Container.RegisterInstance<IProductModule[]>(modules); 

Questo è tutto! Usando questo codice, posso iniettare i miei moduli in ShellViewModel e visualizzare ogni modulo come un pulsante nella mia applicazione!

UNA SOLUZIONE semplice! Da un bravo ragazzo nel gruppo di discussione CompositeWPF. Li raccomando senza riserve^_^

+0

Tranne la creazione di più istanze di ciascun modulo, se si intende attivarle in base alle informazioni visualizzate. Questo probabilmente funzionerà, ma tutto ciò che farai nel tuo constrictor per ogni modulo sarà fatto due volte E avrai permanentemente in memoria 2 istanze di ogni tipo di modulo. Questo è il motivo per cui non ho suggerito questo approccio. Mostra una mancanza di comprensione di come il Prisma funzioni davvero. –

+0

Ciao Anderson, ho una mancanza di comprensione con Prism! Ho testato la tua teoria e ci sono in effetti due istanze dei miei moduli in fase di creazione. Ma questa è la soluzione migliore che ho potuto inventare finora (il che mi consente di legare i miei moduli ai loro pulsanti, ognuno con un comando). –

1

Penso che tu abbia frainteso lo scopo dei moduli. I moduli sono solo contenitori per le viste e i servizi che desideri utilizzare. D'altra parte la shell dovrebbe contenere solo il layout principale della tua applicazione.

Quello che penso che dovresti fare è definire una regione nella tua shell, e quindi registrare le viste (che nel tuo caso sono pulsanti) con quella regione.

Come si desidera implementare viste e servizi in termini di moduli è più correlato al livello di modularità che si sta cercando, vale a dire se si desidera poter distribuire le viste ei servizi di ModuleA indipendentemente dalle viste e servizi di ModuleB e così via. Nel tuo caso potrebbe essere sufficiente registrare tutto in un unico modulo.

Prendetevi del tempo per giocare con gli esempi forniti con la documentazione, sono abbastanza buoni.

Il motivo per cui i vostri esempi generano un esempio è perché ShellViewModel dipende da List e quel tipo non è registrato in Unity. Inoltre stai registrando IProductModule con Unity, il che non ha senso perché non è possibile costruire un'interfaccia.