取消在对话框视图模型中打开对话框

Cancel opening dialog in dialog view model

我用Prism 7.2.0。我想知道是否可以在视图模型中取消打开对话框,在参数无效的情况下实现 IDialogAware。我尝试了以下代码,但它不起作用。

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
' ...

您无法取消在对话框视图模型本身中打开对话框。这是因为 DialogService 的实现方式。从 source 您可以看到,OnDialogOpened(IDialogParameters parameters) 方法在 之前被调用 对话框本身被显示并且中间没有检查来阻止它。尽管如此,还是有一些选项可以达到预期的结果。

  1. 在调用对话服务显示对话之前检查对话参数的有效性
  2. 创建您自己的对话服务实现

我建议使用第一种方法,因为它需要更少的工作量,并且在传递数据之前检查数据的有效性更合理。我认为调用验证其参数并再次自行关闭的对话框没有意义。

创建您自己的对话服务

我不熟悉Visual Basic,所以我只能提供一个使用C#的例子。不过思路是一样的。

  1. 创建用于验证对话框参数的界面。
public interface IDialogParametersValidator
{
   bool ValidateDialogParameters(IDialogParameters parameters);
}
  1. 为您的扩展对话服务创建一个界面。
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. 复制此对话框 window 扩展 class,您需要在自定义对话框服务中获取视图模型。 Prism源码中internal,需要复制一份
public static class IDialogWindowExtensions
{
   public static IDialogAware GetDialogViewModel(this IDialogWindow dialogWindow)
   {
      return (IDialogAware)dialogWindow.DataContext;
   }
}
  1. 创建扩展对话服务,实现 IExtendedDialogService 以及 IDialogService 以实现兼容性。您可以从 Prism 复制 DialogService 并对验证进行小幅调整。首先,您需要在 ShowDialogInternal 方法中添加一个参数 validateDialogParameters。然后添加检查对话框参数是否应被验证以及对话框参数是否有效(如果我们必须这样做)。方法 AreDialogParametersValid 将查找实现 IDialogParametersValidator 接口的视图模型并使用它验证对话框参数。如果对话框参数无效,ShowDialogInternal 将只是 return 而不显示对话框。最后,您需要实现 ShowShowDialog 方法,它们只需使用适当的参数调用 ShowDialogInternal 方法。
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. 注册扩展对话服务并覆盖默认服务。
containerRegistry.RegisterSingleton<IDialogService, ExtendedDialogService>();
containerRegistry.RegisterSingleton<IExtendedDialogService, ExtendedDialogService>();
  1. 在对话视图模型中实现 IDialogParametersValidator 接口并验证。
public class DialogViewModel : BindableBase, IDialogAware, IDialogParametersValidator
{
   // ...your dialog view model implementation.

   public bool ValidateDialogParameters(IDialogParameters parameters)
   {
      return /* ...your validation logic here. */;
   }
}
  1. 使用新的对话服务。瞧。
dialogService.ShowDialog(nameof(YourDialog), dialogParameters, resultDelegate, true);

我没有足够的声誉来添加评论。 Thatguy 的解决方案很好,但我对内存优化做了一些更改。

我已经切换了 ConfigureDialogWindowContentConfigureDialogWindowEvents 方法,因为我不想注册事件。

我已将检查本身移至 ConfigureDialogWindowContent 方法。如果没通过,我returnButtonResult.Abort.

我的验证接口称为 IDialogAwareEx,但可以从那个家伙 post

更改 IDialogParametersValidator
 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;
    }