使用嵌套视图时,Prism 7引发异常

如何解决使用嵌套视图时,Prism 7引发异常

几个月前,我Working with nested views using Prism with IsNavigationTarget which can return false发表了类似的问题,我仍然不确定什么是正确的方法。

假设您有一个视图A,在该视图A中您声明了一个区域A,然后将视图B注入了该区域A。类似地,在视图B中您注册了区域B,然后又注入了视图C。进入下图所示的区域B。

enter image description here

在ViewA的ViewModelA中,我有一个方法SetUpSubViews()可以在其中调用:

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

用于View B的ViewModelB实现INavigationAware。因此,在OnNavigatedTo()方法中,我调用:

_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}" />

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

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

我需要的是每次需要时都有一种从头开始重新创建view viewmodel对的方法。似乎在放置视图时,棱镜不会删除在删除的视图中声明的区域。向社区和棱镜开发者提出疑问,如何以正确的方式做到这一点?

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

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

通知棱镜不创建新视图,这也可能意味着如果在视图中发现任何区域,也不要在区域管理器中注册该区域。

第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",NavigationCallback);}

类似地,我必须从ViewB的区域regionB中删除ViewC。 (这是region.RemoveAll()是关键行。)

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

可以,但是看起来不正确。

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

谢谢。

解决方法

这是一个相当麻烦的问题。我推荐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);
            }
        }
    }
}

创建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,StringComparison.Ordinal));
    }
}

在您的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并将其作为get属性

public bool CreateRegionManagerScope => true;

现在应该可以了。

再次,我真的推荐Brian在Prism上在Pluralsight上播放的视频。当您开始使用Prism时,他有一些视频对您有很大帮助。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res