2011-05-05 4 views
1

Edit:
J'ai trouvé une méthode pour le faire, mais je ne suis pas sûr que ce soit la meilleure façon.
Dans l'initialisation WindsorContainer, d'abord j'enregistrer viewmodel:WPF + Château de Windsor + MVVM: Locator-DataContext

container.Register(Component.For<CentrosViewModel>().LifeStyle.Transient); 

et plus tard, j'enregistrer la vue

 container.Register(Component.For<CentrosAdminView>().LifeStyle.Transient.DependsOn(Property.ForKey("DataContext") 
      .Eq(ViewModelLocator.Centrosviewmodel))); 

et la définition de la propriété ViewModelLocator.Centrosviewmodel est:

public static CentrosModel Centrosviewmodel 
    { 
     get 
     { 
      return App.container.Resolve<CentrosViewModel>(); 
     } 
    } 

Fin Modifier

J'essaie de faire une application Wpf en utilisant Castle Windsor et Mvvm Toolkit (galasoft) mais je pense que mon problème sera le même avec n'importe quelle boîte à outils MVVM. Avec MVVM, vous devez définir le DataContext de la vue sur votre ViewModel. Normalement, cela se fait par quelque chose comme cela dans la déclaration de la vue

DataContext={Binding MyViewModelInstanceName,Source={StaticResource Locator}} 

Resource Locator est défini dans App.xaml comme ceci:

<Application.Resources> 
    <!--Global View Model Locator--> 
    <vm:ViewModelLocator x:Key="Locator" /> 
</Application.Resources> 

Si je StartupUri dans App.xaml établirai à mon avis tout est correct. Mais si je laisse StartupUri vide et je tente d'obtenir une instance de mon point de vue à travers le château en utilisant la syntaxe suivante:

container.Resolve<CentrosAdminView>().Show(); 

Je me exception: "Cannot Find Resource with Name '{Locator}'

Je supose que initial DataContext est différent lors de l'exécution directe que lors de la traversée de Castle Windsor et c'est la raison pour laquelle il ne peut pas trouver de ressources.

Mes deux questions sont:

  • Est-il nécessaire avoir un ViewModelLocator lors de l'utilisation du château de Windsor? Dans
  • cas de Oui: Comment puis-je configurer correctement DataContext de vues avec
  • Windsor? Dans le cas de Non: Comment serait la bonne façon?

Je laisse ma configuration de château. Toute aide sera grandement appréciée.

Mon apparence de configuration Windsor comme ceci:

<castle> 
    <properties> 
     <!-- SQL Server settings --> 
     <connectionString>Server=192.168.69.69;Database=GIOFACTMVVM;user id=sa;password=22336655</connectionString> 
     <nhibernateDriver>NHibernate.Driver.SqlClientDriver</nhibernateDriver> 
     <nhibernateDialect>NHibernate.Dialect.MsSql2005Dialect</nhibernateDialect> 
    </properties> 

    <facilities> 

     <facility id="nhibernatefacility" 
       type="Repository.Infrastructure.ContextualNHibernateFacility, Repository"> 

     <factory id="sessionFactory1"> 
      <settings> 
      <item key="connection.provider">NHibernate.Connection.DriverConnectionProvider</item> 
      <item key="connection.driver_class">#{nhibernateDriver}</item> 
      <item key="connection.connection_string">#{connectionString}</item> 
      <item key="dialect">#{nhibernateDialect}</item> 
      <item key="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</item> 
      </settings> 
      <assemblies> 
      <assembly>Domain</assembly> 
      <assembly>ObservableCollections</assembly> 
      </assemblies> 
     </factory> 

     </facility> 
    </facilities> 
    </castle> 

et par code:

public static IWindsorContainer Start() 
    { 
     var container = new WindsorContainer(new XmlInterpreter()); 

     container.AddFacility<TransactionFacility>(); 

     container.Register(
      Component.For<HistoriasAdminView>().LifeStyle.Transient, 
      Component.For<HistoriasModel>().LifeStyle.Transient, 
      Component.For<CentrosModel>().LifeStyle.Transient, 
      Component.For<CentrosAdminView>().LifeStyle.Transient, 
      Component.For<MainViewModel>().LifeStyle.Transient, 
      Component.For<MainWindow>().LifeStyle.Transient, 

      Component.For<IRepository<Historias>>().ImplementedBy<Repository<Historias>>().LifeStyle.Transient, 
      Component.For<IRepository<Centros>>().ImplementedBy<Repository<Centros>>().LifeStyle.Transient, 
      Component.For<IUnitOfWork>().ImplementedBy<NHUnitOfWork>().LifeStyle.Transient 
      ); 

     return container; 
    } 

Répondre

8

Vous utilisez le modèle de service de localisation, où vous enregistrez les services, passer autour d'une référence au conteneur, et appelez explicitement résoudre tout le code. Si vous faites un tour rapide à travers le Castle Windsor wiki, ils découragent cette utilisation du conteneur.En règle générale, vous devez enregistrer tous vos types (via les installateurs), résoudre un seul objet racine (peut-être votre vue principale, peut-être une sorte de code de démarrage du contrôleur/MVC) et laisser le reste être résolu par le conteneur. La prochaine fois que vous appellerez le conteneur sera presque toujours container.Dispose, lorsque votre application se termine.

