WPF + Castle Windsor + MVVM:Locator-DataContext

时间:2011-05-05 14:58:22

标签: wpf mvvm castle-windsor castle

修改
我找到了一种方法来做到这一点,但我不确定它是否是最好的方法 在WindsorContainer初始化中,首先注册viewmodel:

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

后来我注册了视图

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

属性ViewModelLocator.Centrosviewmodel的定义是:

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

结束修改

我正在尝试使用Castle Windsor和Mvvm Toolkit(galasoft)创建一个Wpf应用程序,但我认为我的问题与任何MVVM工具包都是一样的。

使用MVVM,您必须将View的DataContext设置为ViewModel。通常这是在视图声明

中通过类似的方式完成的
DataContext={Binding MyViewModelInstanceName,Source={StaticResource Locator}}

资源定位器在App.xaml中定义如下:

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

如果我在App.xaml中建立StartupURI到我的视图,那一切都是对的。 但是,如果我将StartupUri留空,我会尝试使用以下语法通过城堡获取我的视图实例:

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

我得到例外:"Cannot Find Resource with Name '{Locator}'

我认为,直接运行时的Initial DataContext与通过Castle Windsor运行时不同,这就是它无法找到资源的原因。

我的两个问题是:

  • 使用Castle Windsor时是否需要ViewModelLocator?在
  • 是的情况:如何使用
  • 正确设置视图的DataContext
  • 温莎?如果不是:怎么会是正确的方式?

我留下了我的城堡配置。任何帮助将非常感激。

我的Windsor配置如下所示:

<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>

并按代码:

    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;
    }

1 个答案:

答案 0 :(得分:9)

您正在使用服务定位器模式,您在其中注册服务,传递对容器的引用,并显式调用解析您的代码。如果您快速浏览Castle Windsor wiki,他们会阻止对容器的使用。

通常,您应该注册所有类型(通过安装程序),只解析一个根对象(可能是您的主视图,可能是某种启动/ MVC控制器样式代码),然后由容器解析其余部分。当您的应用程序退出时,下次调用容器时几乎总是container.Dispose

有关the Three Calls Pattern的信息,请参阅Windsor wiki页面。

如果您发现需要在运行时从容器中提取以创建特定实例(必须传递特定参数以创建该实例),请使用Typed Factory Facility而不是直接解析依赖关系。

MVVM与Windsor:

在我第一次使用Windsor编写的MVVM应用程序中(刚刚完成了第一个版本),我只是注册了我的主视图并查看模型而没有指定生活方式。这使他们成为单身人士。

视图将视图模型实例作为必需的依赖项(在构造函数中),并使用它来设置数据上下文。我在代码隐藏中做到了这一点,因为它是非侵入性和无痛的。

// 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;
        // ...
    }

    // ...
}

如果我有子视图,我想创建多个实例,我在瞬态生命周期中注册它们。然后我创建了一个视图工厂和视图模型工厂,并使用它们从父视图中获取子视图和子视图模型的实例。我为视图的close事件注册了一个事件处理程序,并在工厂类中调用了Release方法。

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...
}

最后,对容器的唯一调用是:

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

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

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

所有这些问题的好处在于它独立于容器(并且可以在没有容器的情况下使用),很明显我的每个组件都依赖于哪些依赖项,而且我的大部分代码都不需要询问对于它的依赖。依赖关系只是在需要时传递给每个组件。