Misho73bg
Misho73bg

Reputation: 58

Cancel opening dialog in dialog view model

I use Prism 7.2.0. I wonder if it is possible to cancel opening dialog in a view model, which implements IDialogAware in case of invalid parameters. I tried the following code but it does not work.

If parameters Is Nothing Then
    'No parameters, close
    Me._SysDlg.ShowMessageBox("Invalid parameters for Visit window (no prameters).", MessageBoxButton.OK, MessageBoxImage.Error)
    RaiseEvent RequestClose(New DialogResult(ButtonResult.Cancel))
Else
' ...

Upvotes: 0

Views: 645

Answers (2)

Milan Kovarik
Milan Kovarik

Reputation: 56

I don't have a sufficient reputation to add a comment. The solution from thatguy is good, but I've made some changes for memory optimization.

I've switched the ConfigureDialogWindowContent and ConfigureDialogWindowEvents methods, because I don't want to have the events registered.

The check itself I've moved to ConfigureDialogWindowContent method. If It not passed, I return ButtonResult.Abort.

My validation Interface is called IDialogAwareEx, but can be changed for IDialogParametersValidator from thatguy post

 void ShowDialogInternal(string name, IDialogParameters parameters, Action<IDialogResult> callback, bool isModal, string windowName = null)
    {
        IDialogWindow dialogWindow = CreateDialogWindow(windowName);
        if (!ConfigureDialogWindowContent(name, dialogWindow, parameters))
        {
            callback?.Invoke(new DialogResult(ButtonResult.Abort));
            return;
        }
        ConfigureDialogWindowEvents(dialogWindow, callback);

        if (isModal)
            dialogWindow.ShowDialog();
        else
            dialogWindow.Show();

    }

 protected virtual bool ConfigureDialogWindowContent(string dialogName, IDialogWindow window, IDialogParameters parameters)
    {
        var content = _containerExtension.Resolve<object>(dialogName);

        var dialogContent = content as FrameworkElement;
        if (dialogContent == null)
            throw new NullReferenceException("A dialog's content must be a FrameworkElement");

        if (dialogContent.DataContext is IDialogAwareEx dialogAwareEx
            && !dialogAwareEx.CanBeOpened(parameters))
        {
            // opening is not allowed
            return false;
        }

        var viewModel = dialogContent.DataContext as IDialogAware;
        /* other codes */

        return true;
    }

Upvotes: 0

thatguy
thatguy

Reputation: 22119

You cannot cancel opening the dialog in the dialog view model itself. That is because of the way that the DialogService is implemented. From the source you can see, that the OnDialogOpened(IDialogParameters parameters) method is called before the dialog itself is shown and there are no checks in between to prevent it. Nevertheless, there are options to achive the desired result.

  1. Check the validity of the dialog parameters before you call the dialog service to show the dialog
  2. Create your own implementation of the dialog service

I recommend to use the first approach, because it requires less effort and it is more reasonable to check the validity of your data before passing it. I do not think taht it makes sense to call up a dialog that validates its parameters and closes itself again.

Creating your own dialog service

I am not familiar with Visual Basic, so I can only provide an example using C#. However, the idea is the same.

  1. Create an interface for validating dialog parameters.
public interface IDialogParametersValidator
{
   bool ValidateDialogParameters(IDialogParameters parameters);
}
  1. Create an interface for your extended dialog service.
public interface IExtendedDialogService
{
   void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback, bool validateDialogParameters = false);
   void ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback, bool validateDialogParameters = false);
}
  1. Copy this dialog window extensions class that you need in your custom dialog service for getting the view model. It is internal in the Prism source, so you need to make a copy.
public static class IDialogWindowExtensions
{
   public static IDialogAware GetDialogViewModel(this IDialogWindow dialogWindow)
   {
      return (IDialogAware)dialogWindow.DataContext;
   }
}
  1. Create the extended dialog service, that implements IExtendedDialogService as well as IDialogService for compatibility. You can copy the DialogService from Prism and make small adjustments for the validation. First, you need to add a parameter validateDialogParameters to the ShowDialogInternal method. Then you add a check if the dialog parameters shall be validated and if the dialog parameters are valid if we have to do so. The method AreDialogParametersValid will look for view models that implement the IDialogParametersValidator interface and validates the dialog parameters with it. If the dialog parameters are invalid, ShowDialogInternal will just return without showing the dialog. Finally, you need to implement the Show and ShowDialog methods, which simply call the ShowDialogInternal method with appropriate parameters.
public class ExtendedDialogService : IDialogService, IExtendedDialogService
{
   public void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback)
   {
      ShowDialogInternal(name, parameters, callback, false);
   }

   public void ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback)
   {
      ShowDialogInternal(name, parameters, callback, false);
   }

   public void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback, bool validateDialogParameters = false)
   {
      ShowDialogInternal(name, parameters, callback, false, validateDialogParameters);
   }

   public void ShowDialog(string name, IDialogParameters parameters, Action<IDialogResult> callback, bool validateDialogParameters = false)
   {
      ShowDialogInternal(name, parameters, callback, true, validateDialogParameters);
   }

   void ShowDialogInternal(string name, IDialogParameters parameters, Action<IDialogResult> callback, bool isModal, bool validateDialogParameters = false)
   {
      IDialogWindow dialogWindow = CreateDialogWindow();
      ConfigureDialogWindowEvents(dialogWindow, callback);
      ConfigureDialogWindowContent(name, dialogWindow, parameters);

      // This is the only change to this method, validate and cancel if necessary
      if (validateDialogParameters && !AreDialogParametersValid(dialogWindow, parameters))
         return;

      if (isModal)
         dialogWindow.ShowDialog();
      else
         dialogWindow.Show();
   }

   private static bool AreDialogParametersValid(IDialogWindow dialogWindow, IDialogParameters parameters)
   {
      if (dialogWindow.GetDialogViewModel() is IDialogParametersValidator validator)
         return validator.ValidateDialogParameters(parameters);

      return true;
   }

   // ...copy all other code from the Prism implementation of dialog service.
}
  1. Register the extended dialog service and overwrite the default one.
containerRegistry.RegisterSingleton<IDialogService, ExtendedDialogService>();
containerRegistry.RegisterSingleton<IExtendedDialogService, ExtendedDialogService>();
  1. Implement the IDialogParametersValidator interface in your dialog view model and validate.
public class DialogViewModel : BindableBase, IDialogAware, IDialogParametersValidator
{
   // ...your dialog view model implementation.

   public bool ValidateDialogParameters(IDialogParameters parameters)
   {
      return /* ...your validation logic here. */;
   }
}
  1. Use the new dialog service. Voilà.
dialogService.ShowDialog(nameof(YourDialog), dialogParameters, resultDelegate, true);

Upvotes: 1

Related Questions