takataka430’s blog

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

【Xamarin.Forms】CheckBoxをListView内で利用して複数選択する方法

少し前になりますが、Xamarin.Forms 4.1.0 Pre-Release でチェックボックスが使えるようになりましたね。

devblogs.microsoft.com

実は以前からListViewの要素を複数選択したかったので、これを見た瞬間に「これ使えばできるのでは!?」と思いやり方を調べてみました。iOSの編集モードのような機能を目指しています。それでは早速コードを見てみましょう。

環境

Visual Studio Community 2019 for Mac
Xamarin.Fomrs (4.1.0.496342 pre2)

コード

画面

<?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:CheckBox" 
             x:Class="CheckBox.MainPage">
    <ContentPage.ToolbarItems>
        <ToolbarItem Text="編集"
                     Clicked="Handle_Clicked"/>
    </ContentPage.ToolbarItems>
    <StackLayout>
        <ListView x:Name="list">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <Grid>
                            
                            <!-- 2行1列のグリッドを作成 -->
                            <Grid.ColumnDefinitions>
                                
                                <!-- チェックボックスを表示する領域 -->
                                <ColumnDefinition Width="30"/>
                                
                                <ColumnDefinition Width="*"/>
                                
                            </Grid.ColumnDefinitions>
                            
                            <!-- Personクラスのプロパティにバインディング -->
                            <CheckBox IsChecked="{Binding IsChecked}"
                                      IsVisible="{Binding EditMode}"
                                      Grid.Column="0"/>
                            <Label Text="{Binding Name}"
                                   Grid.Column="1"
                                   VerticalOptions="Center"/>
                        </Grid>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage>

コードビハインド

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Xamarin.Forms;

namespace CheckBox
{
    // Learn more about making custom code visible in the Xamarin.Forms previewer
    // by visiting https://aka.ms/xamarinforms-previewer
    [DesignTimeVisible(true)]
    public partial class MainPage : ContentPage
    {
        //ListView表示用のコレクション
        private ObservableCollection<Person> person = new ObservableCollection<Person>
        {
            new Person{Name = "A"},
            new Person{Name = "B"},
            new Person{Name = "C"},
            new Person{Name = "D"},
            new Person{Name = "E"},
        };

        public MainPage()
        {
            InitializeComponent();

            //ListViewの要素を指定
            list.ItemsSource = person;
        }
        
        //チェックボックスの表示・非表示の切り替え
        void Handle_Clicked(object sender, EventArgs e)
        {
            foreach (var a in person)
            {
                if(a.EditMode == true)
                {
                    a.EditMode = false;
                    a.IsChecked = false;
                }
                else
                {
                    a.EditMode = true;
                }
            }
        }
    }

    //変更通知がいくようにしないとObservableCollectionの要素内のプロパティが変更されても画面に反映されない
    public class Person : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

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

        private bool isChecked;
        public bool IsChecked
        {
            get { return this.isChecked; }
            set
            {
                if (isChecked != value)
                {
                    this.isChecked = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsChecked)));
                }
            }
        }

        private bool editMode;
        public bool EditMode
        {
            get { return this.editMode; }
            set
            {
                if (editMode != value)
                {
                    this.editMode = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(EditMode)));
                }
            }
        }
    }
}

コメントにも書いていますが、PersonクラスにINotifyPropertyChangedインターフェイスを継承させて変更通知が行くようにしないと、EditModeプロパティの値が変更されても画面に反映されません。

それでは画面の動きを見てましょう。

f:id:takataka430:20190629215441g:plain:w200

このコードでは表示・非表示の切り替えのみですが、例えばチェックした要素のみコレクションから削除する機能を実装すれば選択削除ができますね。
ぜひ試してみてください!
  
(2019/7/9 追記) 実装編も書きました↓ takataka430.hatenablog.com

【Xamarin.Forms】ViewModelからDisplayAlertを呼び出す方法

最終更新:2019年5月29日

Xamarin.Formsを使ってMVVMアーキテクチャにする場合、ViewModelからDisplayAlertを呼び出す方法がわからなくて困っていたのですが、実現する方法がありました。

環境

Visual Studio Community 2019 for Mac
Xamarin.Forms (3.6.0.264807)

手順

やり方は意外に簡単でApplication.Current.MainPage.DisplayAlertをViewModelで使うだけです。

引数を3つにするとキャンセルボタンだけのアラート、4つにするとBool値を返すことができます。
docs.microsoft.com

以下、サンプルコードです。 (コードビハインドはInitializeComponent()のみなので省略しています)

View(MainPage.xaml.cs)

<?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:DisplayAlertFromViewModel" 
             x:Class="DisplayAlertFromViewModel.MainPage">
    <ContentPage.BindingContext>
        <local:MainPageViewModel/>
    </ContentPage.BindingContext>
    <StackLayout>
        <Button Text="Alert" 
                Command="{Binding OnButton}"
                HorizontalOptions="Center" 
                VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

  
ViewModel(MainPageViewModel.cs)

using Xamarin.Forms;

namespace DisplayAlertFromViewModel
{
    public class MainPageViewModel
    {
        public MainPageViewModel()
        {
            OnButton = new Command(async() =>
            {
                var select = await Application.Current.MainPage.DisplayAlert("Alert", "アラートです", "OK","キャンセル");
                if (select)
                    await Application.Current.MainPage.DisplayAlert("Alert", "OKが選択されました", "終了");
                else
                    await Application.Current.MainPage.DisplayAlert("Alert", "キャンセルが選択されました", "終了");
            });
        }

