使用嵌套视图时 Prism 7 抛出异常

2023-12-14

几个月前我发布了类似的问题使用 Prism 和 IsNavigationTarget 处理嵌套视图,可能返回 false,我仍然不确定正确的方法是什么。

假设你有一个视图A,在这个视图A中你声明了一个区域A,然后你在这个区域A中注入了一个视图B。同样,在视图B中你注册了区域B,然后你在这个区域中注入了一个视图C B、如下图所示:

enter image description here

在 ViewS 的 ViewModel 中,我有一个方法 SetUp SubViews() ,我在其中调用:

_regionManager.RequestNavigate("regionA", "ViewB", NavigationCallback);

视图 B 的 ViewModelB 实现 INavigationAware。因此,在 OnNavigateTo() 方法中我调用:

_regionManager.RequestNavigate("regionB", "ViewC", NavigationCallback);

View C 的 ViewModelC 也实现了 INavigationAware。

现在,我在 IsNavigationTarget() 方法中的 ViewModelB 和 ViewModelC 中都有:

public bool IsNavigationTarget(NavigationContext navigationContext)
    {            
        return false;
    }

这意味着我想在每次导航该视图时创建新视图。

ViewB和ViewC都实现了IRegionMemberLifetime接口,我在其中设置:

#region IRegionMemberLifetime

public bool KeepAlive => false;

#endregion

这意味着我不想重用视图并且希望将其丢弃。

视图中的区域声明如下:

<ContentControl prism:RegionManager.RegionName="{x:Static localRegions:LocalRegions.RegionB}" />

现在,当我第一次在 ViewModel 上调用 SetUp SubViews() 方法时,一切都很好。当我第二次调用它时,我看到了异常:

具有给定名称的区域已注册 ...

我需要的是有一种方法可以在每次需要时从头开始重新创建视图视图模型对。似乎当视图被处置时,棱镜不会删除在已删除视图中声明的区域。请问社区和棱镜开发者,如何正确地做到这一点?

当前的解决方案并不令人满意,这就是我所做的: 步骤 1 - 我在 INavigationAware 部分中设置 ViewModelB 和 ViewModelC

    public bool IsNavigationTarget(NavigationContext navigationContext)
    {            
        return true;
    }

这向 prism 发出信号,要求不要创建新视图,并且可能还意味着,如果在视图中找到任何区域,则不要将其注册到区域管理器中。

第 2 步 - 当我需要将视图注入到区域时,我手动删除旧视图并创建新视图。所以我的 SetUpSubViews() 方法如下所示:

protected void SetUpSubViews(){
            //get region by name
            var region = _regionManager.Regions["regionA"];
            // push to remove all views from the region
            region.RemoveAll();
            // navigate to view
           _regionManager.RequestNavigate("regionA", "ViewB", NavigationCallback);}

同样,我必须从 View 上的区域区域中删除 ViewS 有 Region.RemoveAll() 是关键行。)

Step3 - 我没有在 viewB 和 viewC 上实现 IRegionMemberLifetime 接口。

它有效,但看起来不正确。

附:我也尝试了作用域管理器,但我不知道如何将新创建的作用域管理器传播到视图模型,因为它们是自动创建的,如果我通过构造函数解决它,我会得到主全局管理器而不是作用域。

Thanks.


这是一个相当麻烦的问题。我推荐 Brian Lagunas 本人的视频,他在其中提供了解决方案和解释。比如这个。https://app.pluralsight.com/library/courses/prism-problems-solutions/table-of-contents

如果你能看的话。如果没有,我会尽力解释。

我相信的问题是IRegionManager从容器中是一个单例,无论何时使用它,它都是同一个实例,因此当您尝试在已注入的区域中注入一个区域时,它将不起作用,您需要有一个单独的实例RegionManager对于嵌套视图。

这应该可以解决它。 创建两个接口

public interface ICreateRegionManagerScope
{
    bool CreateRegionManagerScope { get; }
}
public interface IRegionManagerAware
{
    IRegionManager RegionManager { get; set; }
}

创建一个RegionManagerAwareBehaviour

public class RegionManagerAwareBehaviour : RegionBehavior
{
    public const string BehaviorKey = "RegionManagerAwareBehavior";

    protected override void OnAttach()
    {
        Region.Views.CollectionChanged += Views_CollectionChanged;
    }

