Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Should be able to execute a CommandAsyncChecked-generated command multiple times #17

Closed
WallaceKelly opened this issue May 2, 2016 · 2 comments

Comments

@WallaceKelly
Copy link

Description

When I create a WPF Command with Factory.CommandAsyncChecked(asyncWorkflow, canExecute), and bind that command to a button, and click the button, the button becomes disabled and does not become re-enabled until the asyncWorkflow completes. It does this even if canExecute always returns true.

Repro steps

  1. Create a new F# project
  2. Reference FSharp.ViewModule.Core, 0.9.9.2 and FsXaml.Wpf, 0.9.9.
  3. Use the code below.
  4. Run the program. Press the button.
  5. Observe that the button becomes disabled for three seconds and then becomes re-enabled.

MainWindow.xaml

<Window xmlns="http://schemas.microsoft.com/netfx/2009/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Button Width="200" Height="100"
            Command="{Binding DoSomethingAsync}"
            Content="DoSomethingAsync" />
</Window>

Program.cs

open System
open System.Windows
open FSharp.ViewModule

type MainWindow = FsXaml.XAML< "MainWindow.xaml", true >

type MainWindowViewModel() as this = 
    inherit ViewModelBase()
    let asyncWorkflow _ =
        async {
            do! Async.Sleep(3000)
            System.Diagnostics.Debug.WriteLine("asyncWorkflow completed.")
        }
    let canExecute _ = true
    member val DoSomethingAsync =
        this.Factory.CommandAsyncChecked(asyncWorkflow, canExecute)

[<STAThread; EntryPoint>]
let main argv = 
    if SynchronizationContext.Current = null then
        DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher)
        |> SynchronizationContext.SetSynchronizationContext
    let win = MainWindow()
    win.Root.DataContext <- MainWindowViewModel()
    let app = Application()
    app.Run(win.Root)

Expected behavior

I expected that the button would never become disabled.

Actual behavior

The button becomes disabled for three seconds.

Known workarounds

None.

Related information

  • Windows 10
  • Visual Studio 2015
  • .NET Framework 4.5.2, FSharp.Core 4.4.0.0
@ReedCopsey
Copy link
Member

Looking back at this, I believe this is by design.

If you want to avoid it, you can use a Sync command that just starts an async workflow. The entire reason behind the separate Async command support was to allow it to track execution and disable.

I believe you can "workaround" this behavior if you don't like the defaults by doing:

type MainWindowViewModel() as this = 
    inherit ViewModelBase()
    let asyncWorkflow _ =
        async {
            do! Async.Sleep(3000)
            System.Diagnostics.Debug.WriteLine("asyncWorkflow completed.")
        }
        |> Async.Start
    member val DoSomethingAsync = this.Factory.CommandSync(asyncWorkflow)

@ReedCopsey
Copy link
Member

The relevant code for this is here:

https://github.com/fsprojects/FSharp.ViewModule/blob/master/src/FSharp.ViewModule/MVVM.fs#L247
and:

https://github.com/fsprojects/FSharp.ViewModule/blob/master/src/FSharp.ViewModule/FunCommand.fs#L43

In hindsight, it'd probably be a good idea to automatically add OperationExecuting as a dependent property.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants