取消在对话框视图模型中打开对话框
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)
方法在 之前被调用 对话框本身被显示并且中间没有检查来阻止它。尽管如此,还是有一些选项可以达到预期的结果。
- 在调用对话服务显示对话之前检查对话参数的有效性
- 创建您自己的对话服务实现
我建议使用第一种方法,因为它需要更少的工作量,并且在传递数据之前检查数据的有效性更合理。我认为调用验证其参数并再次自行关闭的对话框没有意义。
创建您自己的对话服务
我不熟悉Visual Basic,所以我只能提供一个使用C#的例子。不过思路是一样的。
- 创建用于验证对话框参数的界面。
public interface IDialogParametersValidator
{
bool ValidateDialogParameters(IDialogParameters parameters);
}
- 为您的扩展对话服务创建一个界面。
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);
}
- 复制此对话框 window 扩展 class,您需要在自定义对话框服务中获取视图模型。 Prism源码中
internal
,需要复制一份
public static class IDialogWindowExtensions
{
public static IDialogAware GetDialogViewModel(this IDialogWindow dialogWindow)
{
return (IDialogAware)dialogWindow.DataContext;
}
}
- 创建扩展对话服务,实现
IExtendedDialogService
以及 IDialogService
以实现兼容性。您可以从 Prism 复制 DialogService
并对验证进行小幅调整。首先,您需要在 ShowDialogInternal
方法中添加一个参数 validateDialogParameters
。然后添加检查对话框参数是否应被验证以及对话框参数是否有效(如果我们必须这样做)。方法 AreDialogParametersValid
将查找实现 IDialogParametersValidator
接口的视图模型并使用它验证对话框参数。如果对话框参数无效,ShowDialogInternal
将只是 return 而不显示对话框。最后,您需要实现 Show
和 ShowDialog
方法,它们只需使用适当的参数调用 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.
}
- 注册扩展对话服务并覆盖默认服务。
containerRegistry.RegisterSingleton<IDialogService, ExtendedDialogService>();
containerRegistry.RegisterSingleton<IExtendedDialogService, ExtendedDialogService>();
- 在对话视图模型中实现
IDialogParametersValidator
接口并验证。
public class DialogViewModel : BindableBase, IDialogAware, IDialogParametersValidator
{
// ...your dialog view model implementation.
public bool ValidateDialogParameters(IDialogParameters parameters)
{
return /* ...your validation logic here. */;
}
}
- 使用新的对话服务。瞧。
dialogService.ShowDialog(nameof(YourDialog), dialogParameters, resultDelegate, true);
我没有足够的声誉来添加评论。
Thatguy 的解决方案很好,但我对内存优化做了一些更改。
我已经切换了 ConfigureDialogWindowContent
和 ConfigureDialogWindowEvents
方法,因为我不想注册事件。
我已将检查本身移至 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;
}
我用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)
方法在 之前被调用 对话框本身被显示并且中间没有检查来阻止它。尽管如此,还是有一些选项可以达到预期的结果。
- 在调用对话服务显示对话之前检查对话参数的有效性
- 创建您自己的对话服务实现
我建议使用第一种方法,因为它需要更少的工作量,并且在传递数据之前检查数据的有效性更合理。我认为调用验证其参数并再次自行关闭的对话框没有意义。
创建您自己的对话服务
我不熟悉Visual Basic,所以我只能提供一个使用C#的例子。不过思路是一样的。
- 创建用于验证对话框参数的界面。
public interface IDialogParametersValidator
{
bool ValidateDialogParameters(IDialogParameters parameters);
}
- 为您的扩展对话服务创建一个界面。
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);
}
- 复制此对话框 window 扩展 class,您需要在自定义对话框服务中获取视图模型。 Prism源码中
internal
,需要复制一份
public static class IDialogWindowExtensions
{
public static IDialogAware GetDialogViewModel(this IDialogWindow dialogWindow)
{
return (IDialogAware)dialogWindow.DataContext;
}
}
- 创建扩展对话服务,实现
IExtendedDialogService
以及IDialogService
以实现兼容性。您可以从 Prism 复制DialogService
并对验证进行小幅调整。首先,您需要在ShowDialogInternal
方法中添加一个参数validateDialogParameters
。然后添加检查对话框参数是否应被验证以及对话框参数是否有效(如果我们必须这样做)。方法AreDialogParametersValid
将查找实现IDialogParametersValidator
接口的视图模型并使用它验证对话框参数。如果对话框参数无效,ShowDialogInternal
将只是 return 而不显示对话框。最后,您需要实现Show
和ShowDialog
方法,它们只需使用适当的参数调用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.
}
- 注册扩展对话服务并覆盖默认服务。
containerRegistry.RegisterSingleton<IDialogService, ExtendedDialogService>();
containerRegistry.RegisterSingleton<IExtendedDialogService, ExtendedDialogService>();
- 在对话视图模型中实现
IDialogParametersValidator
接口并验证。
public class DialogViewModel : BindableBase, IDialogAware, IDialogParametersValidator
{
// ...your dialog view model implementation.
public bool ValidateDialogParameters(IDialogParameters parameters)
{
return /* ...your validation logic here. */;
}
}
- 使用新的对话服务。瞧。
dialogService.ShowDialog(nameof(YourDialog), dialogParameters, resultDelegate, true);
我没有足够的声誉来添加评论。 Thatguy 的解决方案很好,但我对内存优化做了一些更改。
我已经切换了 ConfigureDialogWindowContent
和 ConfigureDialogWindowEvents
方法,因为我不想注册事件。
我已将检查本身移至 ConfigureDialogWindowContent 方法。如果没通过,我returnButtonResult.Abort.
我的验证接口称为 IDialogAwareEx
,但可以从那个家伙 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;
}