两次创建Prism / WPF视图/视图模型 [英] Prism/WPF views/viewmodels being created twice

查看:68
本文介绍了两次创建Prism / WPF视图/视图模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Prism 7 / WPF / MVVM应用程序,该应用程序在具有viewmodel的视图中配置了AutowireViewModel = True。我的大多数视图模型都具有在Prism Unity容器中配置的依赖项,并且这些依赖项已注入到viewmodel构造器中。我没有在任何地方后面的代码中显式创建视图/视图模型的实例。我也没有在XAML中设置数据上下文,即使用DataContext元素或d:DataContext)。 XAML中唯一的引用是视图(即HamburgerMenu控件的一部分)。

I have a Prism 7 / WPF / MVVM app that is configured with AutowireViewModel="True" in the views that have a viewmodel. Most of my view models have dependencies that I have configured in the Prism Unity container and these dependencies are injected into the viewmodel contructor. I do not explicitly create instances of views/viewmodels in the code behind anywhere. I also do not set the data context in XAML i.e. using DataContext element or d:DataContext). The only references in the XAML are to views (i.e. part of the HamburgerMenu control).

所有工作都很好,只是出于某种原因总是每次构造两次视图/视图模型,无论是主窗口还是模块内的视图。我在模块管理器中放置了断点(仅被命中一次)-以及都被命中两次的视图构造函数和视图模型构造函数中都设置了断点。
以下是App类的一些代码,以及在运行时未按需加载的名为Messaging的模块的一些代码/视图/视图模型。由于希望尽可能多地使用MVVM模式,因此视图后面的代码中几乎没有代码。注意:我试图查看这是否只是启动时加载的模块的视图问题,但是按需加载的模块也存在同样的命运。

All is working fine except each view/viewmodel is ALWAYS constructed twice for some reason, whether this is the main window, or views within modules. I have placed breakpoints in the module managers (only gets hit once) - and also in the view constructors and viewmodel constructors which are both hit twice. The following is some code for App class and also some code/view/viewmodel for a module named Messaging that is loaded at runtime not on demand. Due to wishing to use the MVVM pattern as much as possible, I have very little code in the code behind of the views. NOTE: I tried to see if this is just an issue with views for modules that are loaded on start up, but the same fate is present for the modules loaded on demand.

完整的App / PrismApplication类:

The App/PrismApplication class in full:

public partial class App : PrismApplication
{
    private ILoggerFactory loggerFactory;
    private TaskbarIcon _taskbarIcon;

    protected override Window CreateShell()
    {
        // Register an instance of the Window (used by the dialog service to get a handle on the window when displaying dialogs)
        Container.GetContainer().RegisterInstance(typeof(Window), "MainWindow", Container.Resolve<MainWindow>(), new ContainerControlledLifetimeManager());

        return Container.Resolve<MainWindow>();
    }

    protected override void OnInitialized()
    {
        base.OnInitialized();

        // Create the links to each module for the banner view to display
        var menuService = Container.Resolve<IMenuService>();
        var regionManager = Container.Resolve<IRegionManager>();

        menuService.AddItem("Home", "Home", () => regionManager.RequestNavigate("MainRegion", "HomeMainView"));
        menuService.AddItem("Messaging", "Messaging", () => regionManager.RequestNavigate("MainRegion", "MessagingMainView"));
        menuService.AddItem("Charts", "Charts", () => regionManager.RequestNavigate("MainRegion", "ChartsMainView"));
        menuService.AddItem("Admin", "Admin", () => regionManager.RequestNavigate("MainRegion", "AdminMainView"));

        // Register banner view with region manager and display it
        regionManager.RegisterViewWithRegion("BannerRegion", typeof(BannerView));

        // Load the desired module into the main window on start up
        Container.Resolve<IRegionManager>().RequestNavigate("MainRegion", "HomeMainView");
    }

    protected override void RegisterTypes(IContainerRegistry containerRegistry)
    {
        var container = this.ConfigureLogging(containerRegistry);

        // Register types 
        container.RegisterInstance<IDbConnectionFactory>(
            new SqlConnectionFactory(
                ConfigurationManager.ConnectionStrings["Messaging"].ConnectionString,
                loggerFactory));            

        /************************************************************/
        // TODO: Not sure if need singletons or not - to test......
        /************************************************************/
        containerRegistry.RegisterSingleton<IMetroMessageDisplayService, MetroMessageDisplayService>();
        containerRegistry.RegisterSingleton<IUserService, UserService>();
        containerRegistry.RegisterSingleton<IUserStore, UserStore>();
        containerRegistry.RegisterSingleton<IMenuService, MenuService>();
        containerRegistry.Register<ICustomerService, CustomerService>();
        containerRegistry.Register<INotifyIconService, NotifyIconService>();
        containerRegistry.RegisterDialog<DefaultDialog, DefaultDialogViewModel>("Default");
        containerRegistry.RegisterDialog<HtmlDialog, HtmlDialogViewModel>("Html");

        // Get the current user's details - prevent a deadlock in the way we use Task.Run(...).Result
        // Then add to container as we will be injecting into VMs
        var clientUser = Task.Run(GetCurrentUserDetails).Result;

        containerRegistry.RegisterInstance(clientUser);
        containerRegistry.RegisterSingleton<IClientUser, ClientUser>();

        // Add the task bar icon
        _taskbarIcon = (TaskbarIcon)FindResource("NotifyIcon");
        containerRegistry.RegisterInstance(_taskbarIcon);

        // Create a logger instance
        var logger = loggerFactory.CreateLogger<App>();

        logger.LogDebug("Finished registering types in App.xaml.cs");
    }

