I am starting a new desktop application and I want to build it using MVVM and WPF.
我正在启动一个新的桌面应用程序,我想使用MVVM和WPF来构建它。
I am also intending to use TDD.
我也打算使用TDD。
The problem is that I don´t know how I should use an IoC container to inject my dependencies on my production code.
´问题是我不知道我应该使用IoC容器注入我依赖我的产品代码。
Suppose I have the folowing class and interface:
假设我有folowing类和接口:
public interface IStorage
{
bool SaveFile(string content);
}
public class Storage : IStorage
{
public bool SaveFile(string content){
// Saves the file using StreamWriter
}
}
And then I have another class that has IStorage
as a dependency, suppose also that this class is a ViewModel or a business class...
然后我有另一个类,它将IStorage作为一个依赖项,假设这个类是一个ViewModel或一个business类……
public class SomeViewModel
{
private IStorage _storage;
public SomeViewModel(IStorage storage){
_storage = storage;
}
}
With this I can easily write unit tests to ensure that they are working properly, using mocks and etc.
有了这个,我可以很容易地编写单元测试,以确保它们能够正常工作,使用模拟等等。
The problem is when it comes to use it in the real application. I know that I must have an IoC container that links a default implementation for the IStorage
interface, but how may I to do it?
问题是当它在实际应用程序中使用时。我知道我必须有一个IoC容器来链接IStorage接口的默认实现,但是我要怎么做呢?
For example, how would it be if I had the following xaml:
例如,如果我有以下的xaml:
How can I correctly 'tell' WPF to inject dependencies in that case?
在这种情况下,如何正确地“告诉”WPF注入依赖项?
Also, suppose I need an instance of SomeViewModel
from my cs
code, how should I do it?
另外,假设我需要一个来自cs代码的SomeViewModel的实例,我该如何做呢?
I feel I´m completely lost in this, I would appreciate any example or guidance of how is the best way to handle it.
我觉得我´m完全迷失在这,我将不胜感激任何例子或指导的是最好的方法来处理它。
I am familiar with StructureMap, but I´m not an expert. Also, if there is a better/easier/out-of-the-box framework, please let me know.
我熟悉StructureMap,但我´m不是专家。此外,如果有更好的/更容易的/开箱即用的框架,请让我知道。
Thanks in advance.
提前谢谢。
63
I have been using Ninject, and found that it's a pleasure to work with. Everything is set up in code, the syntax is fairly straightforward and it has a good documentation (and plenty of answers on SO).
我一直在使用Ninject,我发现和他一起工作很愉快。所有的东西都是在代码中设置的,语法非常简单,并且有很好的文档(并且有大量的答案)。
So basically it goes like this:
基本上是这样的:
Create the view model, and take the IStorage interface as constructor parameter:
创建视图模型,并以IStorage接口作为构造函数参数:
class UserControlViewModel
{
public UserControlViewModel(IStorage storage)
{
}
}
Create a ViewModelLocator with a get property for the view model, which loads the view model from Ninject:
为视图模型创建一个带有get属性的ViewModelLocator,它将从Ninject加载视图模型:
class ViewModelLocator
{
public UserControlViewModel UserControlViewModel
{
get { return IocKernel.Get();} // Loading UserControlViewModel will automatically load the binding for IStorage
}
}
Make the ViewModelLocator an application wide resource in App.xaml:
将ViewModelLocator设置为App.xaml中的应用程序范围资源:
Bind the DataContext of the UserControl to the corresponding property in the ViewModelLocator.
将UserControl的DataContext绑定到ViewModelLocator中的相应属性。
Create a class inheriting NinjectModule, which will set up the necessary bindings (IStorage and the viewmodel):
创建一个继承NinjectModule的类,它将设置必要的绑定(IStorage和viewmodel):
class IocConfiguration : NinjectModule
{
public override void Load()
{
Bind().To().InSingletonScope(); // Reuse same storage every time
Bind().ToSelf().InTransientScope(); // Create new instance every time
}
}
Initialize the IoC kernel on application startup with the necessary Ninject modules (the one above for now):
在应用程序启动时,使用必要的Ninject模块(上面的模块)初始化IoC内核:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
IocKernel.Initialize(new IocConfiguration());
base.OnStartup(e);
}
}
I have used a static IocKernel class to hold the application wide instance of the IoC kernel, so I can easily access it when needed:
我使用了一个静态的IocKernel类来保存IoC内核的应用程序范围的实例,所以我可以在需要的时候很容易地访问它:
public static class IocKernel
{
private static StandardKernel _kernel;
public static T Get()
{
return _kernel.Get();
}
public static void Initialize(params INinjectModule[] modules)
{
if (_kernel == null)
{
_kernel = new StandardKernel(modules);
}
}
}
This solution does make use of a static ServiceLocator (the IocKernel), which is generally regarded as an anti-pattern, because it hides the class' dependencies. However it is very difficult to avoid some sort of manual service lookup for UI classes, since they must have a parameterless constructor, and you cannot control the instantiation anyway, so you cannot inject the VM. At least this way allows you to test the VM in isolation, which is where all the business logic is.
这个解决方案确实使用了静态ServiceLocator (IocKernel),它通常被认为是一个反模式,因为它隐藏了类的依赖关系。但是,很难避免UI类的一些手动服务查找,因为它们必须有一个无参数的构造函数,而且无论如何您都无法控制实例化,因此无法注入VM。至少这种方式允许您单独测试VM,这是所有业务逻辑的所在。
If anyone has a better way, please do share.
如果有人有更好的方法,请分享。
EDIT: Lucky Likey provided an answer to get rid of the static service locator, by letting Ninject instantiate UI classes. The details of the answer can be seen here
编辑:Lucky Likey通过让Ninject实例化UI类,提供了摆脱静态服务定位器的答案。详细的答案可以在这里看到
34
In your question you set the value of the DataContext
property of the view in XAML. This requires that your view-model has a default constructor. However, as you have noted, this does not work well with dependency injection where you want to inject dependencies in the constructor.
在您的问题中,您在XAML中设置了视图的DataContext属性的值。这要求您的视图模型有一个默认构造函数。但是,正如您所注意到的,对于要在构造函数中注入依赖项的依赖项注入,这并不能很好地工作。
So you cannot set the DataContext
property in XAML. Instead you have other alternatives.
因此不能在XAML中设置DataContext属性。相反,你有其他的选择。
If you application is based on a simple hierarchical view-model you can construct the entire view-model hierarchy when the application starts (you will have to remove the StartupUri
property from the App.xaml
file):
如果您的应用程序基于简单的层次化视图模型,您可以在应用程序启动时构建整个视图模型层次结构(您必须从App.xaml文件中删除StartupUri属性):
public partial class App {
protected override void OnStartup(StartupEventArgs e) {
base.OnStartup(e);
var cOntainer= CreateContainer();
var viewModel = container.Resolve();
var window = new MainWindow { DataCOntext= viewModel };
window.Show();
}
}
This is based around an object graph of view-models rooted at the RootViewModel
but you can inject some view-model factories into parent view-models allowing them to create new child view-models so the object graph does not have to be fixed. This also hopefully answers your question suppose I need an instance of SomeViewModel
from my cs
code, how should I do it?
这是基于扎根于RootViewModel的视图模型的对象图,但是您可以将一些视图模型工厂注入到父视图模型中,从而允许它们创建新的子视图模型,这样对象图就不必被修复。这也很有希望回答你的问题假设我需要cs代码中的SomeViewModel实例,我该怎么做呢?
class ParentViewModel {
public ParentViewModel(ChildViewModelFactory childViewModelFactory) {
_childViewModelFactory = childViewModelFactory;
}
public void AddChild() {
Children.Add(_childViewModelFactory.Create());
}
ObservableCollection Children { get; private set; }
}
class ChildViewModelFactory {
public ChildViewModelFactory(/* ChildViewModel dependencies */) {
// Store dependencies.
}
public ChildViewModel Create() {
return new ChildViewModel(/* Use stored dependencies */);
}
}
If your application is more dynamic in nature and perhaps is based around navigation you will have to hook into the code that performs the navigation. Each time you navigate to a new view you need to create a view-model (from the DI container), the view itself and set the DataContext
of the view to the view-model. You can do this view first where you pick a view-model based on a view or you can do it view-model first where the view-model determines which view to use. A MVVM framework provides this key functionality with some way for you to hook your DI container into the creation of view-models but you can also implement it yourself. I am a bit vague here because depending on your needs this functionality may become quite complex. This is one of the core functions you get from a MVVM framework but rolling your own in a simple application will give you a good understanding what MVVM frameworks provide under the hood.
如果您的应用程序本质上更动态,并且可能基于导航,那么您将不得不钩入执行导航的代码。每次导航到新视图时,都需要创建视图模型(来自DI容器),视图本身并将视图的DataContext设置为view-model。您可以先在视图中选择视图模型,或者在视图模型确定要使用哪个视图时先进行视图模型。MVVM框架提供了这个关键功能,您可以通过某种方式将DI容器与视图模型的创建关联起来,但您也可以自己实现它。这里我有点模糊,因为根据您的需要,这个功能可能会变得非常复杂。这是MVVM框架的核心功能之一,但是在一个简单的应用程序中滚动自己的功能将使您很好地理解MVVM框架提供的内容。
By not being able to declare the DataContext
in XAML you lose some design-time support. If your view-model contains some data it will appear during design-time which can be very useful. Fortunately, you can use design-time attributes also in WPF. One way to do this is to add the following attributes to the
element or
in XAML:
由于不能在XAML中声明DataContext,您将丢失一些设计时支持。如果您的视图模型包含一些数据,它将在设计时出现,这将非常有用。幸运的是,您还可以在WPF中使用设计时属性。一种方法是将以下属性添加到XAML中的
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DataCOntext="{d:DesignInstance Type=local:MyViewModel, IsDesignTimeCreatable=True}"
The view-model type should have two constructors, the default for design-time data and another for dependency injection:
视图模型类型应该有两个构造函数,默认为设计时数据,另一个为依赖项注入:
class MyViewModel : INotifyPropertyChanged {
public MyViewModel() {
// Create some design-time data.
}
public MyViewModel(/* Dependencies */) {
// Store dependencies.
}
}
By doing this you can use dependency injection and retain good design-time support.
通过这样做,您可以使用依赖注入并保留良好的设计时支持。
12
I go for a "view first" approach, where I pass the view-model to the view's constructor (in its code-behind), which gets assigned to the data context, e.g.
我采用“视图优先”的方法,将视图模型传递给视图的构造函数(代码隐藏),并将其分配给数据上下文,例如。
public class SomeView
{
public SomeView(SomeViewModel viewModel)
{
InitializeComponent();
DataCOntext= viewModel;
}
}
This replaces your XAML-based approach.
这将取代基于xam的方法。
I use the Prism framework to handle navigation - when some code requests a particular view be displayed (by "navigating" to it), Prism will resolve that view (internally, using the app's DI framework); the DI framework will in turn resolve any dependencies that the view has (the view model in my example), then resolves its dependencies, and so on.
我使用Prism框架来处理导航——当某些代码请求显示特定视图时(通过“导航”到它),Prism将解析该视图(在内部,使用应用程序的DI框架);DI框架将依次解析视图拥有的任何依赖项(在我的示例中是视图模型),然后解析它的依赖项,依此类推。
Choice of DI framework is pretty much irrelevant as they all do essentially the same thing, i.e. you register an interface (or a type) along with the concrete type that you want the framework to instantiate when it finds a dependency on that interface. For the record I use Castle Windsor.
DI框架的选择几乎是无关紧要的,因为它们基本上都做相同的事情,例如,当框架在该接口上找到依赖项时,您将注册一个接口(或一个类型)以及希望该框架实例化的具体类型。我用温莎城堡作记录。
Prism navigation takes some getting used to but is pretty good once you get your head around it, allowing you to compose your application using different views. E.g. you might create a Prism "region" on your main window, then using Prism navigation you would switch from one view to another within this region, e.g. as the user selects menu items or whatever.
Prism导航需要一些时间来适应,但是一旦你熟悉了它,它就会变得非常好,允许你使用不同的视图来编写应用程序。例如,你可以在你的主窗口上创建一个“区域”棱镜,然后使用“区域”棱镜导航系统,你可以在这个区域内从一个视图切换到另一个视图,例如,当用户选择菜单项或其他内容时。
Alternatively take a look at one of the MVVM frameworks such as MVVM Light. I've got no experience of these so can't comment on what they're like to use.
或者看看MVVM框架中的一个,比如MVVM Light。我没有这些经验,所以不能评论它们的用途。
11
Install MVVM Light.
安装MVVM光。
Part of the installation is to create a view model locator. This is a class which exposes your viewmodels as properties. The getter of these properties can then be returned instances from your IOC engine. Fortunately, MVVM light also includes the SimpleIOC framework, but you can wire in others if you like.
安装的一部分是创建视图模型定位器。这是一个将视图模型公开为属性的类。然后可以从IOC引擎返回这些属性的getter。幸运的是,MVVM light还包含SimpleIOC框架,但是如果您愿意,也可以连接其他框架。
With simple IOC you register an implementation against a type...
使用简单的IOC,您可以根据类型注册实现……
SimpleIOC.Default.Register(()=> new MyViewModel(new ServiceProvider()), true);
In this example, your view model is created and passed a service provider object as per its constructor.
在本例中,您的视图模型是根据其构造函数创建并传递服务提供者对象的。
You then create a property which returns an instance from IOC.
然后创建一个属性,该属性返回IOC的实例。
public MyViewModel
{
get { return SimpleIOC.Default.GetInstance; }
}
The clever part is that the view model locator is then created in app.xaml or equivalent as a data source.
聪明的部分是,视图模型定位符然后在app.xaml中创建,或等效为数据源。
You can now bind to its 'MyViewModel' property to get your viewmodel with an injected service.
现在,您可以绑定到它的“MyViewModel”属性,通过注入的服务获得您的viewmodel。
Hope that helps. Apologies for any code inaccuracies, coded from memory on an iPad.
希望有帮助。对任何代码不准确的道歉,在iPad上用记忆编码。
11
What I'm posting here is an improvement to sondergard's Answer, because what I'm going to tell doesn't fit into a Comment :)
我在这里发布的是对sondergard的回答的改进,因为我要讲的内容不适合评论:
In Fact I am introducing a neat solution, which avoids the need of a ServiceLocator and a wrapper for the StandardKernel
-Instance, which in sondergard's Solution is called IocContainer
. Why? As mentioned, those are anti-patterns.
事实上,我正在引入一个简洁的解决方案,它避免了标准内核实例(在sondergard的解决方案中称为IocContainer)需要一个ServiceLocator和一个包装器。为什么?如前所述,这些是反模式。
StandardKernel
available everywhereThe Key to Ninject's magic is the StandardKernel
-Instance which is needed to use the .Get
-Method.
Ninject的神奇之处在于使用. get
Alternatively to sondergard's IocContainer
you can create the StandardKernel
inside the App
-Class.
另外,您也可以在application - class中创建StandardKernel,而不是sondergard的IocContainer。
Just remove StartUpUri from your App.xaml
从App.xaml中删除StartUpUri即可
...
This is the App's CodeBehind inside App.xaml.cs
这是App.xaml.cs里面的应用程序的代码。
public partial class App
{
private IKernel _iocKernel;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
_iocKernel = new StandardKernel();
_iocKernel.Load(new YourModule());
Current.MainWindow = _iocKernel.Get();
Current.MainWindow.Show();
}
}
From now on, Ninject is alive and ready to fight :)
从现在开始,Ninject还活着,准备战斗
DataContext
As Ninject is alive, you can perform all kinds of injections, e.g Property Setter Injection or the most common one Constructor Injection.
因为Ninject是活的,你可以进行各种注射。属性Setter注入或最常见的一个构造函数注入。
This is how you inject your ViewModel into your Window
's DataContext
这就是如何将ViewModel注入到窗口的DataContext中。
public partial class MainWindow : Window
{
public MainWindow(MainWindowViewModel vm)
{
DataCOntext= vm;
InitializeComponent();
}
}
Of course you can also Inject an IViewModel
if you do the right bindings, but that is not a part of this answer.
当然,如果您做了正确的绑定,也可以注入IViewModel,但这不是这个答案的一部分。
If you need to call Methods on the Kernel directly (e.g. .Get
-Method), you can let the Kernel inject itself.
如果需要直接调用内核上的方法(例如,get
private void DoStuffWithKernel(IKernel kernel)
{
kernel.Get();
kernel.Whatever();
}
If you would need a local instance of the Kernel you could inject it as Property.
如果需要内核的本地实例,可以将其作为属性注入。
[Inject]
public IKernel Kernel { private get; set; }
Allthough this can be pretty useful, I would not recommend you to do so. Just note that objects injected this way, will not be available inside the Constructor, because it's injected later.
虽然这很有用,但我不建议你这样做。请注意,以这种方式注入的对象在构造函数中不可用,因为稍后将注入。
According to this link you should use the factory-Extension instead of injecting the IKernel
(DI Container).
根据这个链接,您应该使用工厂扩展而不是注入IKernel (DI容器)。
The recommended approach to employing a DI container in a software system is that the Composition Root of the application be the single place where the container is touched directly.
在软件系统中使用DI容器的推荐方法是,应用程序的组合根是直接接触容器的惟一位置。
How the Ninject.Extensions.Factory is to be used can also be red here.
Ninject.Extensions。工厂在这里也可以用红色。
3
Use the Managed Extensibility Framework.
使用托管的可扩展性框架。
[Export(typeof(IViewModel)]
public class SomeViewModel : IViewModel
{
private IStorage _storage;
[ImportingConstructor]
public SomeViewModel(IStorage storage){
_storage = storage;
}
public bool ProperlyInitialized { get { return _storage != null; } }
}
[Export(typeof(IStorage)]
public class Storage : IStorage
{
public bool SaveFile(string content){
// Saves the file using StreamWriter
}
}
//Somewhere in your application bootstrapping...
public GetViewModel() {
//Search all assemblies in the same directory where our dll/exe is
string currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var catalog = new DirectoryCatalog(currentPath);
var cOntainer= new CompositionContainer(catalog);
var viewModel = container.GetExport();
//Assert that MEF did as advertised
Debug.Assert(viewModel is SomViewModel);
Debug.Assert(viewModel.ProperlyInitialized);
}
In general, what you would do is have a static class and use the Factory Pattern to provide you with a global container (cached, natch).
通常,您要做的是拥有一个静态类,并使用Factory模式为您提供一个全局容器(缓存的natch)。
As for how to inject the view models, you inject them the same way you inject everything else. Create an importing constructor (or put a import statement on a property/field) in the code-behind of the XAML file, and tell it to import the view model. Then bind your Window
's DataContext
to that property. Your root objects you actually pull out of the container yourself are usually composed Window
objects. Just add interfaces to the window classes, and export them, then grab from the catalog as above (in App.xaml.cs... that's the WPF bootstrap file).
至于如何注入视图模型,您的注入方式与注入其他所有内容的方式相同。在XAML文件的代码后面创建一个导入构造函数(或在属性/字段上放置一个导入语句),并告诉它导入视图模型。然后将窗口的DataContext绑定到该属性。实际上从容器中取出的根对象通常是由窗口对象组成的。只需向窗口类添加接口,并导出它们,然后从上面的目录中抓取(在app .xam .cs中……)这是WPF的引导文件)。
0
I would suggest to use the ViewModel - First approach https://github.com/Caliburn-Micro/Caliburn.Micro
我建议使用ViewModel - First方法https://github.com/calibury - micro/caliburn.micro。
see: https://caliburnmicro.codeplex.com/wikipage?title=All%20About%20Conventions
参见:https://caliburnmicro.codeplex.com/wikipage?title=All%20About%20Conventions
use Castle Windsor
as IOC container.
使用温莎城堡作为IOC容器。
All About Conventions
关于约定
One of the main features of Caliburn.Micro is manifest in its ability to remove the need for boiler plate code by acting on a series of conventions. Some people love conventions and some hate them. That’s why CM’s conventions are fully customizable and can even be turned off completely if not desired. If you are going to use conventions, and since they are ON by default, it’s good to know what those conventions are and how they work. That’s the subject of this article. View Resolution (ViewModel-First)
这是Caliburn的一个主要特点。通过一系列的惯例,微观的能力可以消除对锅炉板代码的需求。有些人喜欢习俗,有些人讨厌习俗。这就是为什么CM的约定是完全可定制的,如果不需要,甚至可以完全关闭。如果您打算使用约定,并且由于它们是默认的,那么了解这些约定是什么以及它们是如何工作的很好。这就是本文的主题。视图解析(ViewModel-First)
Basics
基础知识
The first convention you are likely to encounter when using CM is related to view resolution. This convention affects any ViewModel-First areas of your application. In ViewModel-First, we have an existing ViewModel that we need to render to the screen. To do this, CM uses a simple naming pattern to find a UserControl1 that it should bind to the ViewModel and display. So, what is that pattern? Let’s just take a look at ViewLocator.LocateForModelType to find out:
使用CM时可能遇到的第一个约定与视图解析有关。此约定影响应用程序的任何ViewModel-First区域。在ViewModel- first中,我们有一个现有的ViewModel,需要呈现给屏幕。为此,CM使用一个简单的命名模式来查找应该绑定到ViewModel和display的UserControl1。那么,这个模式是什么呢?让我们来看看ViewLocator。LocateForModelType找出:
public static Func LocateForModelType = (modelType, displayLocation, context) =>{
var viewTypeName = modelType.FullName.Replace("Model", string.Empty);
if(context != null)
{
viewTypeName = viewTypeName.Remove(viewTypeName.Length - 4, 4);
viewTypeName = viewTypeName + "." + context;
}
var viewType = (from assmebly in AssemblySource.Instance
from type in assmebly.GetExportedTypes()
where type.FullName == viewTypeName
select type).FirstOrDefault();
return viewType == null
? new TextBlock { Text = string.Format("{0} not found.", viewTypeName) }
: GetOrCreateViewType(viewType);
};
Let’s ignore the “context” variable at first. To derive the view, we make an assumption that you are using the text “ViewModel” in the naming of your VMs, so we just change that to “View” everywhere that we find it by removing the word “Model”. This has the effect of changing both type names and namespaces. So ViewModels.CustomerViewModel would become Views.CustomerView. Or if you are organizing your application by feature: CustomerManagement.CustomerViewModel becomes CustomerManagement.CustomerView. Hopefully, that’s pretty straight forward. Once we have the name, we then search for types with that name. We search any assembly you have exposed to CM as searchable via AssemblySource.Instance.2 If we find the type, we create an instance (or get one from the IoC container if it’s registered) and return it to the caller. If we don’t find the type, we generate a view with an appropriate “not found” message.
让我们先忽略“上下文”变量。为了导出视图,我们假设您在vm的命名中使用了文本“ViewModel”,因此我们只需将其更改为“view”(通过删除“Model”一词找到的所有地方)。这可以改变类型名和名称空间。所以视图模型。CustomerViewModel将成为Views.CustomerView。或者,如果您正在按特性组织应用程序:CustomerManagement。CustomerViewModel变得CustomerManagement.CustomerView。希望这是非常直接的。一旦我们有了这个名字,我们就会搜索这个名字的类型。我们将通过assembly . instance搜索您向CM公开的任何程序集。如果我们找到了该类型,我们将创建一个实例(或者从IoC容器中获取一个实例,如果它已注册)并将其返回给调用者。如果我们没有找到该类型,我们将生成一个带有适当“not found”消息的视图。
Now, back to that “context” value. This is how CM supports multiple Views over the same ViewModel. If a context (typically a string or an enum) is provided, we do a further transformation of the name, based on that value. This transformation effectively assumes you have a folder (namespace) for the different views by removing the word “View” from the end and appending the context instead. So, given a context of “Master” our ViewModels.CustomerViewModel would become Views.Customer.Master.
现在,回到“上下文”值。这就是CM在同一视图模型上支持多个视图的方式。如果提供了上下文(通常是字符串或枚举),我们将根据该值对名称进行进一步的转换。这种转换有效地假设您有一个文件夹(名称空间)用于不同的视图,方法是从末尾删除单词“View”,然后添加上下文。因此,在“掌握”我们的视图模型的上下文中。CustomerViewModel将成为Views.Customer.Master。
0
Remove the startup uri from your app.xaml.
从app.xaml中删除启动uri。
App.xaml.cs
App.xaml.cs
public partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
IoC.Configure(true);
StartupUri = new Uri("Views/MainWindowView.xaml", UriKind.Relative);
base.OnStartup(e);
}
}
Now you can use your IoC class to construct the instances.
现在可以使用IoC类来构造实例。
MainWindowView.xaml.cs
MainWindowView.xaml.cs
public partial class MainWindowView
{
public MainWindowView()
{
var mainWindowViewModel = IoC.GetInstance();
//Do other configuration
DataCOntext= mainWindowViewModel;
InitializeComponent();
}
}