C# 我应该在MVVM项目中使用其他容器吗?
示例项目的来源: 我在WPF MVVM中做了一个项目。它有三个子项目:WPF应用程序、ViewModels(PCL)和Domain(PCL)。WPF只是一个单一的C# 我应该在MVVM项目中使用其他容器吗?,c#,.net,wpf,xaml,mvvm,C#,.net,Wpf,Xaml,Mvvm,示例项目的来源: 我在WPF MVVM中做了一个项目。它有三个子项目:WPF应用程序、ViewModels(PCL)和Domain(PCL)。WPF只是一个单一的窗口,有一个框架和两个页面。我将展示代码,但建议在repo中克隆/分叉我准备好的示例 以下是WPF客户端的代码: App.xaml.cs namespace NaviWPFApp { using System.Windows; using NaviWPFApp.Views; using NaviWPFApp.Vi
窗口
,有一个框架
和两个页面
。我将展示代码,但建议在repo中克隆/分叉我准备好的示例
以下是WPF客户端的代码:
App.xaml.cs
namespace NaviWPFApp
{
using System.Windows;
using NaviWPFApp.Views;
using NaviWPFApp.Views.Pages;
public partial class App : Application
{
public static NavigationService Navigation;
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
Navigation = new NavigationService(mainWindow.MyFrame);
Navigation.Navigate<FirstPage>();
}
}
}
我的业务逻辑是:
public class BusinessLogic
{
private int counter = 0;
public bool CountSomething()
{
return ++counter > 10;
}
}
依赖关系很简单:域除了自己的操作之外,什么都不知道,视图模型知道域,但对视图和视图却一无所知。。。好吧,这是我的问题-它知道ViewModel,但View应该知道域吗?我会解释我的担忧,但我的意思是:
第一个问题:如您所见,导航都在ViewModel中完成,业务逻辑仅在第二个页面视图模型中使用。SecondPage视图不需要了解逻辑
第二个问题:因为我试图坚持依赖注入,所以我想在程序开始时创建我的域对象(我只需要一个)。因此在受保护的override void OnStartup(StartupEventArgs e)
中,因此在视图中。我不知道如何将它传递给第二个视图模型,它是在ViewModelLocator
中创建的
所以我的问题是:如何转换这段代码,使它更加面向ViewModel?我只想将我的域对象注入ViewModel(它所属的位置),而不是视图
谢谢你的建议 简单的回答是,您无法避免它,尤其是当您使用依赖注入/IoC容器时(建议使用该容器来解耦代码并提高可测试性) 您无法避免这种情况的原因是(在使用IoC时)您需要通过构造函数(或者通过属性/方法)注入依赖项。大多数IoC容器都要求这些是公共的 由于以这种方式注入的大多数类型不在ViewModel PCL中,而是在模型(域)中,并且是公开可见的,因此需要对程序集的引用,即使您没有手动初始化ViewModels 你的困惑来自“观点”。在本例中,您的WPF项目有多个角色
NavigationService
implementation(INavigationService
接口属于ViewModel组件)虽然您的视图不需要任何对域类的引用,但您的应用程序需要(组合根目录、DI/IoC容器)。简而言之,您无法避免它,尤其是如果您使用依赖项注入/IoC容器(建议将代码解耦并提高可测试性) 您无法避免这种情况的原因是(在使用IoC时)您需要通过构造函数(或者通过属性/方法)注入依赖项。大多数IoC容器都要求这些是公共的 由于以这种方式注入的大多数类型不在ViewModel PCL中,而是在模型(域)中,并且是公开可见的,因此需要对程序集的引用,即使您没有手动初始化ViewModels 你的困惑来自“观点”。在本例中,您的WPF项目有多个角色
NavigationService
implementation(INavigationService
接口属于ViewModel组件)虽然您的视图不需要对域类进行任何引用,但您的应用程序确实需要(合成根,DI/IoC容器)。您无法避免这种依赖关系,因为
App.OnStartup
是合成根,这意味着App.OnStartup
知道一切。
但是,你可以避免的是应用程序中的这个全局道具:公共静态导航服务导航代码>。您可以简单地将它注入到需要它的对象中
第一个问题:正如您所看到的,导航都是在中完成的
ViewModel,业务逻辑仅在第二个页面视图模型中使用。
SecondPage视图不需要了解逻辑
SecondPage不必了解业务对象。应用程序必须知道。所以,您可以将对象注入定位器,定位器可以在时机成熟时将此对象注入特定的ViewModel
第二个问题:因为我正努力坚持依赖注入,
我想创建我的域对象(我只需要一个)
节目开始了。因此,在受保护的覆盖无效
启动时(StartupEventArgs e),因此在视图中。我也不知道该怎么做
将其传递给在ViewModelLocator中创建的第二个视图模型
依赖注入
我是这样做的:
public class ViewModelLocator
{
private NavigationService navigationService;
private BusinessLogic businessLogic;
public void InjectNavigationService(NavigationService navigation)
{
navigationService = navigation;
}
public void InjectBusinessLogic(BusinessLogic logic)
{
businessLogic = logic;
}
public FirstPageViewModel FirstPageViewModel => new FirstPageViewModel(navigationService);
public SecondPageViewModel SecondPageViewModel => new SecondPageViewModel(navigationService, businessLogic);
}
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Create/resolve all your objects in Comoposition Root:
var businessLogic = new BusinessLogic();
// Here you will have locator created already, but mainWindow has not been created yet
// Retrive your locator
ViewModelLocator locator = Resources.Values.OfType<ViewModelLocator>().FirstOrDefault();
if (locator == null)
throw new NoNullAllowedException("ViewModelLocator cannot be null.");
MainWindow mainWindow = new MainWindow();
var navigation = new NavigationService(mainWindow.MyFrame);
// Inject your logic and navigation into locator
locator.InjectBusinessLogic(businessLogic);
locator.InjectNavigationService(navigation);
// Set up first page
navigation.Navigate<FirstPage>();
// and show the window
mainWindow.Show();
}
}
公共类ViewModelLocator
{
私人导航服务导航服务;
私有业务逻辑;
公共导航服务(导航服务导航)
{
导航服务=导航;
}
公共业务逻辑(BusinessLogic逻辑)
{
业务逻辑=逻辑;
}
public FirstPageViewModel FirstPageViewModel=>新的FirstPageViewModel(导航服务);
public SecondPageViewModel SecondPageViewModel=>new SecondPageViewModel(导航服务、业务逻辑);
}
公共部分类应用程序:应用程序
{
启动时受保护的覆盖无效(StartupEventArgs e)
{
基础。启动时(e);
//在根目录中创建/解析所有对象:
var businessLogic=new businessLogic();
//这里有定位器crea
namespace NaviWPFApp
{
using NaviWPFApp.ViewModels.Pages;
public class ViewModelLocator
{
public FirstPageViewModel FirstPageViewModel => new FirstPageViewModel(App.Navigation);
public SecondPageViewModel SecondPageViewModel => new SecondPageViewModel(App.Navigation);
}
}
namespace NaviWPFApp
{
using System;
using System.Linq;
using System.Reflection;
using System.Windows.Controls;
using NaviWPFApp.ViewModels.Common;
public class NavigationService : INavigationService
{
readonly Frame frame;
public NavigationService(Frame frame)
{
this.frame = frame;
}
public void GoBack()
{
frame.GoBack();
}
public void GoForward()
{
frame.GoForward();
}
public bool Navigate(string page)
{
var type = Assembly.GetExecutingAssembly().GetTypes().SingleOrDefault(a => a.Name.Equals(page));
if (type == null) return false;
var src = Activator.CreateInstance(type);
return frame.Navigate(src);
}
public bool Navigate<T>(object parameter = null)
{
var type = typeof(T);
return Navigate(type, parameter);
}
public bool Navigate(Type source, object parameter = null)
{
var src = Activator.CreateInstance(source);
return frame.Navigate(src, parameter);
}
}
}
public interface INavigationService
{
void GoForward();
void GoBack();
bool Navigate(string page);
}
namespace NaviWPFApp.ViewModels
{
public class FirstPageViewModel : MyObservableObject
{
private readonly INavigationService navigationService;
public FirstPageViewModel(INavigationService navigationService)
{
this.navigationService = navigationService;
}
public MyCommand GoToSecondPageCommand
{
get { return new MyCommand(x => navigationService.Navigate("SecondPage")); }
}
}
}
namespace NaviWPFApp.ViewModels
{
public class SecondPageViewModel : MyObservableObject
{
private readonly INavigationService navigationService;
private readonly BusinessLogic businessLogic;
public SecondPageViewModel(INavigationService navigationService, BusinessLogic businessLogic = null)
{
this.navigationService = navigationService;
this.businessLogic = businessLogic;
}
public MyCommand BackToFirstPageCommand
{
get { return new MyCommand(x => navigationService.Navigate("FirstPage")); }
}
public MyCommand CountSomethingCommand
{
get { return new MyCommand(x => businessLogic?.CountSomething()); }
}
}
}
public class BusinessLogic
{
private int counter = 0;
public bool CountSomething()
{
return ++counter > 10;
}
}
public class ViewModelLocator
{
private NavigationService navigationService;
private BusinessLogic businessLogic;
public void InjectNavigationService(NavigationService navigation)
{
navigationService = navigation;
}
public void InjectBusinessLogic(BusinessLogic logic)
{
businessLogic = logic;
}
public FirstPageViewModel FirstPageViewModel => new FirstPageViewModel(navigationService);
public SecondPageViewModel SecondPageViewModel => new SecondPageViewModel(navigationService, businessLogic);
}
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Create/resolve all your objects in Comoposition Root:
var businessLogic = new BusinessLogic();
// Here you will have locator created already, but mainWindow has not been created yet
// Retrive your locator
ViewModelLocator locator = Resources.Values.OfType<ViewModelLocator>().FirstOrDefault();
if (locator == null)
throw new NoNullAllowedException("ViewModelLocator cannot be null.");
MainWindow mainWindow = new MainWindow();
var navigation = new NavigationService(mainWindow.MyFrame);
// Inject your logic and navigation into locator
locator.InjectBusinessLogic(businessLogic);
locator.InjectNavigationService(navigation);
// Set up first page
navigation.Navigate<FirstPage>();
// and show the window
mainWindow.Show();
}
}