0

I have an application that requires a user to enter SQL connection information in a dialog. When the user clicks the "Connect" button, I await the result of SqlConnection.OpenAsync() with the supplied credentials in order to test that they are valid before closing the dialog box. I also disable the Connect button to avoid duplicate attempts. When the credentials are correct, it returns almost immediately, but when they are not, it can take up to 30 seconds to return. The cancel button is able to use a CancellationToken to cancel the request and close the dialog.

The problem is that the user is still able to click the window's close button on the dialog to dismiss it. My view model doesn't get notified, but the form is still closed. 30 seconds or so later the connection attempt returns with error information, and shows a message box.

Is there a good, MVVM friendly way, to cancel the connection attempt with my CancelationToken when the form is closed in this way? I know that I could rig up something using in the dialog's code-behind, but I'd like to avoid referencing the view model from there.

Bradley Uffner
  • 16,641
  • 3
  • 39
  • 76
  • 1
    "A call to Close will attempt to cancel or close the corresponding OpenAsync call." –  Apr 15 '16 at 16:20
  • @Will That is nice find, but I'm still missing a way to call that from the Dialog's Windows supplied Close (X) button in an MVVM friendly way. I'm looking in to implementing Bas' `EventToCommand` idea, though I may use your cancellation method over my original `CancelationToken` in the end. – Bradley Uffner Apr 15 '16 at 16:28

3 Answers3

2

Depending on what you are using for your MVVM infrastructure, you could use something like MVVM Light's EventToCommand on the Window.Closing. You may bind this event to the cancellation command that you have attached to the button.

See MVVM Light: Adding EventToCommand in XAML without Blend, easier way or snippet? for a way to do this.

Community
  • 1
  • 1
Bas
  • 26,772
  • 8
  • 53
  • 86
1

Try awaiting the result of SqlConnection.OpenAsync() and pass in a cancellation token.

sithius92
  • 136
  • 4
  • I'm already doing that. Canceling the async operating isn't a problem when the user presses the cancel button. The problem is when the user presses the Window's close (X) button. There is no MVVM friendly way I've found to connect this to the view model. – Bradley Uffner Apr 15 '16 at 16:22
  • 1
    Ok, you mentioned you were using SqlConnection.Open(), not the OpenAsync() method. – sithius92 Apr 15 '16 at 16:24
1

You can write an extension-method therefor.

So you have a class (let's call it WindowExtensions) where your attached property is definded.

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);
    }
}

In your XAML on the Window-Element you can map your attached-property to an ICommand-Property in your ViewModel like:

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

And in your ViewModel you have a ICommand-Property where you can handle it. Something like:

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;
    }
}

If you don't need the CancelEventArgs in your ViewModel, just modify the following line in the attached-property:

windowClosingCommand.Execute(e);

to

windowClosingCommand.Execute(null);
Tomtom
  • 9,087
  • 7
  • 52
  • 95