Is there any way to remove a view (by name) from a Prism region when the view was added using the RegionManager.RequestNavigate method?

Timothy Schoonover picture Timothy Schoonover · Aug 6, 2013 · Viewed 8.1k times · Source

I am using Prism for navigation in my WPF MVVM application. I register my view as follows.

// 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");

I display this view as follows.

_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.
);

Elsewhere in the application, I need to remove this view in an event handler; however, the following code returns an ArgumentNullException.

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

This indicates that the RequestNavigate method does not add MyView to MyRegion using the name "MyView". I know that if I were to use the _regionManager.Add(MyView, "MyView") method, the GetView method would not return null. Unfortunately, RequestNavigate does not seem to handle the view name in the same way. Is there any way to remove a view (by name) from a region when the view was added using the RequestNavigate method?

Answer

bland picture bland · Aug 7, 2013

It stems from how you add your view, not with your removal. Previously asked answered by adding the view fully, aka including the name.

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

So now you can do your retrieval and removal:

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

Without defining name during Regions.Add()

In your View, define a property that is accessible (public if multi-project, internal if all in one project). Use this property in everything, one example would be a public string ViewTitle { get { return "XYZ"; } }. Then retrieve from the Views the item that has the desired ViewTitle. The Views collection is the collection of views in that region, so you can use dynamic in .NET 4.0+ to ignore the type and get the property/function you specify, assuming it is there. Another option is to make your imported ViewModel in the View have a getter rather than just setting the DataContext, then you'd check the property "is" to the ViewModel you're looking for. Removes the string search but exposes the view's datacontext. So probably make an enum like I would do with the region.

I included everything in my View's .cs file so you can see how it works without complicating it or really breaking 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"; } }
}

Now in the ViewModel at some point:

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