当使用 RegionManager.RequestNavigate 方法添加视图时,有啥方法可以从 Prism 区域中删除视图(按名称)?

Posted

技术标签:

【中文标题】当使用 RegionManager.RequestNavigate 方法添加视图时,有啥方法可以从 Prism 区域中删除视图(按名称)?【英文标题】:Is there any way to remove a view (by name) from a Prism region when the view was added using the RegionManager.RequestNavigate method?当使用 RegionManager.RequestNavigate 方法添加视图时,有什么方法可以从 Prism 区域中删除视图(按名称)? 【发布时间】:2013-08-07 19:33:22 【问题描述】:

我在我的 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 方法添加视图时,有什么方法可以从区域中删除视图(按名称)?

【问题讨论】:

在深入了解 Prism 源代码后,RequestNavigate 似乎最终调用了region.Add(view);(在RegionNavigationContentLoader.LoadContent 中下降)。所以就目前而言,我认为我想做的事情是不可能的。我认为可以通过使用用于指定视图名称的附加参数重载各种RequestNavigate 方法和扩展来实现此功能。这将允许通过名称访问已使用 RequestNavigate 添加到区域的视图。 我已将您的场景明确添加到我的答案中。 【参考方案1】:

它源于您添加视图的方式,而不是您的删除。 Previously asked 通过完整添加视图来回答,也就是包括名称。

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

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

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

Regions.Add() 期间没有定义名称

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

我将所有内容都包含在我的 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);

【讨论】:

是的,但我的问题是关于RequestNavigate 方法。如果您阅读我的评论,您会明白为什么这是不可能的。 @odysseus.section9 已编辑以包含此部分。 第二个建议实际上适用于RequestNavigate @odysseus.section9 只需确保每个视图都包含您在 lambda 表达式中定义的任何内容,无论是属性、函数还是字段。否则,您将抛出异常并退出 LINQ 搜索而没有结果。至少,适用于您要搜索的区域中的任何视图。【参考方案2】:

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

我们考虑过making all views implement an interface having a Name property,但感觉不太对劲。然后我们探索了@bland 解决方案,但对使用动态感到不舒服,因此我们采用了一种非常相似的方法,使用 reflection

由于我们也已经在使用 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));
    

【讨论】:

以上是关于当使用 RegionManager.RequestNavigate 方法添加视图时,有啥方法可以从 Prism 区域中删除视图(按名称)?的主要内容,如果未能解决你的问题,请参考以下文章

当我们使用 JavaScript 隐藏/显示时清除下拉列表和/或文本框内容当它们被交替选择时

当 Model 超出使用范围时,实际会发生啥?

RHandsontable 不正确的输入转换,当使用格式时

当我们使用它时,线程本地是啥? [复制]

当我们有 OkHttp 时为啥要使用 Retrofit

当使用SQLserver时,当连接参考已建立的表格,却显示引用的表无效,是啥原因