【问题标题】:ViewModel instance will always be recreated when switching between pages在页面之间切换时总是会重新创建 ViewModel 实例
【发布时间】:2016-02-04 07:43:28
【问题描述】:

我正在使用 Prism 6,UWP 和 Unity。

ViewModel 将自动注入到页面的数据上下文中。但是,当我在页面之间导航时,总是会重新创建视图模型。 Prism 和 Unity 是否需要这种行为?

想象以下场景,用户将一些数据输入到页面中,因此将设置视图模型的正确属性。当用户切换回另一个页面并重新访问该页面时,所有输入的数据都会丢失,因为创建了一个新的视图模型实例。

目前我的解决方法是重写 OnNavigatedTo 和 OnNavigatingFrom 以使用 SessionStateService 手册保存视图模型的所有属性。我不确定这是否是正确的方法?

您可以使用以下示例重现此行为: https://github.com/PrismLibrary/Prism-Samples-Windows/tree/master/SplitViewSample/SplitViewSample

【问题讨论】:

  • 我不熟悉 Prism 6,所以我不知道它是如何插入的,但我知道 Unity 有办法控制实例的生命周期。使用它将您的视图模型设置为单例,因此不会每次都重新创建它
  • 请展示一些代码,您如何使用统一注册您的视图以及您的视图的构造函数
  • 我在官方 prism 示例中添加了一个 url,它也得到了与我描述的相同的行为。

标签: unity-container prism uwp


【解决方案1】:

我没有使用 Prism,我使用的是模板 10 的修改版本。 我只是快速浏览了 Prism 源代码。看起来模板 10 借鉴了 Prism 的很多想法。

我会尝试从两个角度回答你的问题:

1) AFAIK,在 Prism 中有一个静态类,您可以使用该类设置在自动查找相应视图时如何创建/解析视图模型。类是ViewModelLocationProvider,在文件ViewModelLocationProvider.cs中,您可以使用以下方法设置“视图模型工厂”

    /// <summary>
    /// Sets the default view model factory.
    /// </summary>
    /// <param name="viewModelFactory">The view model factory which provides the ViewModel type as a parameter.</param>
    public static void SetDefaultViewModelFactory(Func<Type, object> viewModelFactory)
    {
        _defaultViewModelFactory = viewModelFactory;
    }

    /// <summary>
    /// Sets the default view model factory.
    /// </summary>
    /// <param name="viewModelFactory">The view model factory that provides the View instance and ViewModel type as parameters.</param>
    public static void SetDefaultViewModelFactory(Func<object, Type, object> viewModelFactory)
    {
        _defaultViewModelFactoryWithViewParameter = viewModelFactory;
    }

    /// <summary>
    /// Registers the view model factory for the specified view type name.
    /// </summary>
    /// <param name="viewTypeName">The name of the view type.</param>
    /// <param name="factory">The viewmodel factory.</param>
    public static void Register(string viewTypeName, Func<object> factory)
    {
        _factories[viewTypeName] = factory;
    }

那么获取视图模型实例的所有逻辑都在下面,注意这里的注释摘要,它描述了逻辑/策略

    /// <summary>
    /// Automatically looks up the viewmodel that corresponds to the current view, using two strategies:
    /// It first looks to see if there is a mapping registered for that view, if not it will fallback to the convention based approach.
    /// </summary>
    /// <param name="view">The dependency object, typically a view.</param>
    /// <param name="setDataContextCallback">The call back to use to create the binding between the View and ViewModel</param>
    public static void AutoWireViewModelChanged(object view, Action<object, object> setDataContextCallback)
    {
        // Try mappings first
        object viewModel = GetViewModelForView(view);

        // Fallback to convention based
        if (viewModel == null)
        {
            var viewModelType = _defaultViewTypeToViewModelTypeResolver(view.GetType());
            if (viewModelType == null) 
                return;

            viewModel = _defaultViewModelFactoryWithViewParameter != null ? _defaultViewModelFactoryWithViewParameter(view, viewModelType) : _defaultViewModelFactory(viewModelType);
        }

        setDataContextCallback(view, viewModel);
    }

