General dialog window for WPF and Prism

3.9k views Asked by At

I've been through too much pain trying to switch between managing VM state for various views in the shell window, together with VM state for numerous edit dialogues, modal or non-modal, and state for the windows themselves.

Now I wish to redo with one shell window, and hoping for only one dialogue window, then my windows and Vms have far less coupling, and I can borrow the shell's VM management patterns into the dialogue's management.

I see quite a lot of guidance for directly managing popups and less generalised non-modals, but is there any established pattern or guidance for using only one dialogue to host various VMs? Even better, is there any such functionality available in Prism already?

I want to really to stick the DRY principle, and inheritance in both views and VMs is adding complexities I can't afford on a supposed to be quick first iteration prototype.

2

There are 2 answers

2
AudioBubble On BEST ANSWER

This is actually quite simple, but has some complexities. First off, you will want to create a custom dialog service. This service can be as simple or as complicated as you like. The main thing this service will do is show a dialog that is treated as it's own shell. Meaning that the dialog will be the same, but the contents within the dialog will be different based on the information you pass to it when you call it. SO this means a custom dialog with it's own regions. To handle using different views in the dialog shell, I would utilize the navigation service.

Your dialog might look something like this:

public interface IDialogService
{
    void ShowDialog(string uri);
}

public class DialogService : IDialogService
{
    private readonly IUnityContainer _container;
    private readonly IRegionManager _regionManager;

    public DialogService(IUnityContainer container, IRegionManager regionManager)
    {
        _container = container;
        _regionManager = regionManager;
    }

    public void ShowDialog(string uri)
    {
        var dialog = _container.Resolve<DialogShell>();
        //use a scoped region just in case you can have multiple instances
        var scopedRegion = _regionManager.CreateRegionManager();
        //set the region manager of the dialog to the scoped region
        RegionManager.SetRegionManager(dialog, scopedRegion);
        //navigate to show the desired view in the dialog
        scopedRegion.RequestNavigate(KnownRegionNames.ContentRegion, uri);
        //show the dialog
        dialog.Show();
    }
}

You can modify this approach to fit your needs exactly, but you get the idea.

EDIT: I also want to mention that you can even get crazy with this by allowing your dialog to have it's own separate navigation within it and unique to each instance that is shown. I have a Pluralsight course that shows how to do this if you are interested. https://www.pluralsight.com/courses/prism-showing-multiple-shells

4
DHN On

Well I don't know about any out of the box solution, but creating such a reusable dialog implementation is not that hard. Actually, I implemented something like this a few years ago. Well it was in another job, so I don't have access to the code anymore. Additionally, I cannot remember all the details, but I can try to give you the basic idea of such an implementation.

You can create a DialogVm, which is providing the generic dialog functionality.

First of all, what is needed for a dialog in presentation layer? Usually, three buttons, like...

  • Apply, Cancel and Close (Modification dialog)
  • Ok and Cancel or Yes and No (Question dialog)
  • Ok (in case of a message box)

So as you can see, you need three commands (ICommand doc). Actually, I've created a DelegateCommand implementation (based on this). ICommand.CanExecute determines, whether a bound button is disabled or enabled. If a command is null, the button should be hidden.

(If you're using the right layout control, the positions of the buttons are adjusted properly, if a button is not shown.)

To provide support for more than the four scenarios above, I added a CommandTitle property to DelegateCommand, so that the content of the button is coming from there.

Next thing you'll need a Title property for the title of the dialog. So add this to the DialogVm.

If you want to be able to close the dialog (only necessary, if it's a child of Window) by executing a command, you can follow this approach. Of course I've used the version, which I've described there. But the others are also looking promising.

The last open point is a property, which represents the different dialog contents. If I recall it correctly, I've used a small set of view models and corresponding DataTemplates (and of course a TemplateSelector, which is providing the right template based on the VM type). Of course you will also need a ContentPresenter control in your dialog, which is showing the DataTemplate provided by the TemplateSelector.

This is the only downsite, it's only a good approach, if you have only a few different dialog types (e.g. Question box, Message box...)

The usage is pretty easy. Simply, initialize a DialogVm instance with the desired ICommand logic, the DialogContentVm (however you want to call it), pass it to the DialogWindow (perhaps, you want to use sth. different e.g. a flyout) and present it to the user.

Well I hope it helps. If you need more info or any help, pls let me know.