より良いエンジニアを目指して

1日1つ。良くなる!上手くなる!

WPFのボタン処理であるICommand.Executeで非同期処理を使えるようにする。

RelayCommandのExecuteメソッドって非同期処理使えないの?と。

HttpClientを使うことになれば自然とExecuteメソッドをasync修飾子を指定したくなります。

Task.Runで包むという手はあるのですが、それも微妙だなあと思っていると以下の記事が見つかりました。

blogs.msdn.microsoft.com

自分でRelayCommandAsyncというクラスを定義して使うことになります。

以下のような形になります。

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

namespace WpfApp1
{
    public class MainWindowViewModel
    {
        public ICommand ClickCommand { get; set; }


        public MainWindowViewModel()
        {
            ClickCommand = new RelayCommandAsync<object>(async o =>
            {
                HttpClient httpClient = new HttpClient();
                HttpResponseMessage response = await httpClient.GetAsync("http://www.contoso.com/");
                response.EnsureSuccessStatusCode();
                string responseBody = await response.Content.ReadAsStringAsync();
                MessageBox.Show(responseBody.Substring(0, 100));
            });
        }
    }
    public class RelayCommandAsync<T> : ICommand
    {
        private readonly Func<T, Task> _executedMethod;
        private readonly Func<T, bool> _canExecuteMethod;
 
        public event EventHandler CanExecuteChanged;
        public RelayCommandAsync(Func<T, Task> execute) : this(execute, null) { }
 
        public RelayCommandAsync(Func<T, Task> execute, Func<T, bool> canExecute)
        {
            _executedMethod = execute ?? throw new ArgumentNullException("execute");
            _canExecuteMethod = canExecute;
        }
 
        public bool CanExecute(object parameter) => _canExecuteMethod == null || _canExecuteMethod((T)parameter);
        public async void Execute(object parameter) => await _executedMethod((T)parameter);
        public void OnCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

参考記事

docs.microsoft.com