修改
我找到了一种方法来做到这一点,但我不确定它是否是最好的方法
在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运行时不同,这就是它无法找到资源的原因。
我的两个问题是:
我留下了我的城堡配置。任何帮助将非常感激。
我的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;
}
答案 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();
}
所有这些问题的好处在于它独立于容器(并且可以在没有容器的情况下使用),很明显我的每个组件都依赖于哪些依赖项,而且我的大部分代码都不需要询问对于它的依赖。依赖关系只是在需要时传递给每个组件。