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

时间:2013-08-06 17:00:50

标签: c# wpf mvvm navigation prism

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

2 个答案:

答案 0 :(得分:3)

它源于您添加视图的方式,而不是您的删除。 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,然后检查属性“is”到您正在寻找的ViewModel。删除字符串搜索但公开视图的datacontext。所以可能会像我对该地区那样做一个枚举。

我在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);

答案 1 :(得分:3)

我们最近发现自己遇到了同样的问题;感谢@ odysseus.section9将其根源指向您的评论,这确实有所帮助。

我们考虑making all views implement an interface having a Name property但感觉不太对劲。然后我们探索了@bland解决方案,但对使用动态感到不舒服,所以我们采用了非常类似的方法,使用反射

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