问题描述
编辑:
我找到了一种方法来执行此操作,但是我不确定这是否是最佳方法。
在 WindsorContainer
初始化,首先我注册视图模型:
I have found one method to do this but I'm not sure if it is the best way.
In WindsorContainer
initialization, first I register 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工具包相同。
I'm trying to make an Wpf application using Castle Windsor and Mvvm Toolkit (galasoft) but I thing my problem will be the same with any MVVM toolkit.
使用MVVM,必须将View的DataContext设置为ViewModel。通常,这是通过在视图的声明中执行类似的操作
With MVVM you must set the DataContext of the View to your ViewModel. Normally this is done by something like this in declaration of the view
DataContext={Binding MyViewModelInstanceName,Source={StaticResource Locator}}
在App.xaml中定义资源定位符,如下所示:
Resource Locator is defined in App.xaml like this:
<Application.Resources>
<!--Global View Model Locator-->
<vm:ViewModelLocator x:Key="Locator" />
</Application.Resources>
如果我在App.xaml中建立StartupURI,则一切正常。
但是如果我将StartupUri留空,则尝试使用以下语法通过城堡获取我的视图实例:
If I establish StartupURI in App.xaml to my view all is right.But if I leave StartupUri blank and I try to get an instance of my view through castle using following syntax:
container.Resolve<CentrosAdminView>().Show();
我遇到异常:找不到名称为'{Locator}'的资源
我认为直接运行的初始DataContext与通过城堡温莎运行的初始DataContext是不同的,这就是它找不到资源的原因。
I supose that Initial DataContext is different when running directly than when running through Castle Windsor and this is the reason why it can't find resource.
我的两个问题是:
- 是否有必要在使用ViewModelLocator时使用Castle Windsor?在
- 情况下是:如何使用
- Windsor正确设置视图的DataContext?
我放弃了城堡配置,任何帮助将不胜感激。
I leave down my Castle Configuration. Any help would be really appreciated.
我的Windsor配置如下:
My Windsor configuration look like this:
<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;
}
推荐答案
您正在使用服务定位器模式,在其中注册服务,传递对容器的引用,并在您的代码中显式调用resolve。如果您快速浏览,则他们不鼓励使用此容器
You are using the Service Locator pattern, where you register services, pass around a reference to the container, and explicitly call resolve all over you code. If you take a quick tour through the Castle Windsor wiki, they discourage this use of the container.
通常,您应该注册所有类型(通过安装程序),仅解析一个根对象(也许是您的主视图,也许是某种启动/ MVC控制器样式代码) ),其余的由容器解决。下次调用该容器时,几乎总是。
Generally, you should register all your types (via installers), resolve only one root object (maybe your main view, maybe some sort of startup/MVC controller style code), and let the rest be resolved by the container. The next time you will call the container will almost always be container.Dispose
, when your application exits.
请参见有关的温莎Wiki页面。
See the Windsor wiki page regarding the Three Calls Pattern.
如果发现需要在运行时从容器中拉出以创建特定实例的情况(必须传递特定参数来创建该实例),请使用,而不是直接解决依赖关系。
If you find cases where you need to pull from the container during runtime to create specific instances (where you have to pass specific parameters to create that instance), use the Typed Factory Facility instead of directly resolving dependencies.
使用Windsor的MVVM:
在我用Windsor编写的第一个MVVM应用程序中(刚刚完成了第一个版本),我只是注册了我的主视图和视图模型,而不指定生活方式。
In my first MVVM app I wrote with Windsor (just finished the first version), I simply registered my main view and view model without specifying the lifestyle. This defaulted them to be singletons.
该视图将视图模型实例作为必需的依赖项(在构造函数中),并使用它来设置数据上下文。我这样做是在代码隐藏的后面完成的,因为它是非侵入性的且无痛苦的。
The view took a view model instance as a required dependency (in the constructor), and used it to set the data context. I did this in code-behind because it was non-intrusive and painless.
// 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;
// ...
}
// ...
}
在我要创建多个实例的子视图中,我用短暂的生命周期注册了它们。然后,我创建了一个视图工厂和视图模型工厂,并使用它们从父视图中获取子视图和子视图模型的实例。我为视图的关闭事件注册了一个事件处理程序,并在工厂类上调用了 Release
方法。
Where I had sub-views that I wanted to create multiple instances of, I registered them with a transient life-cycle. Then I created a view factory and view model factory, and used them to get instances of the sub-view and sub-viewmodel from the parent view. I registered an event handler for the view's close event, and called a Release
method on the factory classes.
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...
}
最后,对容器的唯一调用是:
At the end of all this, the only calls to the container are:
void App_Startup()
{
this.container = new WindsorContainer();
container.Install(Configuration.FromAppConfig());
var mainView = container.Resolve<IMainView>();
mainView.Show();
}
public override OnExit()
{
container.Dispose();
}
所有这些琐事的好处是它独立于容器(并且可以在没有容器的情况下使用),很明显,我的每个组件都具有哪些依赖关系,并且我的大多数代码都不需要询问其依赖关系。依赖关系只在需要时交给每个组件。
The benefit to all this rigmarole is that it is independent of the container (and can be used without a container), it is obvious which dependencies each of my components take, and most of my code doesn't ever have to ask for its dependencies. The dependencies are just handed to each component as it needs it.
这篇关于WPF +温莎城堡+ MVVM:Locator-DataContext的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!