Voir la page du wiki Windsor concernant le the Three Calls Pattern.

Si vous trouvez des cas où vous devez extraire du conteneur pendant l'exécution pour créer des instances spécifiques (où vous devez passer des paramètres spécifiques pour créer cette instance), utilisez Typed Factory Facility au lieu de résoudre directement les dépendances.

MVVM avec Windsor:

Dans ma première application MVVM je l'ai écrit avec Windsor (vient de terminer la première version), je simplement enregistré mon point de vue principal et le modèle de vue, sans préciser le mode de vie. Cela leur a par défaut être des singletons.

La vue a pris une instance de modèle de vue comme dépendance requise (dans le constructeur) et l'a utilisée pour définir le contexte de données. Je l'ai fait en code-behind parce que ce n'était pas intrusif et indolore.

// In my program I used interfaces for everything. You don't actually have to... 
public interface IMainView 
{ 
    void Show(); 
} 

public class MainView : Window, IMainView 
{ 
    public MainView(IMainViewModel viewModel) 
    { 
     Initialize(); 
     this.DataContext = viewModel; 
    } 
} 

public interface IMainViewModel 
{ 
    int SomeProperty { get; set; } 
    ICommand ShowSubViewCommand { get; } 
    // ... 
} 

public class MainViewModel : IMainViewModel 
{ 
    public MainViewModel(SomeOtherSubComponent subComponent) 
    { 
     this.subComponent = subComponent; 
     // ... 
    } 

    // ... 
} 

Lorsque j'avais sous-vues que je voulais créer plusieurs instances de, je les ai enregistrées avec un cycle de vie transitoire. Ensuite, j'ai créé une fabrique de vues et une fabrique de modèles de vues, et je les ai utilisées pour obtenir des instances de la sous-vue et du sous-modèle de vues de la vue parente. J'ai enregistré un gestionnaire d'événements pour l'événement close de la vue et j'ai appelé une méthode Release sur les classes d'usine.

public interface ISubView 
{ 
    void Show(); 
    event Action OnDismissed; 
} 

public class SubView : Window, ISubView 
{ 
    public SubView(ISubViewModel viewModel) 
    { 
     Initialize(); 
     this.DataContext = viewModel; 
     // Would do this in the view model, 
     // but it is a pain to get Window.Close to call an ICommand, ala MVVM 
     this.OnClose += (s, a) => RaiseDismissed(); 
    } 

    public event Action OnDismissed; 

    private void RaiseDismissed() 
    { 
     if(OnDismissed != null) 
      OnDismissed(); 
    } 
} 

public interface ISubViewModel 
{ 
    string SomeProperty { get; } 
    // ... 
} 

// Need to create instances on the fly, so using Typed Factory facility. 
// The facility implements them, so we don't have to :) 
public interface IViewFactory 
{ 
    ISubView GetSubView(ISubViewModel viewModel); 
    void Release(ISubView view); 
} 

public interface IViewModelFactory 
{ 
    ISubViewModel GetSubViewModel(); 
    void Release(ISubViewModel viewModel); 
} 

// Editing the earlier class for an example... 
public class MainViewModel : IMainViewModel 
{ 
    public MainViewModel(IViewFactory viewFactory, IViewModelFactory viewModelFactory) 
    { 
     this.viewFactory = viewFactory; 
     this.viewModelFactory = viewModelFactory; 
     // Todo: Wire up ShowSubViewCommand to call CreateSubView here in ctor 
    } 

    public ICommand ShowSubViewCommand { get; private set; } 

    private void CreateSubView() 
    { 
     var viewModel = viewModelFactory.GetSubViewModel(); 
     var view = viewFactory.GetSubView(viewModel); 

     view.OnDismissed +=() => 
     { 
      viewModelFactory.Release(viewModel); 
      viewFactory.Release(view); 
     }; 

     view.Show(); 
    } 

    // Other code, private state, etc... 
} 

A la fin de tout cela, les seuls appels au conteneur sont:

void App_Startup() 
{ 
    this.container = new WindsorContainer(); 
    container.Install(Configuration.FromAppConfig()); 

    var mainView = container.Resolve<IMainView>(); 
    mainView.Show(); 
} 

public override OnExit() 
{ 
    container.Dispose(); 
} 

L'avantage pour tout ce galimatias est qu'il est indépendant du conteneur (et peut être utilisé sans conteneur), il est évident quelles sont les dépendances de chacun de mes composants, et la plupart de mon code n'a jamais besoin de demander ses dépendances. Les dépendances sont juste données à chaque composant comme il en a besoin.

+1

Dans votre cas spécifique, vous pouvez prendre des références à vos composants 'Model' en tant que paramètres constructeurs ou propriétés sur chaque' ViewModel'. Alors vous n'avez pas besoin de cette classe de localisateur. –

+1

bien, avez-vous le temps de l'écrire dans codeproject? :) – ktutnik

+0

Vous ne pouvez pas démarrer le nom de vos méthodes d'usine avec "Get". Voir https://groups.google.com/forum/#!topic/castle-project-users/nA_iF-6NqtY – reggaeguitar

Questions connexes