MVVM ViewModel构造函数实例化多次

问题描述 投票:0回答:1

有人介意帮助我解决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中删除。提前致谢!

c# wpf mvvm constructor caliburn.micro
1个回答
0
投票

此标记调用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次。完全正确。

单身危险。

© www.soinside.com 2019 - 2024. All rights reserved.