在页面之间切换时将始终重新创建 ViewModel 实例
ViewModel instance will always be recreated when switching between pages
我正在使用 Prism 6、UWP 和 Unity。
ViewModel 将自动注入到页面的数据上下文中。但是,当我在页面之间导航时,将始终重新创建视图模型。 Prism 和 Unity 需要这种行为吗?
想象一下以下场景,用户在页面中输入一些数据,因此将设置视图模型的适当属性。当用户切换回另一个页面并重新访问该页面时,所有输入的数据都将丢失,因为创建了一个新的视图模型实例。
目前我的解决方法是覆盖 OnNavigatedTo 和 OnNavigatingFrom 以使用 SessionStateService 手册保存视图模型的所有属性。我不确定这是不是正确的方法?
您可以使用以下示例重现此行为:
https://github.com/PrismLibrary/Prism-Samples-Windows/tree/master/SplitViewSample/SplitViewSample
当您在 UnityContainer 中将 ViewModel 注册为单例 (ContainerControlledLifetimeManager
) 时应该可以解决。最好的地方是方法 OnInitializeAsync
中的 App.xaml.cs
protected override Task OnInitializeAsync(IActivatedEventArgs args)
{
Container.RegisterType<MyViewModel>(new ContainerControlledLifetimeManager());
// rest of the method
}
我没有使用 Prism,我使用的是 Template 10 的修改版本。
我只是快速浏览了 Prism 源代码。看起来Template 10借鉴了Prism的很多想法。
我会尝试从两个角度来回答你的问题:
1) AFAIK,在 Prism 中有一个静态 class,您可以使用它设置如何 create/resolve 在自动查找相应视图时查看模型。 class是ViewModelLocationProvider
,在文件ViewModelLocationProvider.cs中你可以使用下面的方法设置'view model factories'
/// <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;
}
那么获取view model实例的所有逻辑在下面,注意这里的评论总结,它描述了logic/strategy
/// <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.cs 中的 PrismApplication
class 中,它设置工厂如下:
/// <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);
}
您可以用自己的实现覆盖它。
在PrismUnityApplication
class、PrismUnityApplication.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 中是如何实现的,以及它们如何管理 view/page 实例。我会假设一旦你导航到不同的页面,之前的 view/page 应该被释放或者可以在 GC 上释放,并且你有参数和页面类型可以导航回来,但它将是一个新的实例然后通过恢复视图模型来恢复视图状态。
真的,我认为你走在正确的轨道上。并且您应该 save/persist 您的用户数据,并且您的解决方案在应用程序暂停然后恢复时有效,您仍然可以恢复视图的状态。
感谢阅读。
我正在使用 Prism 6、UWP 和 Unity。
ViewModel 将自动注入到页面的数据上下文中。但是,当我在页面之间导航时,将始终重新创建视图模型。 Prism 和 Unity 需要这种行为吗?
想象一下以下场景,用户在页面中输入一些数据,因此将设置视图模型的适当属性。当用户切换回另一个页面并重新访问该页面时,所有输入的数据都将丢失,因为创建了一个新的视图模型实例。
目前我的解决方法是覆盖 OnNavigatedTo 和 OnNavigatingFrom 以使用 SessionStateService 手册保存视图模型的所有属性。我不确定这是不是正确的方法?
您可以使用以下示例重现此行为: https://github.com/PrismLibrary/Prism-Samples-Windows/tree/master/SplitViewSample/SplitViewSample
当您在 UnityContainer 中将 ViewModel 注册为单例 (ContainerControlledLifetimeManager
) 时应该可以解决。最好的地方是方法 OnInitializeAsync
App.xaml.cs
protected override Task OnInitializeAsync(IActivatedEventArgs args)
{
Container.RegisterType<MyViewModel>(new ContainerControlledLifetimeManager());
// rest of the method
}
我没有使用 Prism,我使用的是 Template 10 的修改版本。 我只是快速浏览了 Prism 源代码。看起来Template 10借鉴了Prism的很多想法。
我会尝试从两个角度来回答你的问题:
1) AFAIK,在 Prism 中有一个静态 class,您可以使用它设置如何 create/resolve 在自动查找相应视图时查看模型。 class是ViewModelLocationProvider
,在文件ViewModelLocationProvider.cs中你可以使用下面的方法设置'view model factories'
/// <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;
}
那么获取view model实例的所有逻辑在下面,注意这里的评论总结,它描述了logic/strategy
/// <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.cs 中的 PrismApplication
class 中,它设置工厂如下:
/// <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);
}
您可以用自己的实现覆盖它。
在PrismUnityApplication
class、PrismUnityApplication.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 中是如何实现的,以及它们如何管理 view/page 实例。我会假设一旦你导航到不同的页面,之前的 view/page 应该被释放或者可以在 GC 上释放,并且你有参数和页面类型可以导航回来,但它将是一个新的实例然后通过恢复视图模型来恢复视图状态。
真的,我认为你走在正确的轨道上。并且您应该 save/persist 您的用户数据,并且您的解决方案在应用程序暂停然后恢复时有效,您仍然可以恢复视图的状态。
感谢阅读。