9

我在我的 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方法没有添加MyViewMyRegion使用名称“MyView”。我知道如果我要使用该_regionManager.Add(MyView, "MyView")方法,该GetView方法将不会返回 null。不幸的是,RequestNavigate似乎没有以相同的方式处理视图名称。当使用该方法添加视图时,有什么方法可以从区域中删除视图(按名称)RequestNavigate

4

2 回答 2

4

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

_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);
于 2013-08-07T15:01:19.530 回答
3

我们最近发现自己遇到了同样的问题;感谢@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));
    }
于 2013-11-27T19:28:08.467 回答