简体   繁体   中英

Navigate to View by injecting specific ViewModel in WPF Prism

I have a design related question specific to WPF and Prism. I am working on a rule application where i need to display different types of rules (Models) in the hierarchical way in TreeView control. I have wrapped each instance of Model inside their respective ViewModel and bind it to TreeView. Below i have tried to depict the hierarchy just to emphasize the point that there can be multiple ViewModels of same type and based on their selection i need to display their respective View accordingly.

Rules

--- Group (GroupViewModel instance 1)

------ X (XViewModel instance 1)

--------- Z (ZViewModel instance 1)

------ X (XViewModel instance 2)

--- Group (GroupViewModel instance 2)

------ Y (YViewModel instance 1)

------ Z (ZViewModel instance 2)

--------- X (XViewModel instance 3)

Problem:

I need a mechanism to first get the selected item from TreeView and then navigate to ViewModel's View using Uri (to have loose coupling) based on Tree item selection and to feed/inject the selected ViewModel in the View. Need code example for getting selected ViewModel, navigate to View along with injection of ViewModel?

Goal:

I am looking for some best practice to handle this situation in WPF with Prism to maintain the loose coupling.

Important Note: TreeView is showing in one region and Content is part of another region and i am using MEF DI container.

1) Assuming "Execute" is some kind of business logic then it seems perfectly valid to have this in the model class. Ditto with Validation. In fact this is probably a given as you're looking to have all your business logic and validation in one place. That model can then (potentially) be re-used in the future, say if you decide to develop a web-based system on top of your existing models. In fact I once asked a similar question to yours some time ago.

Personally I think it's fine to have a "rich" model class, including properties that implement INotifyPropertyChanged , making it "binding friendly". This is perfectly acceptable as this interface doesn't live in a UI-related framework namespace (System.ComponentModel). I even go so far as to include ICommand properties in my models for convenience, although it depends how much of a "purist" you are, blurring the lines between the definition of a model and view model. This leads on to your second question - by having such a model, it can be bound directly to the view by exposing the model via a VM property.

A lot of WPF developers seem to write VM classes that are basically mirror images of the model classes (with all the to/from mapping code that goes with it), but including things like INPC properties and ICommands. It's a lot of overhead that I've (personally) never seen the point of. Again, there seems to be two "camps" regarding how you use models and VMs, perhaps to some extent depending on how "tiered" your application needs to be. A lot of it comes down to personal preference too, and like many dev challenges I don't think there's a right or wrong approach.

Regarding 3), do you want to click an item in a TreeView, resulting in a view being displayed elsewhere in the application? I'm assuming you are using a dependency injection framework? AFAIK the Prism region manager requires this and I'm not aware of a way to use the region manager without a DI framework.

You'll first need to define a region in which to display that view, eg:-

<ContentControl Regions:RegionManager.RegionName="MyRegionName" />

To display a view in a region you use the Prism region manager:-

_regionManager.RequestNavigate("MyViewName", "MyRegionName");

The view name is assigned when you register your view with the DI framework, eg this is how it's done in Castle Windsor:-

Container.Register(Component.For<MyView>().ImplementedBy<MyView>().LifeStyle.Transient.Named("MyViewName"));

Hope this sets you on your way. There's a lot to Prism navigation, especially getting your head around view lifecycles. Plus it's often useful to have the VM of the view being navigated to implement INavigationAware so that it can react to events, eg being navigated to/from.

Edit (based on OP edit):

You haven't said whether the region is in the same view as the TreeView. If it is, then you can probably achieve this without Prism navigation. The "main" VM (relating to the TreeView's view) could expose the currently selected rule VM (as an INotifyPropertyChanged property of course!), which you could bind to a ContentControl:

<ContentControl Content="{Binding SelectedRule}" />

Then it's just a case of setting up DataTemplates for the rule VMs:-

<DataTemplate DateType="{x:Type XRuleViewModel}">
    ... XAML ...
</DataTemplate>

As the TreeView selection changes, the ContentControl will bind to the appropriate rule VM, and the corresponding DataTemplate will render the required content.

If the region is in a separate view then you'll need to use Prism navigation. The problem here is that you need the name of the view to navigate to, but your "main" (TreeView) VM has no knowledge of views, only the rule VMs. A simple option may be to have a property on your rule VMs that exposes the relevant view name. If you are more of a purist, and want to keep this knowledge out of the VMs, then you could implement a mechanism where you register all your rule VMs with their corresponding views' names (eg during application startup). This could be as simple as a Dictionary . When a rule VM is selected in the TreeView, you just need to look up the corresponding view name to use in the RequestNavigate() call.

@Andrew Is on the right track. For one, no need to wrap your models in a ViewModel unless your architecture absolutely requires it. Which, I can guess it doesn't. The easiest way to accomplish this task it to use the InvokeCommandAction in response to the selected item changed event. Then execute a command that will navigate to your desired view. Use the selected tem as a parameter to determine what view you are looking for. When you navigate, you can also pass the selected item as a parameter using the NavigationParameters, and use the INavigationAware on the target in order to get that parameter in the OnNavigatedTo method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM