takataka430’s blog

.NET系を中心に勉強したことのまとめを書きます

【Xamarin.Forms】ページ遷移と同時に実行する非同期メソッドについて考えた話

Xamarin.Formsで開発する時に非同期処理を結構使うのですが、いつも悩むのが非同期処理のメソッドをページ遷移と同時に実行するにはどうすればいいのかということです。というわけで自分なりに色々考えてみました。

どんな方法がある?

パターンとしては以下の3通りがあると思います。
  
1. ViewまたはViewModelのコンストラクタ内で実行する
2. ViewのOnAppearingメソッドで実行する
3. ViewのAppearingイベントで実行する
  
1と2の方法は、コンストラクタ自体は非同期にできないので、呼び出す非同期メソッドをasync voidとすれば画面上の動きとしてはうまくいっているように見えます。しかし、非同期処理について調べると「async voidイベントハンドラ以外では使わない」というのが鉄則のようです。また、Visual Studioからも「非同期メソッドでvoidを返すべきではない」と注意されます。

イベントハンドラを使えばいい!

そこで3の方法です。イベントなのでasync voidでも問題ないはずです。具体例としてコードを書くと以下のようなります。ページ遷移直後は待機中と表示し、3秒後に待機完了という文字列に変更するものです。

View

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:local="clr-namespace:XF_VmConstructor" 
             Appearing="MainPage_Appearing"
             x:Class="XF_VmConstructor.MainPage">
    <ContentPage.BindingContext>
        <local:MainPageViewModel x:Name="mainPageViewModel"/>
    </ContentPage.BindingContext>
    <StackLayout>
        <Label Text="{Binding Label}" 
               HorizontalOptions="Center" 
               VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

  
コードビハインド

using System;
using System.ComponentModel;
using Xamarin.Forms;

namespace XF_VmConstructor
{
    [DesignTimeVisible(true)]
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        async void MainPage_Appearing(object sender, EventArgs e)
        {
            await mainPageViewModel.AsyncMethod();
        }
    }
}

  
ViewModel

using System.ComponentModel;
using System.Threading.Tasks;

namespace XF_VmConstructor
{
    public class MainPageViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public async Task AsyncMethod()
        {
            Label = "待機中";
            await Task.Delay(3000);
            Label = "待機完了";
        }

        private string label;
        public string Label
        {
            get { return label; }
            set
            {
                if(label != value)
                {
                    label = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Label)));
                }
            }
        }
    }
}

ViewModelで非同期なメソッドを定義して、ViewのAppearingイベントで呼び出しています。 この方法ならVisual Studioにも怒られずに済みます。

気になっているのが、Xamarin.Formsのサンプルコードでasync voidを使っているメソッドをたまに見るんですよね(async void OnAppearingとか)。それでも問題ないんですかね??やはり非同期処理についてしっかり理解しなければダメだな・・・。

といったところで今回は失礼します。