StepUp
StepUp

Reputation: 38189

Creating add-ons for WPF applications

I have a simple database application where an user can add or delete persons. Moreover, the application has a button "Add new button to application". This application is built using Prism framework. There are two modules:

My requirement is to add new buttons at runtime.

Let's imagine a situation. I live in Washington and I am happy with these two buttons(Add Person and Delete Person). But my friend, Bob, who lives in New Jersey would like to add new button Edit Button without recompilation the whole application. That is, Bob writes dll where he can edit person and then clicks Add new button to application in RibbonControlModule. After that, EditPerson button is appeared in RibbonControl and, for example, in ContextMenu. Maybe EditPerson dll would be another Prism module, I do not know.

That is, my requirements are:

Is it possible using WPF, MVVM and Prism? I really like Prism and do not want to deny Prism, but if "the end justifies the means", I would like to use other technologies.

If it is possible, then how can I do it?

Upvotes: 5

Views: 1688

Answers (2)

Brian Noyes
Brian Noyes

Reputation: 2070

You can do what you are describing using Regions in Prism. You can add a named region to your Ribbon that allows Prism modules to plug in new buttons into that region when the module first loads or later when a user clicks a button in some UI of your module as you describe.

To do that, add an ItemsControl into some pane within your Ribbon where you want the plugged in controls to show up. Add the prism namespace as a XAML namespace like so:

xmlns:prism="http://prismlibrary/"

Then add the following attached property to your ItemsControl:

prism:RegionManager.RegionName="CustomModuleCommandRegion"

Then in your module, inject IRegionManager either in the Module class itself if the commands should be added as soon as the module is loaded, or elsewhere in a ViewModel if it won't happen until a particular view is loaded or some user interaction like you described:

public ConstructorForModuleOrViewModel(IRegionManager regionManager)
{
    _regionManager = regionManager;
}

private SomeCommandHandler()
{
    var commandButton = // create button and wire up command here)
    _regionManager.AddViewToRegion(commandButton, "CustomModuleCommandRegion");
}

You also have the option of using the RegisterViewWithRegion method of the region manager to set up a factory method or just specify the type of the view (i.e. button) that you want to inject. But for a button you will need to wire up a command handler before (or after) placing it into the region, so the AddViewToRegion is probably more appropriate. If it is something that is context sensitive - i.e. you only want the button to show up in the ribbon perhaps when a selection is made in a view - then you can get the region from the region manager first, and then use the Add and Remove methods on IRegion to add and remove your view (button) dynamically like so:

IRegion region = _regionManager.Regions["CustomModuleCommandRegion"];
region.Add(myCommandView);
...
region.Remove(myCommandView);

Using the combination of Prism Modules and Regions you can achieve runtime extensibility of your app - i.e. this new functionality could be "dropped in" without needing to recompile the main app or other modules in the app. To do that you need to use either configuration to specify your modules so that can be edited in the deployed environment to add a module, or you can use the DirectoryModuleCatalog to scan a directory for modules on start up. Its even possible to use a FileSystemWatcher to watch a directory for modules that are dropped in while the app is running and have them light up immediately when placed in the watched directory.

Upvotes: 4

Mark Feldman
Mark Feldman

Reputation: 16138

This is what the MEF plugin architecture was designed for.

In short, you create an SDK containing an interface for your plugins and provide that to your clients as a standalone library. Your client's plugins then implement this interface and export them with the MEF Export attribute, which your main application then imports.

Where it gets a little tricky is with data templating, which is often a key component of MVVM. To cut a long story short your plugins need to put their data templates in a resource dictionary, give that dictionary it's own partial class file and export it with MEF's [Export] attribute. Your main app then needs to import these and add them to the global ResourceDictionary's 'MergedDictionaries' array. This is generally done separate to all your view model classes which are imported in a separate pass. The net effect is that your plugins can provides both views and view models at runtime, plus the data templates that bind the two together, and it will all work as though they'd been statically compiled into your original application. It also means you can create a plugin API for your customers without exposing the internals of your main application.

This is a very involved topic, and given how general this question is I'll be surprised if this question isn't flagged. If you'd like more details then let me know and we can move it to a discussion page.

Upvotes: 4

Related Questions