如何在MVVM中使用同一ViewModel创建多个视图



我是WPF和MVVM的新手,在尝试在两个不同的视图中将DataContext设置为ViewModel的同一实例时遇到了问题。

这是因为:

<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>

将为每个视图创建一个新的视图模型实例。

为了解决这个问题,我决定创建一个类来存储我使用的每个ViewModel的静态实例。然后,在每个视图的cs文件中,我将从这个静态类中将DataContext设置为适当的ViewModel。

对于可能同时需要ViewModel的多个实例的大型程序来说,这是可行的,但似乎不是最好的主意。

有什么更好的方法来解决这个问题?有没有合理的方法可以使用ViewModel的同一实例来创建多个视图?

或者这种方法是一种糟糕的做法——我应该为每个ViewModel设计一个视图的程序吗?

您可以在App.xaml中实例化该视图模型,以便整个应用程序都可以访问它。

<Application.Resources>
<local:ViewModel x:Key="sharedViewModel" />
</Application.Resources>

然后,在您的视图中,当您想要使用该数据上下文时,您可以执行以下操作。。。

DataContext="{StaticResource sharedViewModel}"

我也有同样的问题,但我找不到一个好的答案。经过一段时间的思考,我得出的结论是,在大多数情况下,最好在视图模型和视图之间创建一对一的映射。因此,在这种情况下,我将创建两个独立的视图模型,它们继承自基本视图模型。这样,您就可以在基本视图模型中放置任何常见的内容,并添加任何可能与更具体的视图模型不同的字段或方法。如果视图模型真的是等效的,那么你可能想问问自己,为什么一开始就有两个独立的视图。您可以考虑将它们合并到一个视图中。有两种不同的观点可能是你想要的,但这只是需要考虑的事情。

简单易行,推荐的方法之一是实现ViewModelLocator。

Idea在ViewModelLocator类中定义了所有ViewModel,并在需要的地方访问ViewModel。在不同的视图中使用相同的ViewModel不会成为问题。

public class ViewModelLocator
{
private MainWindowViewModel mainWindowViewModel;
public MainWindowViewModel MainWindowViewModel
{
get
{
if (mainWindowViewModel == null)
mainWindowViewModel = new MainWindowViewModel();
return mainWindowViewModel;
}
}
private DataFactoryViewModel dataFactoryViewModel;
public DataFactoryViewModel DataFactoryViewModel
{
get
{
if (dataFactoryViewModel == null)
dataFactoryViewModel = new DataFactoryViewModel();
return dataFactoryViewModel;
}
}
}

App.xaml

xmlns:core="clr-namespace:MyViewModelLocatorNamespace"
<Application.Resources>
<core:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>

使用

<Window ...
DataContext="{Binding Path=MainWindowViewModel, Source={StaticResource ViewModelLocator}}">

参考:所以问题代码是从那里复制的。。因为我不能从我的项目中剽窃代码。。

我正在使用棱镜框架,同时也在寻找一种将一个视图模型用于多个(子(视图的解决方案。在棱镜中有两种可能的解决方案:

  1. 将视图模型定义为singleton(代码气味(或
  2. 使用RegionContext(有关完整描述,请参阅棱镜文档(

根据棱镜文档,我的解决方案是这样的。

在棱镜引导器:

Container.RegisterTypeForNavigation<MyView>("MyView");

在视图模型中:

private void DisplayView(string viewName)
{
RegionManager.Regions["ContentRegion"].Context = this;  // set viewmodel
RegionManager.RequestNavigate("ContentRegion", viewName);
}

在每个视图的代码后面:

public MyView()
{
InitializeComponent();
ObservableObject<object> viewRegionContext = RegionContext.GetObservableContext(this);
viewRegionContext.PropertyChanged += this.ViewRegionContext_OnPropertyChangedEvent;
}
private void ViewRegionContext_OnPropertyChangedEvent(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "Value")
{
var context = (ObservableObject<object>)sender;
DataContext = context.Value;  // get viewmodel as DataContext
}
}

@Fabulous的staticsource/singleton方法(这就是它的名字吗?我不擅长编程术语(非常棒,因为它是简单的代码,易于实现,但它也意味着即使你不明确希望它执行,你的代码也可以执行,这可能会引起一些麻烦。

在我的情况下,它会执行,例如在重建时(不是很糟糕,但不是很好(,在重新打开解决方案时(ugh(,以及在编辑app.xaml的部分时(由于某种原因,这会软锁定VS2019,即非常糟糕(。据我所知,那些知道自己在做什么的人可能已经知道这会发生,但作为一个新手程序员,弄清楚发生了什么是一件很痛苦的事,哈哈。

保持简单性优势而不因虚幻的运行代码而毁了你的一天的一种方法是对你的主要代码进行简单的检查,看看你是否在设计时运行(基本上是@WPFUser链接的简化版本(。

示例:

using System.ComponentModel;
using System.Windows;
public MainCode()
{
if (IsInDesignMode() == false)
{
//execute code normally
}
else
{
//do nothing (or maybe display an info message or something)
}
}
private DependencyObject dummy = new DependencyObject();
private bool IsInDesignMode()
{
return DesignerProperties.GetIsInDesignMode(dummy);
}

(加上仍然使用Fabulous答案中提到的所有内容(

我还没有对此进行广泛的测试,所以请注意,它可能不是100%万无一失的,但到目前为止,对我来说,它在我测试它的方式上运行得很好。

我遇到了同样的情况,只是我在堆栈面板中使用了两个用户控件。因此,使用我在StackPanel中设置的公共DataContext对两个用户控件都有效,如下所示

<StackPanel>
<StackPanel.DataContext>
<vm:RegisterViewModel/>
</StackPanel.DataContext>
<local:Register />
<local:Users />
</StackPanel>

最新更新