Nikita B Nikita B - 1 year ago 36
C# Question

How to plug in an arbitrary view dynamically into xaml in MVVM application?

There are multiple places in my application whehe I have

placed in xaml and I do not know beforehand what its
is going to be. What is the best practice to implement this scenario?

Right now I am considering two approaches:

  1. Bind
    to view model and use a dictionary of
    s to find an appropriate view. My issue with this approach is that even if I were to list all the possible combinations in a dicitonary, in some cases I simply do not know an exact type of view (or viewmodel if there is any) at compilation time. I think, I am also going to have troubles using this approach for hosting non-WPF content.

  2. Create some sort of an interface:

    interface IContentPlugin : IDisposable
    object View { get; }

    and bind
    directly. I could then have multiple implementations of this interface and swap them when I need to. But this solution does not strike me as something that goes well with MVVM application, as it forces me to have references to
    s in my view models.

What do you think is the best option and why? Perhaps there is a better approach?

Answer Source

Eventually, I went with second approach. I was able to solve my main problem, which was:

But this solution does not strike me as something that goes well with MVVM application, as it forces me to have references to IContentPlugins in my view models.

Passing those "plugins" into viewmodels was a mistake, you should not do it. What you can and should do is find a way to partition your view into smaller independent segments, and set their content in non-MVVM way. So basically I ended up with a view, which acted as container and looked like this:

<UserControl x:Name=this>
        <ContentControl Grid.Row="0" Content="{Binding PluginA.View, ElementName=this}"/>
        <ContentControl Grid.Row="1" Content="{Binding PluginB.View, ElementName=this}"/>
        <ContentControl Grid.Row="2" Content="{Binding PluginC.View, ElementName=this}"/>

where PluginA, PluginB and PluginC are dependency properties in code-behind, that are set by DI container using property injection. I am happy with the end-result, and it gives me the flexibility I need.

You can also use PRISM, which roughly speaking does the same thing, but in more general and flexible manner. It was somewhat too complex for my application though, so I decided to keep it simple. But you should give it a try, if you are trying to solve similar issue.