        public Command OnButton { get; }
    }
}

以下のような動きになります。

f:id:takataka430:20190524015457g:plain:w250

これを使えばMVVMなアプリを作りやすくなりそうですね!

App Center Auth で少し苦労した点

最近MBaaS機能が追加されたApp Centerですが、さっそく認証機能(App Center Auth)を試してみました。その時にハマったことのメモです。
  
結論から言いますと、現状App Center Authを使う場合は「マイクロソフトアカウントでログインしたApp Center」を使う必要があるようです。
  

どこでハマった?

基本的には以下のドキュメントの手順に沿っていけばうまくいくと思います。

docs.microsoft.com

ハマったのは以下のページ(「Getting Started with App Center Auth Service」内の「Configure the App Center Auth service」)の6番の場所、つまりApp CenterからAzure AD B2Cに接続するところです。

Getting started with the App Center Auth Service - Visual Studio App Center | Microsoft Docs

私はGithubアカウントを用いてApp Centerにログインしていたのですが、以下のように表示されました。

f:id:takataka430:20190517190454p:plain:w250

文章を読むとマイクロソフトアカウントを用いてログインしたApp CenterでないとAzure AD B2Cを読み込むページが表示されないようです。

ちなみに、中央の「Sign in」ボタンを押すと、マイクロソフトアカウントのログインページに移動します。そのままログイン処理を進めるとマイクロソフトアカウントでログインしたApp Centerにログインした状態になりました。この状態であればAzure AD B2Cを読み込むページが表示されます。

冷静に文章を読めばわかると思うのですが、自分は結構悩んでしまいました。(ドキュメントにはApp Centerのログイン方法って指定されてないですよね・・・?)
  
以上、ご参考になれば幸いです。

App CenterにMBaaSの機能が追加

App Centerに認証(App Center Auth)とデータ連携(App Center Data)の機能がプレビューで追加されましたね。

devblogs.microsoft.com

現状では認証はAzure AD B2C、データ連携はCosmosDBのみの対応のようです。今後はもっと改善していくようなのでフィードバックを送ればもっと機能を追加してくれるのではないかと思います。
  
ところで、MBaaSといえばAzure Mobile Appsがありますね。新しい発表がないなあと思ったら、もう開発はしていないようですね。

github.com

上のリンクの「Future of Azure Mobile Apps」にその旨が書いてあります。
Mobile Apps好きだったので残念ですが、App CenterのMBaaS機能に期待しましょう!

【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とか)。それでも問題ないんですかね??やはり非同期処理についてしっかり理解しなければダメだな・・・。

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

Xamarinの実機デバッグまとめ

Xamarinでアプリの挙動を確認する時、実機で動かして確認したいですよね。そのためのまとめメモです。

開発PCと実機デバッグを行う端末の組み合わせ

それぞれの組み合わせで実機デバッグができるかどうかを表にしてみました。

f:id:takataka430:20190414014543p:plain:w400

よく「XamarinをやるならPCはMac」と言われますが、このようにMacだとAndroidiOSも実機デバッグできるからなんだと思います。なのでもし、例えばXamarin.Androidを使ってAndroidアプリのみを作るのであればWindowsでも問題ないと思います。
(・・・が、個人的にはAndroidに関してもMacの方がトラブルが少ないような気がするのでMacを使うことをオススメしたいです)

実機デバッグのための手順は?

以下の通りMicrosoftの公式ドキュメントに手順が記載されていました。

Xamarin.iOS(Xamarin.FormsでiOSを実機デバッグする場合含む)

docs.microsoft.com

Apple Developer Programに参加している場合は「自動プロビジョニング」「手動プロビジョニング」のどちらかを実行してください。推奨は自動プロビジョニングの方みたいです。
Apple Developer Programに参加していない場合は「無料プロビジョニング」の手順を実行します。なおApple IDが必要になります。

Xamarin.Android(Xamarin.FormsでAndroidを実機デバッグする場合含む)

docs.microsoft.com

AndroidiOSに比べると設定が簡単でいいですね。   
  


動きを確認するのはシミュレータでもできますが、やはり実機で動きを確認した方がいいと思います。実機デバッグの方がトラブルも少ないと思うので、ぜひ使ってみてください。

「Visual Studio 2019 を試してみる会」に参加しました

4月10日(水)に開催された「【Launch 記念】Visual Studio 2019 を試してみる会」に参加しました。

csugjp.connpass.com

Visual Studio2019のGitの使い方およびGithubとの連携、IntelliCodeの使い方、VS for Macなどの発表がありとても参考になりました。

発表資料はこちらにあるのでぜひ見てみてください。 csugjp.connpass.com

感想

・Gitは普段コマンドで使っているのですが、Visual Studioの機能を使えば手軽にコミットやプッシュなどができるのでこちらもいいと思いました。
・IntelliCodeは学習に時間がかかったり注意点もあるみたいですが、良さそうな機能ですね。チームで共有は便利そうだと思いました。
・VS for MacにもIntelliCodeとLive shareが追加されてほしいのでこれからもどんどんVS for Macを使います!みなさんも、是非VS for Macを使いましょう!!



ちなみに、Visual Studio 2019 for MacでXamarin.Formsを使ってみた感想を過去のブログで書いているのでよろしければ読んでください。 takataka430.hatenablog.com