8

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.

ProfK
  • 49,207
  • 121
  • 399
  • 775

2 Answers2

5

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.

Community
  • 1
  • 1
DHN
  • 4,807
  • 3
  • 31
  • 45
  • Thanks. What you suggest is quite close to what I had in mind, except for `DataTemplate` use. Web dev was my mainline many years before the recent 2 to 3 years on WPF, so I just use a simple `UserControl` as the view, which is bound to a `ContentControl` in the window. Then instead of selecting templates, I aim to use Prism's vm auto-wire, so my `ShowView` command just uses Untiy to resolve a view instance, and the vm is already bound. – ProfK Jan 04 '17 at 15:04
  • @ProfK: Ah, that seems to be the better option than the `DataTemplate` approach. Thanks for the hint. :) – DHN Jan 04 '17 at 15:11
  • Please don't be offended by the bounty. I have no doubt your advice is credible, but it is pure WPF and not Prism based, and I did want a few more answers from "authorities" on that framework. Unfortunate for the SO wording of that option button. – ProfK Jan 10 '17 at 10:29
  • @ProfK: Don't worry. I am not offended. It's up to you whether my answer is sufficient for you. ;o) Since I didn't know about the Prism VM auto-wire...it's perfectly fine and understandable. Although I believe, that you only have to do some small modifications to my approach. ;o) So relax, I hope sb will provide you the info you need. – DHN Jan 11 '17 at 13:30
3

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

  • This looks usable, with some adaptation. I'm not using navigation, just plain menu and button commands, but that's just small talk in this scenario. More later. Thank you. – ProfK Jan 12 '17 at 03:02
  • Since this is a workable solution to your problem, how about marking it as the answer? –  Jan 17 '17 at 16:09