MVVM ViewModel конструктор создан несколько раз - PullRequest
0 голосов
/ 06 апреля 2020

Может, кто-нибудь возражает помочь мне с проблемой понимания WPF MVVM? Я создал проект MVVM с caliburn.micro в качестве основы MVVM. Пожалуйста, потерпите меня, так как я впервые создал такой проект. Сетка должна отображать вид домашнего экрана (HomeViewModel). Поэтому привязка к нему была добавлена ​​в управление контентом. Может кто-нибудь помочь и сказать мне, почему конструктор моего HomeViewModel создается 3 раза? С другой стороны, было бы хорошо, если бы кто-то мог объяснить, как реализовать методы для ViewModels? Все они, включая ведение журнала, выполняются три раза.

<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>

Я реализовал SimpleContainer в соответствии с документацией caliburn.micro в моем загрузчике:

    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

Если конструктор вызывается несколько раз, DI-объект Singleton внутри контейнера должен возвращать существующий экземпляр, не так ли? Кстати: StartupUri был удален из app.xaml. Заранее спасибо!

1 Ответ

0 голосов
/ 06 апреля 2020

Эта разметка вызывает конструктор 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 раза. Все правильно.

Синглтон опасен.

...