c# - 多次实例化 MVVM ViewModel 构造函数
问题描述
有人介意帮助我解决 WPF MVVM 理解问题吗?我用 caliburn.micro 作为 MVVM 框架构建了一个 MVVM 项目。请多多包涵,因为这是我第一次创建这种项目。网格应该显示一种主屏幕(HomeViewModel)。因此,它的绑定已添加到内容控件中。有人可以帮忙告诉我为什么我的 HomeViewModel 的构造函数被实例化了 3 次吗?另一方面,如果有人可以解释如何实现 ViewModel 的方法,我会很好吗?所有这些,包括日志记录,都被执行了 3 次。
<Window [...]>
<Window.DataContext>
<viewModels:ShellViewModel />
</Window.DataContext>
<Grid>
<Grid.Resources>
<DataTemplate DataType="{x:Type viewModels:HomeViewModel}">
<local:HomeView DataContext="{Binding}" />
</DataTemplate>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*"/>
<ColumnDefinition Width="0.5*"/>
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="1">
<ContentControl.Content>
<Binding FallbackValue="{x:Null}"
Mode="OneWay"
Path="viewModels:HomeViewItem"
RelativeSource="{RelativeSource Self}" />
</ContentControl.Content>
</ContentControl>
</Grid>
我根据引导程序中的 caliburn.micro 文档实现了一个 SimpleContainer:
public class Bootstrapper : BootstrapperBase
{
private SimpleContainer _container;
public Bootstrapper()
{
Initialize();
}
protected override void Configure()
{
_container = new SimpleContainer();
_container.Singleton<IWindowManager, WindowManager>();
// Registering ViewModels
GetType().Assembly.GetTypes()
.Where(type => type.IsClass)
.Where(type => type.Name.EndsWith("ViewModel"))
.Where(type => !(String.IsNullOrWhiteSpace(type.Namespace)) && type.Namespace.EndsWith("ViewModels"))
.ToList()
.ForEach(viewModelType => _container.RegisterSingleton(
viewModelType, viewModelType.ToString(), viewModelType));
// Registering Views
GetType().Assembly.GetTypes()
.Where(type => type.IsClass)
.Where(type => type.Name.EndsWith("View"))
.Where(type => !(String.IsNullOrWhiteSpace(type.Namespace)) && type.Namespace.EndsWith("Views"))
.ToList()
.ForEach(viewModelType => _container.RegisterSingleton(
viewModelType, viewModelType.ToString(), viewModelType));
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<ShellViewModel>();
}
protected override object GetInstance(Type service, string key)
{
return _container.GetInstance(service, key);
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
我的 HomeViewModel 是这样实现的:
public class HomeViewModel : Conductor<IScreen>
{
public HomeViewModel()
{
Debug.WriteLine("Hello from HomeVM");
}
}
我的 ShellViewModel 是这样的:
public class ShellViewModel : Screen
{
private IScreen _homeViewItem;
public ShellViewModel()
{
CreateSourceItem();
}
public IScreen HomeViewItem
{
get => _homeViewItem;
set
{
if (Equals(value, _homeViewItem))
return;
_homeViewItem = value;
NotifyOfPropertyChange(() => HomeViewItem);
}
}
private void CreateSourceItem()
{
HomeViewItem = new HomeViewModel();
}
}
当应用程序启动时,调试输出中有 3 个 HomeViewModel 实现条目:
Hello from HomeVM
Hello from HomeVM
Hello from HomeVM
如果多次调用构造函数,容器内的 Singleton DI 应该返回现有实例,不是吗?顺便说一句: StartupUri 已从 app.xaml 中删除。提前致谢!
解决方案
此标记调用 ShellViewModel 的构造函数
<Window.DataContext>
<viewModels:ShellViewModel />
</Window.DataContext>
它通过调用 HomeViewModel 的构造函数
public ShellViewModel()
{
CreateSourceItem();
}
private void CreateSourceItem()
{
HomeViewItem = new HomeViewModel();
}
这里还有 2 个电话
GetType().Assembly.GetTypes()
.Where(type => type.IsClass)
.Where(type => type.Name.EndsWith("ViewModel"))
.Where(type => !(String.IsNullOrWhiteSpace(type.Namespace)) && type.Namespace.EndsWith("ViewModels"))
.ToList() // <= !!! look here in debugger, what ToList() returns.
// Here foreach loop calls constructor ShellViewModel()
// which calls HomeViewModel() via CreateSourceItem() again
// then the loop calls constructor of HomeViewModel() separately.
.ForEach(viewModelType => _container.RegisterSingleton(
viewModelType, viewModelType.ToString(), viewModelType));
总计: 3次。全部正确。
辛格尔顿很危险。
推荐阅读
- r - 当且仅当所有值都是 NA 时删除命名列
- php - PHP 7.3.13 为 foreach() 提供的参数无效
- node.js - UnhandledPromiseRejectionWarning:TypeError:无法读取 null 的属性“连接”
- android - 即使函数被调用,UI 也不会更新(¿ 竞争条件?)
- android - 是否可以在 iphone 中使用 Kotlin 代码?
- node.js - 将 JWT 有效负载转换为 Mongoose 模型实例
- ms-access - Access中使用时间的百分位函数
- automation - 如何通过自动化脚本刷新 Maximo 中的页面
- java - Citrus 框架 - 尝试在 JSON 中使用验证器会导致标头 ACCEPT 出错
- powershell - 如何在没有 DnsServer 模块的情况下使用 PowerShell 检索所有 DNS 记录?