    void Views_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            foreach (var item in e.NewItems)
            {
                IRegionManager regionManager = Region.RegionManager;

                // If the view was created with a scoped region manager, the behavior uses that region manager instead.
                if (item is FrameworkElement element)
                {
                    if (element.GetValue(RegionManager.RegionManagerProperty) is IRegionManager scopedRegionManager)
                    {
                        regionManager = scopedRegionManager;
                    }
                }

                InvokeOnRegionManagerAwareElement(item, x => x.RegionManager = regionManager);
            }
        }
        else if (e.Action == NotifyCollectionChangedAction.Remove)
        {
            foreach (var item in e.OldItems)
            {
                InvokeOnRegionManagerAwareElement(item, x => x.RegionManager = null);
            }
        }
    }

    private static void InvokeOnRegionManagerAwareElement(object item, Action<IRegionManagerAware> invocation)
    {
        if (item is IRegionManagerAware regionManagerAwareItem)
        {
            invocation(regionManagerAwareItem);
        }

        if (item is FrameworkElement frameworkElement)
        {
            if (frameworkElement.DataContext is IRegionManagerAware regionManagerAwareDataContext)
            {
                // If a view doesn't have a data context (view model) it will inherit the data context from the parent view.
                // The following check is done to avoid setting the RegionManager property in the view model of the parent view by mistake.
                if (frameworkElement.Parent is FrameworkElement frameworkElementParent)
                {
                    if (frameworkElementParent.DataContext is IRegionManagerAware regionManagerAwareDataContextParent)
                    {
                        if (regionManagerAwareDataContext == regionManagerAwareDataContextParent)
                        {
                            // If all of the previous conditions are true, it means that this view doesn't have a view model
                            // and is using the view model of its visual parent.
                            return;
                        }
                    }
                }

                invocation(regionManagerAwareDataContext);
            }
        }
    }
}

Create ScopedRegionNavigationContentLoader

public class ScopedRegionNavigationContentLoader : IRegionNavigationContentLoader
{
    private readonly IServiceLocator serviceLocator;

    /// <summary>
    /// Initializes a new instance of the <see cref="RegionNavigationContentLoader"/> class with a service locator.
    /// </summary>
    /// <param name="serviceLocator">The service locator.</param>
    public ScopedRegionNavigationContentLoader(IServiceLocator serviceLocator)
    {
        this.serviceLocator = serviceLocator;
    }

    /// <summary>
    /// Gets the view to which the navigation request represented by <paramref name="navigationContext"/> applies.
    /// </summary>
    /// <param name="region">The region.</param>
    /// <param name="navigationContext">The context representing the navigation request.</param>
    /// <returns>
    /// The view to be the target of the navigation request.
    /// </returns>
    /// <remarks>
    /// If none of the views in the region can be the target of the navigation request, a new view
    /// is created and added to the region.
    /// </remarks>
    /// <exception cref="ArgumentException">when a new view cannot be created for the navigation request.</exception>
    public object LoadContent(IRegion region, NavigationContext navigationContext)
    {
        if (region == null) throw new ArgumentNullException("region");
        if (navigationContext == null) throw new ArgumentNullException("navigationContext");

        string candidateTargetContract = this.GetContractFromNavigationContext(navigationContext);

        var candidates = this.GetCandidatesFromRegion(region, candidateTargetContract);

        var acceptingCandidates =
            candidates.Where(
                v =>
                {
                    var navigationAware = v as INavigationAware;
                    if (navigationAware != null && !navigationAware.IsNavigationTarget(navigationContext))
                    {
                        return false;
                    }

                    var frameworkElement = v as FrameworkElement;
                    if (frameworkElement == null)
                    {
                        return true;
                    }

                    navigationAware = frameworkElement.DataContext as INavigationAware;
                    return navigationAware == null || navigationAware.IsNavigationTarget(navigationContext);
                });


        var view = acceptingCandidates.FirstOrDefault();

        if (view != null)
        {
            return view;
        }

        view = this.CreateNewRegionItem(candidateTargetContract);

        region.Add(view, null, CreateRegionManagerScope(view));

        return view;
    }

    private bool CreateRegionManagerScope(object view)
    {
        bool createRegionManagerScope = false;

        if (view is ICreateRegionManagerScope viewHasScopedRegions)
            createRegionManagerScope = viewHasScopedRegions.CreateRegionManagerScope;

        return createRegionManagerScope;
    }

    /// <summary>
    /// Provides a new item for the region based on the supplied candidate target contract name.
    /// </summary>
    /// <param name="candidateTargetContract">The target contract to build.</param>
    /// <returns>An instance of an item to put into the <see cref="IRegion"/>.</returns>
    protected virtual object CreateNewRegionItem(string candidateTargetContract)
    {
        object newRegionItem;
        try
        {
            newRegionItem = this.serviceLocator.GetInstance<object>(candidateTargetContract);
        }
        catch (ActivationException e)
        {
            throw new InvalidOperationException(
                string.Format(CultureInfo.CurrentCulture, "Cannot create navigation target", candidateTargetContract),
                e);
        }
        return newRegionItem;
    }