在第 87 行和第 96 行,您可以获得相应视图的视图模型实例。

这意味着,如果您不调用任何这些方法来设置工厂,它将回退到默认工厂,即

    /// <summary>
    /// The default view model factory whic provides the ViewModel type as a parameter.
    /// </summary>
    static Func<Type, object> _defaultViewModelFactory = type => Activator.CreateInstance(type);

很明显,您总会得到一个新实例。

关于Unity,我没看到什么特别的,唯一的线索是在PrismApplication.csPrismApplication类中,它的工厂设置如下:

    /// <summary>
    /// Configures the <see cref="ViewModelLocator"/> used by Prism.
    /// </summary>
    protected virtual void ConfigureViewModelLocator()
    {
        ViewModelLocationProvider.SetDefaultViewModelFactory((type) => Resolve(type));
    }

这意味着工厂现在正在使用

    /// <summary>
    /// Resolves the specified type.
    /// </summary>
    /// <param name="type">The type.</param>
    /// <returns>A concrete instance of the specified type.</returns>
    protected virtual object Resolve(Type type)
    {
        return Activator.CreateInstance(type);
    }

你可以用你自己的实现覆盖它。

PrismUnityApplicationPrismUnityApplication.cs 中,它提供了一个默认实现来使用Unity 解析实例

    /// <summary>
    /// Implements the Resolves method to be handled by the Unity Container.
    /// Use the container to resolve types (e.g. ViewModels and Flyouts)
    /// so their dependencies get injected
    /// </summary>
    /// <param name="type">The type.</param>
    /// <returns>A concrete instance of the specified type.</returns>
    protected override object Resolve(Type type)
    {
        return Container.Resolve(type);
    }

是的,就像其他人提到的那样,您可以通过 Unity 自己控制视图模型的生命周期。

2) 对不起,答案很长, 但我觉得最好向您展示一些可以使事情变得清晰的代码。 我会保持第二个简短。

在我看来,当您的视图消失时,您不需要视图模型。 我不确定框架堆栈是如何在 UWP 中实现的,以及它们如何管理视图/页面实例。我假设一旦您导航到不同的页面,之前的视图/页面应该被释放或可以在 GC 上释放,并且您具有能够导航回的参数和页面类型,但这将是一个新实例并且您通过恢复视图模型来恢复视图状态。

真的,我认为你在正确的轨道上。并且您应该尽可能保存/保留您的用户数据,并且您的解决方案在应用暂停然后恢复时有效,您仍然可以恢复视图的状态。

感谢阅读。

【讨论】:

    【解决方案2】:

    当您在 UnityContainer 中将您的 ViewModel 注册为单例 (ContainerControlledLifetimeManager) 时,应该可以解决此问题。最好的地方是方法OnInitializeAsync中的App.xaml.cs

    protected override Task OnInitializeAsync(IActivatedEventArgs args)
    {
        Container.RegisterType<MyViewModel>(new ContainerControlledLifetimeManager());
    
        // rest of the method
    }
    

    【讨论】:

    • 您的解决方案有效。我尝试通过以下方式采用它: ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver((viewType) => { [...] if (Container.Registrations.Count(a => a.Name == type.Name) == 0) { Container .RegisterInstance(type.Name, new ContainerControlledLifetimeManager()); } 返回类型;});尽管视图模型已在 UnityContainer 中注册,但视图模型不会被重用。有什么想法吗?
    • @Briefkasten Container.RegisterInstance(type.Name, new ContainerControlledLifetimeManager()) 这是错误的。这将注册一个具有给定名称的 ContainerControlledLifetimeManager。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 1970-01-01
    • 2019-03-17
    • 2016-04-04
    • 2023-01-24
    • 1970-01-01
    相关资源
    最近更新 更多