Cancel opening dialog in dialog view model

前端 未结 2 830
小蘑菇
小蘑菇 2021-01-17 03:58

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

相关标签:
2条回答
  • 2021-01-17 04:24

    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);
    
    0 讨论(0)
  • 2021-01-17 04:27

    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;
        }
    
    0 讨论(0)
提交回复
热议问题