    /// <summary>
    /// Returns the candidate TargetContract based on the <see cref="NavigationContext"/>.
    /// </summary>
    /// <param name="navigationContext">The navigation contract.</param>
    /// <returns>The candidate contract to seek within the <see cref="IRegion"/> and to use, if not found, when resolving from the container.</returns>
    protected virtual string GetContractFromNavigationContext(NavigationContext navigationContext)
    {
        if (navigationContext == null) throw new ArgumentNullException(nameof(navigationContext));

        var candidateTargetContract = UriParsingHelper.GetAbsolutePath(navigationContext.Uri);
        candidateTargetContract = candidateTargetContract.TrimStart('/');
        return candidateTargetContract;
    }

    /// <summary>
    /// Returns the set of candidates that may satisfiy this navigation request.
    /// </summary>
    /// <param name="region">The region containing items that may satisfy the navigation request.</param>
    /// <param name="candidateNavigationContract">The candidate navigation target as determined by <see cref="GetContractFromNavigationContext"/></param>
    /// <returns>An enumerable of candidate objects from the <see cref="IRegion"/></returns>
    protected virtual IEnumerable<object> GetCandidatesFromRegion(IRegion region, string candidateNavigationContract)
    {
        if (region == null) throw new ArgumentNullException(nameof(region));
        return region.Views.Where(v =>
            string.Equals(v.GetType().Name, candidateNavigationContract, StringComparison.Ordinal) ||
            string.Equals(v.GetType().FullName, candidateNavigationContract, StringComparison.Ordinal));
    }
}

In your App.xaml

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
 containerRegistry.RegisterSingleton<IRegionNavigationContentLoader,ScopedRegionNavigationContentLoader>();
    
}

protected override void ConfigureDefaultRegionBehaviors(IRegionBehaviorFactory regionBehaviors)
    {
       base.ConfigureDefaultRegionBehaviors(regionBehaviors);

       regionBehaviors.AddIfMissing(RegionManagerAwareBehaviour.BehaviorKey, typeof(RegionManagerAwareBehaviour));
    }

即将到达终点。 现在在你的ViewModelB实施IRegionManagerAware并将其作为正常财产

public IRegionManager RegionManager { get; set; }

然后在你的ViewB实施ICreateRegionManagerScope并将其作为获取属性

public bool CreateRegionManagerScope => true;

现在应该可以了。

我再次真心推荐 Brian 在 Prism 上发布的 Pluralsight 视频。他有几个视频对您开始使用 Prism 时有很大帮助。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用嵌套视图时 Prism 7 抛出异常 的相关文章

