当使用RegionManager.RequestNavigate方法添加视图时,是否有任何方法可以从Prism区域中删除

本文关键字:方法 任何 Prism 删除 区域 RegionManager RequestNavigate 添加 视图 是否 | 更新日期: 2023-09-27 18:26:35

我在WPF MVVM应用程序中使用Prism进行导航。我的看法如下。

// MyView is the data type of the view I want to register and "MyView"
// is the name by which I want the data type to be identified within
// the IoC container.
_container.RegisterType<object, MyView>("MyView");

我将此视图显示如下。

_regionManager.RequestNavigate(
    "MyRegion", // This is the name of the Region where the view should be displayed.
    "MyView" // This is the registered name of the view in the IoC container.
);

在应用程序的其他地方,我需要在事件处理程序中删除此视图;但是,下面的代码返回一个ArgumentNullException

_regionManager.Regions["MyRegion"].Remove(
    _regionManager.Regions["MyRegion"].GetView("MyView")
);

这表示RequestNavigate方法不会使用名称"MyView"将MyView添加到MyRegion。我知道如果我使用_regionManager.Add(MyView, "MyView")方法,GetView方法不会返回null。不幸的是,RequestNavigate似乎没有以相同的方式处理视图名称。当使用RequestNavigate方法添加视图时,是否有任何方法可以从区域中删除视图(按名称)?

当使用RegionManager.RequestNavigate方法添加视图时,是否有任何方法可以从Prism区域中删除

它源于如何添加视图,而不是删除视图。之前的问题通过添加完整的视图来回答,也就是包括名称。

_regionManager.Regions["MyRegion"].Add(myView, "MyView");

所以现在你可以进行检索和删除:

var theView = _regionManager.Regions["MyRegion"].GetView("MyView");
_regionManager.Regions["MyRegion"].Remove(theView);

在Regions.Add()过程中未定义名称

在视图中,定义一个可访问的属性(如果是多个项目,则为公共属性;如果是一个项目中的所有属性,则为内部属性)。在所有内容中都使用此属性,例如公共字符串ViewTitle{get{return"XYZ";}}。然后从视图中检索具有所需ViewTitle的项目。Views集合是该区域中视图的集合,因此您可以在.NET 4.0+中使用dynamic来忽略类型并获取您指定的属性/函数(假设它在那里)。另一种选择是使视图中导入的ViewModel具有getter,而不仅仅是设置DataContext,然后将属性"is"检查为要查找的ViewModel。删除字符串搜索,但公开视图的数据上下文。所以可能会做一个枚举,就像我对这个区域所做的那样。

我在View的.cs文件中包含了所有内容,这样您就可以看到它是如何工作的,而不会使它复杂化或真正破坏MVVM。

[ViewSortHint("050")]
[ViewExport(RegionName = RegionNames.WorkspaceTabRegion)]
[PartCreationPolicy(CreationPolicy.Shared)]
public partial class AView : UserControl
{
    public AView()
    {
        InitializeComponent();
    }
    [Import]
    [SuppressMessage("Microsoft.Design", "CA1044:PropertiesShouldNotBeWriteOnly", Justification = "MEF requires property; never retrieved")]
    PrintingViewModel ViewModel { set { this.DataContext = value; } }
    public string ViewTitle { get { return "AView"; } }
}

现在在ViewModel中的某个时刻:

   var viewToRemove = RegionManager.Regions[RegionNames.WorkspaceTabRegion].Views.FirstOrDefault<dynamic>(v => v.ViewTitle == "AView");
   RegionManager.Regions[RegionNames.WorkspaceTabRegion].Remove(viewToRemove);

我们最近发现自己也遇到了同样的问题;感谢@odysseus.section9在您的评论中指出了它的根源,它真的很有帮助。

我们曾考虑让所有视图实现一个具有Name属性的接口,但感觉不太好。然后我们探索了@bland解决方案,但对使用dynamic感到不舒服,所以我们采用了非常类似的方法,使用反射

由于我们已经在使用ViewExportAttribute导出视图,并且它包含所需的ViewName属性,因此我们要做的是在一个区域中查询每个视图的属性,查找ViewExportAttribute并检查ViewName属性的值。尽管在我们的设计中,所有视图都用它进行了注释,但查询可以容忍没有注释的视图——它只是忽略了它们。

为了方便起见,我们为IRegion创建了一个扩展方法,用于搜索区域内具有所需名称的视图。此外,我们为应用程序中的两种常见场景向IRegionManager添加了两种扩展方法:重新使用现有视图或导航并删除所有现有视图(匹配名称)和导航。我认为后者只需取消对的呼叫即可解决您的需求

    public static IEnumerable<object> FindViews(this IRegion region, string viewName)
    {
        return from view in region.Views
               from attr in Attribute.GetCustomAttributes(view.GetType())
               where attr is ViewExportAttribute && ((ViewExportAttribute)attr).ViewName == viewName
               select view;
    }
    public static void ActivateOrRequestNavigate(this IRegionManager regionManager, string regionName, string viewName, UriQuery navigationParams)
    {
        IRegion region = regionManager.Regions[regionName];
        object view = region.FindViews(viewName).FirstOrDefault();
        if (view != null)
            region.Activate(view);
        else
            regionManager.RequestNavigate(regionName,
                new System.Uri(navigationParams != null ? viewName + navigationParams.ToString() : viewName, UriKind.Relative));
    }

    public static void RemoveAndRequestNavigate(this IRegionManager regionManager, string regionName, string viewName, UriQuery navigationParams)
    {
        IRegion region = regionManager.Regions[regionName];
        foreach (object view in region.FindViews(viewName))
            region.Remove(view);
        regionManager.RequestNavigate(regionName,
                new System.Uri(navigationParams != null ? viewName + navigationParams.ToString() : viewName, UriKind.Relative));
    }