    private async Task<ClientUser> GetCurrentUserDetails()
    {
        var userService = Container.Resolve<IUserService>();
        var data = await userService.GetClientUsersAsync(ConfigurationManager.AppSettings.Get("TempUserName")).ConfigureAwait(true);

        if (!data.Any())
        {
            // log unable to load user from database then return as no point in loading messages for a user that cannot be found!!
            return null;
        }

        return data.FirstOrDefault();
    }

    protected override IModuleCatalog CreateModuleCatalog()
    {
        // We are returning a type that reads the modules from the config file.
        return new ConfigurationModuleCatalog();
    }

    protected override void OnExit(ExitEventArgs e)
    {
        // The icon would clean up automatically, but this is cleaner
        _taskbarIcon.Dispose();

        base.OnExit(e);
    }

    private IUnityContainer ConfigureLogging(IContainerRegistry containerRegistry)
    {
        // Configure logging - Needed Unity.Microsoft.Logging package 
        // see https://github.com/unitycontainer/microsoft-logging and https://github.com/unitycontainer/examples/tree/master/src/Logging/Microsoft.Logging
        var serilogLogger = new LoggerConfiguration()
            .ReadFrom.AppSettings()
            .CreateLogger();

        this.loggerFactory = new LoggerFactory().AddSerilog(serilogLogger);

        var container = containerRegistry.GetContainer();
        container.AddExtension(new LoggingExtension(loggerFactory));

        return container;
    }
}

消息传递模块:

public class MessagingModule : IModule
{
    public void OnInitialized(IContainerProvider containerProvider)
    {
        // Register main view with region manager
        var regionManager = containerProvider.Resolve<IRegionManager>();
        regionManager.RegisterViewWithRegion("MainRegion", typeof(MessagingMainView));
    }

    public void RegisterTypes(IContainerRegistry containerRegistry)
    {
        containerRegistry.RegisterSingleton<IMessageStore, MessageStore>();
        containerRegistry.RegisterSingleton<IMessageService, MessageService>();
        containerRegistry.RegisterSingleton<IChatService, ChatService>();
        containerRegistry.RegisterSingleton<IFileDialogService, FileDialogService>();
        containerRegistry.RegisterDialog<InboxMessageView, InboxMessageViewModel>("HtmlMessage");

        // Here we are loading the config/settings from the app.config for this module so we
        // can register a ChatServiceConfiguration type as it is injected into ChatService
        var filename = Assembly.GetExecutingAssembly().Location;

        var configuration = ConfigurationManager.OpenExeConfiguration(filename);

        if (configuration != null)
        {
            var hubUrl = configuration.AppSettings.Settings["HubUrl"].Value;

            if (string.IsNullOrEmpty(hubUrl))
            {
                throw new ArgumentException("The HubUrl app setting cannot ne null or whitespace.");
            }

            containerRegistry.RegisterInstance(new ChatServiceConfiguration(hubUrl));
            containerRegistry.RegisterSingleton<IChatServiceConfiguration, ChatServiceConfiguration>();
        }
    }
}

MessagingMainView:

The MessagingMainView:

<UserControl x:Class="Ascensos.Wpf.Modules.Messaging.Views.MessagingMainView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
         xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
         xmlns:materialDesignConverters="clr-namespace:MaterialDesignThemes.Wpf.Converters;assembly=MaterialDesignThemes.Wpf"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:views="clr-namespace:Ascensos.Wpf.Modules.Messaging.Views"
         xmlns:helpers="clr-namespace:Ascensos.Wpf.Modules.Messaging.Helpers"
         xmlns:prism="http://prismlibrary.com/"
         prism:ViewModelLocator.AutoWireViewModel="True"
         d:DesignHeight="300"
         d:DesignWidth="400"
         mc:Ignorable="d">

</UserControl>

后面的代码:

public sealed partial class MessagingMainView : UserControl
{
    public MessagingMainView()
    {
        this.InitializeComponent();
    }
}

viewmodel(调用基本消息视图模型类) :

The viewmodel (calls a base message view model class):

public sealed class MessagingMainViewModel
{
    public MessagingMainViewModel()
    {            
    }
}

更新:我已经从应用中删除了代码,因此仅上面的代码在view和viewmodel中。在模块初始化期间,视图构造函数和viewmodel构造函数仍被击中两次。

UPDATE: I have removed code from my app, so that only the above code is within the view and viewmodel. The view constructor and viewmodel constructor is still being hit twice during the module initialization.

推荐答案

我发现了这个问题。这是App CreateShell()方法中的以下行:

I have found the issue. It is the following line in App CreateShell() method:

Container.GetContainer().RegisterInstance(typeof(Window), "MainWindow", Container.Resolve<MainWindow>(), new ContainerControlledLifetimeManager());

对此进行评论已解决了问题。再看一遍,我一直很傻,没发现这点,就像我是新手一样。添加了此代码,因此我可以在服务类中访问MainWindow / MetroWindow-但我将需要尝试另一种方法。感谢您的宝贵时间@Haukinger。

Commenting this out has sorted the problem. Looking at it again I have been rather silly to not spot this, like the novice I am. This code was added so I could get access to the MainWindow/MetroWindow in a service class - but I will need to try this another way. Thanks for your time @Haukinger.

这篇关于两次创建Prism / WPF视图/视图模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