Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running an async method inside a relaycommand using MVVM

I'm developing an asynchronous application using WPF and MVVM, but I can't seem to get an async method to run inside my relaycommand.

I have a button on my WPF view hooked up to a relaycommand in my viewmodel, which trys to call an async method in my model to return a list of results:

/// <summary>
/// Search results 
/// </summary>
private ObservableCollection<string> _searchResults = new ObservableCollection<string>(); 
public IList<string> SearchResults
{
    get { return _searchResults; }
}

/// <summary>
/// Search button command
/// </summary>
private ICommand _searchCommand;
public ICommand SearchCommand
{
    get
    {
        _searchCommand = new RelayCommand(
            async() =>
            {
                SearchResults.Clear();
                var results = await DockFindModel.SearchAsync(_selectedSearchableLayer, _searchString);
                foreach (var item in results)
                {
                    SearchResults.Add(item);
                }                    
                //notify results have changed
                NotifyPropertyChanged(() => SearchResults);
            },
            () => bAppRunning); //command will only execute if app is running
        return _searchCommand;
    }
}

However I get the following exception when the relaycommand tries to execute:

An unhandled exception of type 'System.AggregateException' occurred in mscorlib.dll

Additional information: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread.

I've tried a number of things in this thread to try and resolve the issue with no luck. Does anyone know how to resolve this?

like image 579
pvdev Avatar asked Aug 24 '26 14:08

pvdev


1 Answers

Not sure where your RelayCommand is coming from (MVVM framework or custom implementation) but consider using an async version.

public class AsyncRelayCommand : ICommand
{
    private readonly Func<object, Task> execute;
    private readonly Func<object, bool> canExecute;

    private long isExecuting;

    public AsyncRelayCommand(Func<object, Task> execute, Func<object, bool> canExecute = null)
    {
        this.execute = execute;
        this.canExecute = canExecute ?? (o => true);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void RaiseCanExecuteChanged()
    {
        CommandManager.InvalidateRequerySuggested();
    }

    public bool CanExecute(object parameter)
    {
        if (Interlocked.Read(ref isExecuting) != 0)
            return false;

        return canExecute(parameter);
    }

    public async void Execute(object parameter)
    {
        Interlocked.Exchange(ref isExecuting, 1);
        RaiseCanExecuteChanged();

        try
        {
            await execute(parameter);
        }
        finally
        {
            Interlocked.Exchange(ref isExecuting, 0);
            RaiseCanExecuteChanged();
        }
    }
}
like image 177
Cameron MacFarland Avatar answered Aug 26 '26 03:08

Cameron MacFarland



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!