我正在开发一个包含主视图和5个用户控件的程序。我已经创建了XAML并创建了一个视图模型,使其位于其中也绑定了视图的每个视图的后面。
我有一个主要的Program
类,并且我想拥有其他一些类,例如product
,testTool
等。
在应用程序启动时,我加载mainWindow
,然后将创建mainWindowViewModel
,并依次创建Program
类。
当用户按下按钮时,我希望mainWindowViewModel
显示userControl1
,但是我希望userControl1ViewModel
能够看到Program
类并访问其属性和方法
我一直在阅读“通过引用传递类的实例”之类的内容,但是如果userControl1View
创建了userControl1ViewModel
,我该如何传递对在创建的'program'类的引用呢?程序启动了吗?
答案 0 :(得分:1)
这是依赖注入旨在解决的问题。
首先,如果您正在执行MVVM,那么您应该能够运行整个应用程序而无需创建任何视图,即仅创建视图模型。如果您的MainWindow带有ChildView(例如),则通常将它们与相应的视图模型匹配:
public MainViewModel : ViewModeBase
{
public ChildViewModel MyChild {get; } // gets assigned later
然后在您的XAML中:
<Window ...
<local:ChildView DataContext="{Binding MyChild}" />
有时候,您需要MyChild显示不同的视图,每个视图都有其自己的对应视图模型,并且您可能需要在运行时进行更改。在这种情况下,MyChild必须是object类型(或某些常见的基类),并且还需要支持属性更改通知:
public class MainViewModel : ViewModelBase
{
private object _MyChild;
public object MyChild
{
get { return this._MyChild; }
set
{
if (this._MyChild != value)
{
this._MyChild = value;
RaisePropertyChanged(() => this.MyChild);
}
}
}
}
然后在XAML中,您改为创建ContentControl:
<Window ...
<ContentControl ="{Binding MyChild}" />
在这里放置之后,您可以在窗口或应用程序“资源”部分中使用DataTemplate来指定哪些视图与哪些视图模型匹配:
<DataTemplate DataType="{x:Type vm:FooViewModel}">
<view:FooView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:BarViewModel}">
<view:BarView />
</DataTemplate>
现在,如果您在MainViewModel中执行类似的操作...
this.MyChild = new FooViewModel();
... ContentControl将自动填充为FooView类型的控件。此外,它将DataContext自动设置为您创建的FooViewModel的实例。然后您像这样重新分配它:
this.MyChild = new BarViewModel();
...然后FooView将被替换为BarView。
因此,在使用DataTemplating的情况下,您只需担心将ViewModel的引用彼此传递,这就是依赖注入的地方。这是一个很大的话题,我建议您继续阅读,但是这个想法是通过DI框架(而不是new
运算符)创建所有视图模型,然后将所有位粘合在一起。例如,您的产品可能是管理所有产品的存储库类的一部分,因此首先声明一个接口:
public interface IProductRepository
{
Products[] AllProducts();
Product GetProductByName(string name);
... etc ...
然后,您将创建一个实现此接口的实际类,并在安装过程中为依赖框架提供规则,以规定任何东西请求IProductRepository(使用单个实例,创建一个新实例等)时应执行的操作。然后,只要您整个应用程序中的任何内容需要访问产品存储库,它要做的就是声明带有[Inject]
标签的属性(这是如果您使用Ninject,则每个库都有其自己的方式) :
public class MyClass
{
[Inject]
public IProductRepository ProductRepo {get; set;} // <-- gets injected
现在,当您创建MyClass类型的实例时,依赖项注入框架将为您创建它,并使用您提供的规则自动初始化ProductRepo。
这是MVVM中的DataTemplating和依赖注入的工作原理的非常简单的概述,一旦开始使用它们,您会想知道如果没有它们,您将如何进行管理。据我所知,问题中的主要问题是您正在尝试使视图模型彼此对话。通常,这不是MVVM的实现方式。视图模型通过注入到其中的服务进行通信作为一般经验法则,它们的工作是充当这些服务与前端GUI元素之间的管道,而不是彼此之间。
答案 1 :(得分:0)
您所谈论的实际上不是一个简单的过程,您所谈论的是架构,以便在期望的位置获得期望的引用。这可以通过很多方法来解决,因此下面我将抛出一个相当不合理但非常快速的示例。体系结构问题与// HACK:
s
通常,您会希望模型来自中央位置,例如数据库支持,该位置控制着正确实例的移交。
public abstract class Model
{
// HACK: Don't bother wiring up OnPropertyChanged here, since we don't expect ID to get updated that often, but need a public setter for the Provider
Guid ID { get; set; }
}
// HACK: While using a generic here makes for readable code, it may become problematic if you want to inherit your models
public class ModelProvider<TModelType> where TModelType : Model, new()
{
// HACK: Use better dependency injection than this
private static ModelProvider<TModelType> _instance = new ModelProvider<TModelType>();
public static ModelProvider<TModelType> Instance => _instance;
private ModelProvider() { }
// TODO: Make this into a dictionary of WeakReferences so that you're not holding stale data in memory
ConcurrentDictionary<Guid, TModelType> LoadedModels = new Dictionary<Guid, TModelType>();
private TModelType GenerateModel(Guid id) => new TModelType { ID = id };
private TModelType LoadKnownModel(Guid id)
{
throw new NotImplementedException("Implement a data store to get known models");
}
public TModelType GetNew() => LoadedModels.AddOrUpdate(Guid.NewGuid(). GenerateModel);
public TModelType GetById(Guid id) => LoadedModels.GetOrAdd(id, LoadKnownModel);
}
然后您的ViewModel可以访问
ModelProvider<Product>.Instance.GetById(WellKnownGuid);
对于测试,WellKnownGuid
可能也是程序中的静态ID