随机推荐

  • 如何设置int值为null? Java Android [重复]

    这个问题在这里已经有答案了 这是设置已定义的最佳方式int to null private int xy int x 5 x null this is ERROR return x 所以我选择这个 private int xy Intege
  • 在 JOptionPane 中将文本右对齐

    是否可以在 JOptionPane 中将文本向右对齐 我不想使用JDialog 因为我想用阿拉伯语写一些句子 创建一个 JPanel 在 JPanel 中对齐文本 然后将 JPanel 添加为 JOptionPane 的 Object 参数
  • 如何在 Swift 中以编程方式旋转一组按钮?

    我正在尝试以编程方式将编号按钮排列在一个圆圈中 NSLayoutConstraint 锚定视图 而 UIView 的子类创建按钮圈 框架绕中心旋转 但按钮旋转不一致 除一次旋转外 所有旋转中的文本方向均相同 for example 我用于排
  • jquery ajax进度与自定义计算

    在执行处理图片的长 PHP 脚本时 我需要有关 ajax 进度条的建议 我知道 stackoverflow 上已经有很多问题了 比如 显示长时间运行的 PHP 脚本的进度 or 通过 xhr 实现 JQuery ajax 进度 or 使用a
  • Java读写本地数据库?

    我有一个程序不断从网站获取信息并不断更新 截至目前 我将所有这些信息存储在arraylist然后当我完成后 我将其写入文本文件 我需要操纵这些信息 但是 它创建了一个巨大的文本文件 我无法不断地读取信息并将信息写入文本文件 因为它需要很长时
  • 如何为 Azure AD B2C 配置 SSO?

    As per Azure AD B2C 文档我们可以为 Azure AD B2C 应用程序配置 SSO 我们希望用户自行注册应用程序 从而选择 AD B2C 但是 在创建 B2C 租户帐户并配置所有策略之后 我无法找到配置 SSO 的选项
  • 这个构造是什么意思“__builtin_expect(!!(x), 1)”

    具体来说 我问的是双 在 built in 的参数中 按照 C 语言 它是双重否定吗 The 简直就是两个 操作员彼此相邻 这是将任何非零值转换为的简单方法1 并离开0按原样 又名 布尔化 值 看 c 运算符 是两个 NOT 吗 对于一般逻
  • 获取视图或控制器中的当前区域名称

    如何获取视图或控制器中的当前区域名称 有没有类似的东西ViewContext RouteData Values controller 对于地区 从MVC2开始你可以使用ViewContext RouteData DataTokens are
  • Rails 3.1,由于缺少局部部分变量而导致内存泄漏(Ruby 1.9.2-p290)

    如果我像这样渲染部分 渲染 部分 gt event news item 对象 gt 事件 变量 gt true 然后参考variable在部分中触发了内存泄漏 Rails 似乎进入了递归 然后 我必须在内存使用量失控之前快速重新启动服务器
  • 如何获取亚马逊S3上文件的md5sum

    如果我在 Amazon S3 上有现有文件 那么无需下载文件即可获取其 md5sum 的最简单方法是什么 AWS 的文档ETag 截至 2023 年 11 月 17 日 说 实体标签 ETag 代表对象的特定版本 ETag 仅反映对象内容的
  • 使用 MinGW/MSYS 编译 freetype2 时出现问题

    用MinGW和MSYS编译freetype2好像有问题 我对 freetype 源代码的干净副本以及纯粹的 MinGW 和 MSYS 的尝试给出了 Andrew MCARDLE PC documents code cd freetype 2
  • 创建单独变量字典的更简单方法?

    我希望能够以字符串形式获取变量的名称 但我不知道 Python 是否具有那么多的自省功能 就像是 gt gt gt print my var name my var 我想这样做 因为我有一堆变量 我想把它们变成字典 例如 bar True
  • 无法调用分页dojo增强网格上的函数

    我尝试了所有可能的方法 但它不起作用 当我移动到 dojo dataGrid 中的另一个页面 分页期间 时 我想调用一个函数 我尝试了下面的代码 但它不起作用 方法一 nextPage function src alert going gr
  • 在loopback.io中进行连接查询

    我正在尝试使用 Loopback io 构建一个简单的应用程序作为我的学习过程 我已经设置了项目 创建的模型和 API 工作正常 现在我正在尝试创建一个自定义 api 它可以通过进行联接查询从两个不同的模型获取数据 所以我有两个模型 sto
  • axios post 请求成功后刷新表

    这是我来自 axios 的示例请求响应 var data id 1 name john username john doe birthdate 1999 05 21 age 20 email email protected id 2 nam
  • 将多个 hdf5 文件合并到一个 pytable 中

    我有一些hdf5文件 每个文件都具有相同的结构 我想创建一个pytable通过某种方式合并它们hdf5 files 我的意思是 如果 file1 中的数组的大小为 x 而 file2 中的数组的大小为 y 则结果数组pytable大小为 x
  • 我可以将 CSS 应用于 swing 文本组件吗?

    许多 swing 文本组件将解释 HTML 如果可能的话 我想使用 CSS 来设置 HTML 的样式 有人知道怎么做吗 The Jaxx框架允许您执行此操作
  • 从音频输入捕获原始音频以在 Mac 上进行实时处理的最简单方法

    从内置音频输入捕获音频并能够在请求时实时读取原始采样值 如 wav 中 的最简单方法是什么 就像从套接字读取一样 希望代码使用 Apple 的框架之一 音频队列 文档不是很清楚 我需要的是非常基础的 为此尝试使用 AudioQueue 框架
  • Google API 上未捕获异常“InvalidArgumentException”,带有消息“

    我已复制此网站上他们的文档中的代码 here 我已经更改了下面这些的 JSON 我已经在 Composer 上的 API 上生成了新文件 但仍然遇到一些问题 我不确定我做错了什么 这是否意味着他们的 API 漏洞太多 这是演示链接 demo
  • 使用嵌套视图时 Prism 7 抛出异常

    几个月前我发布了类似的问题使用 Prism 和 IsNavigationTarget 处理嵌套视图 可能返回 false 我仍然不确定正确的方法是什么 假设你有一个视图A 在这个视图A中你声明了一个区域A 然后你在这个区域A中注入了一个视图