创建视图模型时如何传递对 class 实例的引用
How to pass reference to class instance when creating viewmodel
我正在开发一个包含主视图和 5 个用户控件的程序。我已经创建了 XAML 并创建了一个视图模型以位于其中也绑定了视图的每个视图的后面。
我有一个主要的 Program
class,我想要一些其他的 class,例如 product
、testTool
等
当应用程序启动时,我加载 mainWindow
,然后将创建 mainWindowViewModel
,然后创建 Program
class.
当用户按下按钮时,我希望 mainWindowViewModel
显示 userControl1
,但我希望 userControl1ViewModel
能够看到 Program
class并访问它的属性和方法。
我一直在阅读 "pass the instance of the class in by reference" 之类的东西,这很好,但是如果 userControl1View
创建 userControl1ViewModel
我如何传递对 'program' class 创建的引用在程序开始时?
您所说的实际上并不是简单的过程,您所说的是在您期望的地方获得您期望的参考的架构。这可以通过多种方式解决,所以我将在下面抛出一个相当不合理但非常快速的示例。架构问题与 // 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
也可以是 Program
中的静态 id
这就是依赖注入旨在解决的问题。
首先,如果您正在执行 MVVM,那么您应该能够 运行 您的整个应用程序而无需创建任何视图,即仅视图模型。如果您有一个带有 ChildView(比如说)的 MainWindow,那么通常您会将它们与相应的视图模型相匹配:
public MainViewModel : ViewModeBase
{
public ChildViewModel MyChild {get; } // gets assigned later
然后在你的 XAML:
<Window ...
<local:ChildView DataContext="{Binding MyChild}" />
有时您需要 MyChild 显示不同的视图,每个视图都有自己对应的视图模型,您可能需要在 运行 时更改它。在这些情况下,MyChild 将需要是对象类型(或一些公共基础 class)并且还需要支持 属性 更改通知:
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}" />
在这个地方,您可以在 window 或应用程序资源部分使用 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
运算符)创建所有视图模型,并将所有位粘合在一起。例如,您的产品可能是管理所有产品的存储库 class 的一部分,因此您首先要声明一个接口:
public interface IProductRepository
{
Products[] AllProducts();
Product GetProductByName(string name);
... etc ...
然后您创建一个实际的 class 来实现此接口,并在设置过程中为您的依赖框架指定每当有任何请求 IProductRepository 时它应该做什么的规则(使用单个实例,创建一个新实例等) ).然后,每当您的整个应用程序中的任何内容需要访问产品存储库时,它所要做的就是声明一个带有 [Inject]
标记的 属性(这是如果您使用 Ninject,每个库有它自己的方式来做到这一点):
public class MyClass
{
[Inject]
public IProductRepository ProductRepo {get; set;} // <-- gets injected
现在,当您创建 MyClass 类型的实例时,依赖注入框架将为您创建它并使用您提供的规则自动初始化 ProductRepo。
这是对 DataTemplating 和 Dependency Injection 如何在 MVVM 中工作的一个非常简单的概述,一旦您开始使用它们,您就会想知道没有它们您是如何管理的。据我所知,你问题中的主要问题是你试图让你的视图模型相互交谈。一般来说,这不是 MVVM 的实现方式。视图模型通过注入到它们中的服务进行通信根据一般经验法则,它们的工作是充当这些服务和前端 GUI 元素之间的管道,而不是彼此之间的管道。
我正在开发一个包含主视图和 5 个用户控件的程序。我已经创建了 XAML 并创建了一个视图模型以位于其中也绑定了视图的每个视图的后面。
我有一个主要的 Program
class,我想要一些其他的 class,例如 product
、testTool
等
当应用程序启动时,我加载 mainWindow
,然后将创建 mainWindowViewModel
,然后创建 Program
class.
当用户按下按钮时,我希望 mainWindowViewModel
显示 userControl1
,但我希望 userControl1ViewModel
能够看到 Program
class并访问它的属性和方法。
我一直在阅读 "pass the instance of the class in by reference" 之类的东西,这很好,但是如果 userControl1View
创建 userControl1ViewModel
我如何传递对 'program' class 创建的引用在程序开始时?
您所说的实际上并不是简单的过程,您所说的是在您期望的地方获得您期望的参考的架构。这可以通过多种方式解决,所以我将在下面抛出一个相当不合理但非常快速的示例。架构问题与 // 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
也可以是 Program
这就是依赖注入旨在解决的问题。
首先,如果您正在执行 MVVM,那么您应该能够 运行 您的整个应用程序而无需创建任何视图,即仅视图模型。如果您有一个带有 ChildView(比如说)的 MainWindow,那么通常您会将它们与相应的视图模型相匹配:
public MainViewModel : ViewModeBase
{
public ChildViewModel MyChild {get; } // gets assigned later
然后在你的 XAML:
<Window ...
<local:ChildView DataContext="{Binding MyChild}" />
有时您需要 MyChild 显示不同的视图,每个视图都有自己对应的视图模型,您可能需要在 运行 时更改它。在这些情况下,MyChild 将需要是对象类型(或一些公共基础 class)并且还需要支持 属性 更改通知:
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}" />
在这个地方,您可以在 window 或应用程序资源部分使用 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
运算符)创建所有视图模型,并将所有位粘合在一起。例如,您的产品可能是管理所有产品的存储库 class 的一部分,因此您首先要声明一个接口:
public interface IProductRepository
{
Products[] AllProducts();
Product GetProductByName(string name);
... etc ...
然后您创建一个实际的 class 来实现此接口,并在设置过程中为您的依赖框架指定每当有任何请求 IProductRepository 时它应该做什么的规则(使用单个实例,创建一个新实例等) ).然后,每当您的整个应用程序中的任何内容需要访问产品存储库时,它所要做的就是声明一个带有 [Inject]
标记的 属性(这是如果您使用 Ninject,每个库有它自己的方式来做到这一点):
public class MyClass
{
[Inject]
public IProductRepository ProductRepo {get; set;} // <-- gets injected
现在,当您创建 MyClass 类型的实例时,依赖注入框架将为您创建它并使用您提供的规则自动初始化 ProductRepo。
这是对 DataTemplating 和 Dependency Injection 如何在 MVVM 中工作的一个非常简单的概述,一旦您开始使用它们,您就会想知道没有它们您是如何管理的。据我所知,你问题中的主要问题是你试图让你的视图模型相互交谈。一般来说,这不是 MVVM 的实现方式。视图模型通过注入到它们中的服务进行通信根据一般经验法则,它们的工作是充当这些服务和前端 GUI 元素之间的管道,而不是彼此之间的管道。