有人介意帮助我解决WPF MVVM理解问题吗?我使用caliburn.micro作为MVVM框架构建了MVVM项目。请忍受,因为这是我第一次创建此类项目。网格应显示一种主屏幕(HomeViewModel)。因此,已将其上的绑定添加到内容控件中。有人可以告诉我为什么HomeViewModel的构造函数被实例化3次吗?另一方面,如果有人可以解释如何向ViewModels实现方法,我会很好。所有这些操作(包括日志记录)都执行了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()
// here foreach loop calls constructor ShellViewModel()
// which calls HomeViewModel() via CreateSourceItem() again
// then the loop it calls constructor of HomeViewModel() separately.
.ForEach(viewModelType => _container.RegisterSingleton(
viewModelType, viewModelType.ToString(), viewModelType));
总计: 3次。完全正确。
单身危险。