【问题标题】:How can I cancel an Await in a WPF dialog when the user clicks the close button with MVVM?当用户单击带有 MVVM 的关闭按钮时,如何在 WPF 对话框中取消等待?
【发布时间】:2016-08-07 17:11:33
【问题描述】:

我有一个应用程序要求用户在对话框中输入 SQL 连接信息。当用户单击“连接”按钮时,我await 使用提供的凭据来测试SqlConnection.OpenAsync() 的结果,以便在关闭对话框之前测试它们是否有效。我还禁用了连接按钮以避免重复尝试。如果凭据正确,它几乎会立即返回,但如果不正确,则最多可能需要 30 秒才能返回。取消按钮可以使用CancellationToken 来取消请求并关闭对话框。

问题是用户仍然可以单击对话框上的窗口关闭按钮来关闭它。我的视图模型没有收到通知,但表单仍然关闭。 30 秒左右后,连接尝试返回错误信息,并显示一个消息框。

当表单以这种方式关闭时,是否有一种好的、MVVM 友好的方式来取消与我的CancelationToken 的连接尝试?我知道我可以在对话框的代码隐藏中使用一些东西,但我想避免从那里引用视图模型。

【问题讨论】:

  • "对 Close 的调用将尝试取消或关闭相应的 OpenAsync 调用。"
  • @Will 这是一个不错的发现,但我仍然缺少一种从 Dialog 的 Windows 提供的关闭 (X) 按钮以 MVVM 友好的方式调用它的方法。我正在考虑实施 Bas 的 EventToCommand 想法,但最终我可能会使用你的取消方法而不是我原来的 CancelationToken

标签: c# wpf mvvm async-await


【解决方案1】:

根据您用于 MVVM 基础架构的内容,您可以在 Window.Closing 上使用类似 MVVM Light 的 EventToCommand。您可以将此事件绑定到您已附加到按钮的取消命令。

请参阅MVVM Light: Adding EventToCommand in XAML without Blend, easier way or snippet? 了解执行此操作的方法。

【讨论】:

  • 我喜欢这种模式。不过,我目前正在使用我自己的 MVVM 支持库。我必须借用这个模式并在我自己的库中实现它。
  • 工作就像一个魅力!
【解决方案2】:

尝试等待SqlConnection.OpenAsync() 的结果并传入取消令牌。

【讨论】:

  • 我已经这样做了。当用户按下取消按钮时,取消异步操作不是问题。问题是当用户按下窗口的关闭 (X) 按钮时。我发现没有任何 MVVM 友好的方式可以将其连接到视图模型。
  • 好的,你提到你使用的是 SqlConnection.Open(),而不是 OpenAsync() 方法。
【解决方案3】:

您可以为此编写扩展方法。

所以你有一个定义了附加属性的类(我们称之为 WindowExtensions)。

internal class WindowExtensions
{
    public static readonly DependencyProperty WindowClosingCommandProperty = DependencyProperty.RegisterAttached(
        "WindowClosingCommand", typeof (ICommand), typeof (WindowExtensions), new PropertyMetadata(null, OnWindowClosingCommandChanged));

    private static void OnWindowClosingCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        Window window = d as Window;
        if (window == null)
            return;

        if (e.NewValue != null)
        {
            window.Closing += WindowOnClosing;
        }
    }

    private static void WindowOnClosing(object sender, CancelEventArgs e)
    {
        Window window = sender as Window;
        if (window == null)
            return;

        ICommand windowClosingCommand = GetWindowClosingCommand(window);
        windowClosingCommand.Execute(e);
    }

    public static void SetWindowClosingCommand(DependencyObject element, ICommand value)
    {
        element.SetValue(WindowClosingCommandProperty, value);
    }

    public static ICommand GetWindowClosingCommand(DependencyObject element)
    {
        return (ICommand) element.GetValue(WindowClosingCommandProperty);
    }
}

在 Window-Element 上的 XAML 中,您可以将附加属性映射到 ViewModel 中的 ICommand-Property,例如:

nameSpaceOfWindowExtensions:WindowExtensions.WindowClosingCommand="{Binding WindowClosingCommand}"

在您的 ViewModel 中,您有一个 ICommand-Property 可以处理它。 类似的东西:

private ICommand windowClosingCommand;
public ICommand WindowClosingCommand
{
    get { return windowClosingCommand ?? (windowClosingCommand = new RelayCommand(OnWindowClosing)); }
}

private void OnWindowClosing(object parameter)
{
    CancelEventArgs cancelEventArgs = parameter as CancelEventArgs;
    if (cancelEventArgs != null)
    {
        // If you want to cancel the closing of the window you can call the following:
        //cancelEventArgs.Cancel = true;
    }
}

如果您不需要 ViewModel 中的 CancelEventArgs,只需修改附加属性中的以下行:

windowClosingCommand.Execute(e);

windowClosingCommand.Execute(null);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-25
    • 1970-01-01
    • 2011-12-07
    • 2012-01-16
    • 1970-01-01
    相关资源
    最近更新 更多