「JXUGC #22 最新事例&お前のアプリを説明してもらおうの会」に参加した話

今まで、ユーザーグループやコミュニティのイベントには消極的だったのですが、2017年から色々なところに参加させていただこうかと思い、本日(1/28)の「JXUGC #22 最新事例&お前のアプリを説明してもらおうの会」にも参加させていただきました。
このイベントは比較的大きく、参加者は60人位いたでしょうか。

で、その感想を、長々と・・・会社で長々すると「面倒臭がられるけど」ここは「俺の自由の場だ!」ということで自由に長々します!(笑)。

では各セッション毎の感想を!

「がんばれガンプ ソルバルウを倒せ について」

@hiro128_777 さんのセッションでした。

twitter.com

ゼビウスの派生ゲームで、ソルバルウを倒す側のゲームを現在開発中ということで、それについてのお話でした(公式ゲーで無料公開されるそうです)。
技術的には、「Xamarin Forms」「Cocos Sharp」なんかを使っているとのこと。
Cocos Sharpは、2D / 3Dのゲーム開発のライブラリで、でもFormsの「UI要素(Label等)」と同様に2Dアニメーション要素(例えばソルバルウのキャラ画像)を扱えるとの事。
そして、その仕組みが故、FormsのPCLに実装を持ってくることができ、普通に作ってもコードの90%を(iOS / androidの)共有コードに持ってくることができるそうです。

私は、ゲームの世界は全く概念の異なる異世界のように考えてしまっていたのですが、なんだか身近に感じることができました。
そして、セッション中にもおっしゃられていましたが、ゲーム以外のアプリにもよりリッチなアニメーション効果を追加したい場合には、Cocos Sharpが使えるんじゃないか、と。
そうそう、うちの会社はエンタープライズな自社サービスを作ってるけど、ちょっとリッチな視覚効果とか好きだから使えるかも!?なのではないだろうか、と思いました。

GitHub - mono/CocosSharp: CocosSharp is a C# implementation of the Cocos2D and Cocos3D APIs that runs on any platform where MonoGame runs.

「ゆるふわ Xamarin Tips」

@Santea3173 さんのセッションでした。

twitter.com

[2017/1/29 リンク追記]

bit.ly

サンテアさんは初めて見たのですが、院生だったんですね。あとtwitterのアイコンからもっとチャラいイメージを持っていましたが、全くもって「賢い紳士な大学院生」でした(笑)。
twitterをデータソースとして、SQLを使って検索一覧表示を行うというアプリの紹介でした。
また、そのアプリで使っているライブラリの紹介などもしていただきました。Syncfusionの「Essential Studio For Xamarin」のDataGridやPDFViewerに興味を持ったので、今度自分でも使って見たいと思いました。

Introducing Essential Studio for Xamarin : Feature-rich data visualization and file format components

また「Xamarin Formsを使うべきアプリ」「Xamarin Traditionalを使うべきアプリ」の指針も非常にまとまっていて、会社での技術選定にも役立てることができる内容だったと思います。

Xamarin Component / Nuget / Xamarin Forms Labs の説明も非常に整理されていて良かった印象です。
Xamarin Forms Labsが

This project is no longer maintained. It may not work with newer versions of Xamarin.Forms.

なのは残念に思っているのですが、別のものに置き換わるのでしょうか?

「Reactive ProperrtyでXamarinアプリの作り方が変わった」

@iseebi さんのセッションでした。

twitter.com

実際の開発において既存ネイティブアプリをXamarinで書き換えた経緯や技術の紹介をしていただきました。
最近私は MVVM フレームワークとしては Prism ばかり見ていたので、MVVM Light を使われたお話が逆に新鮮でした。
実は昔、私は Silverlight 開発を行っていたことがあり、その時はMVVM Lightを使っていたのです。
そして Reactive Property は素晴らしいので、もはや今からでは「最新技術を紹介するぞ!」な内容ではありませんが、自分なりの整理をこのブログでも記したいと思っています。
えーと、たしか、いせさんは「フェンリルさんの方でしたよね・・・で、採用の宣伝もしていた・・・」。
フェンリルさんは自社ソフトウェア・自社サービスの開発も行っているようで、私が所属している会社も同じく自社サービス開発企業なので、最近失われていた「こんな面白いアーキテクチャ、こんな面白い技術」を自分の会社にも広めたいな心が、少し発生しました(笑)。

「証券取引アプリ について」

@omanuke さんのセッションでした。

twitter.com

何と言っても 「F#」 っていうのが印象的でした。
証券取引アプリ や Xamarin というよりも、とにかく 「F#愛!F#推し!」 を強調されていましたね。
うん、確かにデモで見せていただいたコードは「F#すげー!C#どんくさいー!」な内容でした。
そう、C#で長々となるロジックがF#だと超簡潔に書けてしまうのです。
これは「型推論の言語仕様の違い」「優秀なパターンマッチング」から来るそうです。
デモの中でも、仕様変更に対して、C#ではコードの全体の各所の修正が必要になるケースでも、F#ではごくわずかな修正で対応できる例を紹介されていました。

でも、すみません!、私はC#を使い続けるでしょう(笑)。真面目な話、F#使ったら、みんな理解できないと思うのです・・・もし使って自社のソフトを開発したら・・・「あれはF#使ってるから分かんねーよ」と、虐げられるソフトウェアになってしまうと思うのです;;

真面目に技術者として気になったのは
「あのF#コードは、コンパイル後にはどのようなILになるのだろう?」
でした。おそらく、普通のIF分岐等に解釈されるものと想像していますが、C#的概念からは不思議を感じそうです。今度確認します!

あとomanukeさんのキャラが可愛かったです^^結構好きです^^

「AzureVM Power Switch について」

@yamamo さんのセッションでした。

twitter.com

Azureの仮想マシンをXamarin Formsアプリから「状態確認・開始・終了」するというアプリの紹介でした。
技術的には、「Active Directory Authentication Library」はXamarinにも対応してたんだあ!と思いました。ADALは以前、仕事で自社サービスをAzureAD認証するのに使ったことがあったので(Webアプリ)。
このアプリは、私の属する会社でも「使えるアプリ」になりそうな気がしました。AzureのREST APIは機能的に豊富なので、超実用的なアプリにブラッシュアップすることが可能な気がしてしまいました(紹介された以外の機能も既にある?かは不明なのですが)。

XAML について」

@okazuki さんのセッションでした。

twitter.com

[2017/1/29 リンク追記] blog.okazuki.jp

この手の話、私は結構好きです。(元MSの萩原さん的な)
実は数年前に私が執筆した本の中でWPFについて書いたとき、本日、かずきさんが語ってくれたことを散々苦労しながら調べたのです(文書にする場合、「使える」以上の明確な理解と整理が必要なので^^;)。

gihyo.jp

以下のような技術要素になります。

  • XAML名前空間(xmlns)とC#名前空間
  • 要素とクラス
  • 属性とプロパティ
  • 添付プロパティ
  • コンテンツプロパティ
  • ディペンデンシープロパティ(今日のセッションでは出なかったけどXamarinだとバインダブルプロパティ)

多分、これらに関する知識が曖昧でもJXUGの場でこんなの作ったよー、って紹介できるし、十分開発できると思います。
でも、何かあった時、これらの基礎技術知識があるとないとでは、問題解決能力の差が出て来ると思うのです。
知っていれば10分で解決、知らないと延々分からない、的な。

「LT枠の皆様」

基本的に皆さん「制限時間内に伝えるべきことを楽しく明確に伝えることができていて」素晴らしい!って感想を持ちました!
年齢層は様々だったと思いますが、若い方、学生の方なんかが、ガンガンXamarinに取り組んでいて、さらに形ある成果物をあげているのが素晴らしいと思いました。
ここに出て来るような人達が、うちの会社にも毎年入社してくれればいいのに!って感じです(笑)

まとめ

今回、懇親会に参加予定だったのですが、所用で参加できずでした。ちょっと、話をしてみたい方も何人かおられたのですが、またの機会にお願いしたいです。

また、JXUGのイベントには大規模なものも小規模なものも参加させていただきたいと思います。

そして、自分自身に思った事は
「もっと手を動かそう!作っちゃおう!」
でした。
私は結構、「学術的に」というか論理的に物事を明確にしたがるので、実用的なものを作るより、「調査」をしてしまっているように思うので、もっと「考える前に作っちゃおう!」と思いました。

あっ、あと「全行ブレークポイントにはふいた!(笑)」

Xamarin Studio(Xamarin Forms)でxUnitする

Xamarin StudioでxUnitを利用する方法についてメモしておきます。

ユニットテストツール xUnit を Xamarin Studioで使用する方法になります。

誰でも出来るように丁寧に画面キャプチャして説明したいと思います。

アドインを追加する

テストランナーを Xamarin Studio IDE に統合する為にアドインを追加します。

Xamarin Studioを起動し「Xamarin Studio→アドイン...」メニューを選択します。

f:id:daigo-knowlbo:20170123235243p:plain

「アドイン マネージャー」が表示されます。
「ギャラリー」タブを選択し、右上の検索テキストボックスに「xunit」と入力します。
リストから「xUnit.NET 2 testing framework support」を選択し、「インストール...」ボタンをクリックします。

f:id:daigo-knowlbo:20170123235405p:plain

テスト対象のソリューションを作成

「ファイル→新しいソリューション」メニューを選択し、XFormApp1という Xamarin Forms ソリューションを作成します。

f:id:daigo-knowlbo:20170124013700p:plain

f:id:daigo-knowlbo:20170124013718p:plain

f:id:daigo-knowlbo:20170124013746p:plain

完成したソリューションは以下。

f:id:daigo-knowlbo:20170124013812p:plain

ユニットテストプロジェクトの追加

XFormsApp1ソリューションに、xUnitプロジェクトを追加します。

「ソリューション」からXFormsApp1をマウス右ボタンクリックし、「追加→新しいプロジェクトを追加」を選択します。

f:id:daigo-knowlbo:20170124013850p:plain

「ライブラリ→ポータブル ライブラリ」を選択します。

f:id:daigo-knowlbo:20170124014054p:plain

プロジェクト名を「xFormTest」とします。

f:id:daigo-knowlbo:20170124014114p:plain

ソリューション→xFormTestの「パッケージ」をダブルクリックします。

f:id:daigo-knowlbo:20170124015913p:plain

「パッケージを追加」ウィンドウが表示されるので「xUnit.net」を追加します(Add Packageクリック)。

f:id:daigo-knowlbo:20170124014126p:plain

ソリューションツリーから xFormTest の 参照 をダブルクリックし、テスト対象プロジェクト「xFormsApp1」への参照を追加しておきます。

f:id:daigo-knowlbo:20170124014208p:plain

ユニットテストコードの追加

xFormTestプロジェクトにユニットテストコードを追加します。
「ソリューション」から xFormTest をマウス右ボタンクリックし「追加→新しいファイル」を選択します。

f:id:daigo-knowlbo:20170124014146p:plain

「空のクラス」を選択し、名前に「RunningManTest」と入力します。
(Xamarin Forms PCLプロジェクトに RunningMan クラスを追加予定なので、そのユニットテストクラスを用意する想定です)

f:id:daigo-knowlbo:20170124014157p:plain

RunningManクラスは Run_4kmPerHour(int second) / Run_5kmPerHour(int second) / Run_6kmPerHour(int second) といった各時速ごとの走るメソッドを持ち、各々に消費カロリーが記録(計算)されるクラスです。
そのテストメソッドの実装が以下の RunningMaTest クラスになります。

// xFormTest/RunningMantest.cs
using System;
using Xunit;

namespace xFormTest
{
  public class RunningManTest
  {
    [Fact]
    public void TestCase1()
    {
      XFormsApp1.Models.RunningMan runningMan = new XFormsApp1.Models.RunningMan();
      runningMan.Run_4kmPerHour(10); // 4km/hで10分
      runningMan.Run_5kmPerHour(20); // 5km/hで20分

      // 消費カロリーをチェック
      Assert.Equal(runningMan.TotalCal, 2.79 * 10 + 3.90 * 20);
    }

    [Fact]
    public void TestCase2()
    {
      XFormsApp1.Models.RunningMan runningMan = new XFormsApp1.Models.RunningMan();
      runningMan.Run_6kmPerHour(15); // 6km/hで15分

      // 消費カロリーをチェック
      Assert.Equal(runningMan.TotalCal, 5.70 * 15);
    }
  }
}

テスト対象クラスを実装

テスト対象クラス「xFormsApp1/models/RunnnigMan.cs」を追加します。
まず Modelsフォルダ を追加。

f:id:daigo-knowlbo:20170124024439p:plain

RunningMan.cs を追加。

f:id:daigo-knowlbo:20170124024525p:plain

f:id:daigo-knowlbo:20170124020718p:plain

// xFormsApp1/Models/RunningMan.cs
using System;
namespace XFormsApp1.Models
{
  public class RunningMan
  {
    /// <summary>
    /// 合計消費カロリー
    /// </summary>
    /// <value>The total cal.</value>
    public double TotalCal { get; private set;}

    /// <summary>
    /// 時速4kmで走ります。(消費カロリー:2.79kcal/sec)
    /// </summary>
    /// <param name="seconds">継続時間</param>
    public void Run_4kmPerHour(int seconds)
    {
      this.TotalCal += 2.79 * seconds;
    }

    /// <summary>
    /// 時速5kmで走ります。(消費カロリー:3.90kcal/sec)
    /// </summary>
    /// <param name="seconds">継続時間</param>
    public void Run_5kmPerHour(int seconds)
    {
      this.TotalCal += 3.90 * seconds;
    }

    /// <summary>
    /// 時速6kmで走ります。(消費カロリー:5.70kcal/sec)
    /// </summary>
    /// <param name="seconds">継続時間</param>
    public void Run_6kmPerHour(int seconds)
    {
      this.TotalCal += 5.70 * seconds;
    }
  }
}

テスト実行

メニュー「表示→テスト」をクリックします。

f:id:daigo-knowlbo:20170124014226p:plain

単体テスト」「テスト結果」ペインが表示されます。
単体テスト」には先ほど実装した単体テストメソッドが表示されています。(テストメソッドへの[Fact]属性から自動的に認識されています)

f:id:daigo-knowlbo:20170124014609p:plain

単体テスト」の「すべて実行」ボタンをクリックすると、全ての単体テストが実行されます。
今回実装した2つのテストはともに成功するので、緑の丸がつきます。

f:id:daigo-knowlbo:20170124014630p:plain

Visual Studio for Mac で動かないの?

と私自身思いまして、ググったら・・・今のところサポートプランは無いそうです・・・;;

github.com

そもそも、私自身 Xamarin Studioのアドインや Visual Studio for Macの Extensions について、その仕組みやアーキテクチャについて知識がありませんでした。
ということで、これをきっかけに興味を持ったので、暇があったら勉強したいと思います。

プレリリース版のXamarin Formsを使う方法

前回のポストでプレリリース版「Xamarin.Forms 2.3.4.184-pre1」のPickerコントロールについて触れました。

Xamarin Studio や Visual Studio for Mac を利用して新規ソリューション(プロジェクト)を作成した場合、通常、Stable版のXamarin Formsプロジェクトが自動生成されます。

このプロジェクトに対して最新プレリリース版Xamarin Formsを適用する手順について説明します。
以下Xamarin Studioベースで画像キャプチャしていますが、Visual Studio for Macでも同様の手順です。
Visual Studio 2015(windows)における手順も後述します)

Xamarin Studio / Visual Studio for Macの場合

ソリューションの作成

Xamarin Srudio もしくは Visual Studio for Mac で、新規のXamarin Formsアプリケーション ソリューションを作成します。
ここではソリューション名を「PreExample1」としました。Prismも使わないシンプルなXamarin Formsソリューションとしています。

f:id:daigo-knowlbo:20170114154148p:plain

パッケージの追加

PCLプロジェクト(PreExample1)、Androidプロジェクト(PreExample1.Droid)、iOSプロジェクト(PreExample1.iOS)の各プロイジェクトの「パッケージ」に対してデフォルトでは、 stable版 の Xamarin.Forms パッケージへの参照が追加されています。
これをプレリリース版に置き換えましょう。
パッケージをマウス右ボタンクリックし「パッケージの追加」メニューを選択します。

f:id:daigo-knowlbo:20170114154211p:plain

「パッケージを追加」ウィンドウが表示されます。

f:id:daigo-knowlbo:20170114155725p:plain

左下の「プレリリース パッケージを表示する」にチェックを入れ、右上の検索ボックスに「xamarin forms」と入力します。
一覧に表示された「Xamarin.Forms」を選択し、右下のバージョン ドロップダウンリストから利用したいバージョンを選択します(ここでは 2.3.4.184-pre1を選択)。
「OK」ボタンをクリックするとパッケージの追加(更新)が行われます。

パッケージの更新完了後に、「パッケージ→Xamarin.Forms」をマウス右ボタンクリックすると、バージョンが更新されている事を確認することができます。

f:id:daigo-knowlbo:20170114160028p:plain

Android / iOSプロジェクトのパッケージも更新

上述で「PCLプロジェクト(PreExample1)」のパッケージが更新されました。
同様の手順で「Androidプロジェクト(PreExample1.Droid)・iOSプロジェクト(PreExample1.iOS)」のパッケージも最新のXamarin.Formsに更新しましょう。

Visual Studio 2015(Windows)の場合

Xamarin Formsプロジェクトを作成します。
ソリューションエクスプローラーからプロジェクトを選択→マウス右ボタン→NuGetパッケージの管理を選択します。

f:id:daigo-knowlbo:20170115122945p:plain

以下の「NuGetパッケージマネージャー」が表示されるので、Xamarin.Formsを選択→「プレリリースを含める」にチェック→「バージョン」から最新のプレリリース版を選択→更新ボタンクリック、操作を行います。

f:id:daigo-knowlbo:20170115122958p:plain

上記操作をiOS / Android / UWPの各プロジェクトにも行います。

最新版のXamarin Formsで遊べる!

さあ、最新プレリリース版の Xamarin Forms で遊びましょう!

PickerがBindableになるそうだ(Xamarin.Forms 2.3.4.184-pre1)

Xamarin Forms + MVVM開発において最も相性の悪いコントロールの代表である Picker がBindableになるそうです。
現在 Xamarin.Forms 2.3.4.184-pre1 にて実装が提供されています。

blog.xamarin.com

正式版への取り込みは以下のロードマップに記述されている通り「2.4.0 - February 2017」となるそうです。

forums.xamarin.com

そう、従来の(現状の)Pickerコントロールには、ListView.ItemsSourceのようなBindablePropertyが用意されていなかったんですよね。
あと、SelectedItemプロパティも無かったので、 SelectedIndexプロパティから選択オブジェクトを取得するという回りくどい実装が必要でした。

ということでPickerの派生クラスを自前で作ってBindablePropertyを用意するような事をする必要がありました。
以下のような感じで。

github.com

これがネイティブにサポートされる予定です(嬉しい)。

「Xamarin.Forms 2.3.4.184-pre1」のPickerを使ったサンプルを以下に置きました。

XamarinExamples/Prism/Control/UseBindablePicker at master · ryuichi111/XamarinExamples · GitHub

BindablePickerと呼んでいますが、実装自体は「Pickerコントロール」に 「ItemsSourceプロパティ / SelectedItemプロパティ」が追加される形でのアップデートになります。

Xamarin Forms(Prism)のシンプルサンプルをGithubにあげた

Xamarin Forms & Prismにおける超シンプルなサンプルをGithubにあげました。

github.com

サンプルの方針は以下です。

  • Xamarin Forms + Prismプロジェクトである。
  • 各種UIコントロールのプロパティはViewModelのプロパティとデータバインディングする。
  • 各種UIコントロールのイベントはCommand経由でViewModelのICommandにデータバインディングする。
  • 単一機能の超シンプルなサンプルを目指す。

基本的には「Buttonの使い方」とか「DatePickerの使い方」とか単一機能のシンプルなサンプルです。
内容的には、まだ、まったく足りていないので「1日1github」でソースを追加していこうと思っています(コメントとかも書いてないので・・・)。

現状は「いくつかのコントロールの使い方サンプル」「MasterDetailPageの使い方サンプル」のみ載せています。

ではー。

Prism(Xamarin Forms) における INotifyPropertyChanged

Xamarin Formsおいて ソース→ターゲット のデータバインディングでは、ソースオブジェクトに INotifyPropertyChangedインターフェイス を実装する必要があります(OneTimeモードは除く)。

この点について「素のXamarin Forms」と「Xamarin Forms With Prism」での実装方法を比較してみます。

また、Prism内部の実装を覗いて「ライブラリとして何をサポートしてくれているのか?」を見てみたいと思います。

こんなサンプルで説明します

以下のようなサンプルで説明を進めたいと思います。
実行画面は以下の通り。

f:id:daigo-knowlbo:20170109015009p:plain

サンプルの要点は以下の通りです。

  • R G Bの各値をソースとして画面上の各ラベルにデータバインドする
  • 「set White」/「set Black」/「set Yellow」のボタンをクリックするとソースの RGB値 がロジック上で変更される
  • ロジック上で変更された RGB値 は 即座に画面表示(ラベル)に反映される

素のXamarin Formsでは

ソースオブジェクトとして「MyColorクラス」を実装する事とします。
ソースオブジェクトは INotifyPropertyChangedインターフェイス を実装します。
ソース値変更時には PropertyChanged を呼び出します。

// MyColor.cs(ソースとなるオブジェクト)
using System;
using System.ComponentModel;

namespace Example1.Models
{
  public class MyColor : INotifyPropertyChanged
  {
    // INotifyPropertyChangedインターフェイスの実装
    public event PropertyChangedEventHandler PropertyChanged;

    // Fields
    private int red;
    private int green;
    private int blue;

    // Properties
    public int Red {
      get {
        return this.red;
      }
      set {
        if (this.red != value) {
          this.red = value;
          OnPropertyChanged("Red");
        }
      }
    }

    public int Green {
      get {
        return this.green;
      }
      set {
        if (this.green != value) {
          this.green = value;
          OnPropertyChanged("Green");
        }
      }
    }

    public int Blue {
      get {
        return this.blue;
      }
      set {
        if (this.blue != value) {
          this.blue = value;
          OnPropertyChanged("Blue");
        }
      }
    }

    // プロパティ値の変更を通知します
    protected virtual void OnPropertyChanged(string propertyName)
    {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
  }
}
  • PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    「?」はC#言語仕様で「nullでなければ呼び出す」、つまりPropertyChangedがnullでなければInvoke()を呼び出します。

ページの実装は以下の通りです。

// CustomColorPage.xaml(ページ定義)
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
  xmlns="http://xamarin.com/schemas/2014/forms" 
  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
  x:Class="Example1.CustomColorPage">

  <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
    <StackLayout Orientation="Horizontal">
      <Label Text="Red:"/>
      <Label Text="{Binding Red}" />
    </StackLayout>
    <StackLayout Orientation="Horizontal">
      <Label Text="Green:"/>
      <Label Text="{Binding Green}" />
    </StackLayout>
    <StackLayout Orientation="Horizontal">
      <Label Text="Blue:"/>
      <Label Text="{Binding Blue}" />
    </StackLayout>
    <StackLayout Orientation="Vertical">
      <Button Text="set White" Clicked="whiteButtonClicked"/>
      <Button Text="set Black" Clicked="blackButtonClicked"/>
      <Button Text="set Yellow" Clicked="yellowButtonClicked"/>
    </StackLayout>
  </StackLayout>
</ContentPage>

コードビハインドクラスで、ソースオブジェクト MyColor をインスタンス化して、BindingContextに設定する事でデータバインディングを行います。

// CustomColorPage.xaml.cs(コードビハインドクラス)
using System;
using Xamarin.Forms;

using Example1.Models;

namespace Example1
{
  public partial class CustomColorPage : ContentPage
  {
    // ソースオブジェクト
    public MyColor MyColor { get; } = new MyColor();

    // コンストラクタ
    public CustomColorPage()
    {
      InitializeComponent();

      this.BindingContext = this.MyColor;
    }

    // Whiteボタンクリックイベントハンドラ
    public void whiteButtonClicked(object sender, EventArgs e)
    {
      this.MyColor.Red = 255;
      this.MyColor.Green = 255;
      this.MyColor.Blue = 255;
    }

    // Blackボタンクリックイベントハンドラ
    public void blackButtonClicked(object sender, EventArgs e)
    {
      this.MyColor.Red = 0;
      this.MyColor.Green = 0;
      this.MyColor.Blue = 0;
    }

    // Yellowボタンクリックイベントハンドラ
    public void yellowButtonClicked(object sender, EventArgs e)
    {
      this.MyColor.Red = 255;
      this.MyColor.Green = 255;
      this.MyColor.Blue = 0;
    }
  }
}

Prismでは

ソースオブジェクトは BindableBaseクラス を継承します。
ソースオブジェクトはViewModelクラスとします。
ソース変更時には SetProperty() メソッドを呼び出します。

// MainPageViewModel.cs(ビューモデルクラス)
using Prism.Commands;
using Prism.Mvvm;
using System.Windows.Input;

namespace PrismExample1.ViewModels
{
  public class CustomColorPageViewModel : BindableBase
  {
    // Fields
    private int red;
    private int green;
    private int blue;

    // Properties(for DataBind)
    public int Red {
      get {
        return this.red;
      }
      set {
        this.SetProperty(ref this.red, value);
      }
    }

    public int Green {
      get {
        return this.green;
      }
      set {
        this.SetProperty(ref this.green, value);
      }
    }

    public int Blue {
      get {
        return this.blue;
      }
      set {
        this.SetProperty(ref this.blue, value);
      }
    }

    // コマンド
    public ICommand WhiteCommand { get; }

    public ICommand BlackCommand { get; }

    public ICommand YellowCommand { get; }

    // Constructor
    public CustomColorPageViewModel()
    {
      // ボタンクリックコマンド時のイベント処理
      this.WhiteCommand = new DelegateCommand(() =>
      {
        this.Red = 255;
        this.Green = 255;
        this.Blue = 255;
      });
    
      this.BlackCommand = new DelegateCommand(() =>
      {
        this.Red = 0;
        this.Green = 0;
        this.Blue = 0;
      });

      this.YellowCommand = new DelegateCommand(() =>
      {
        this.Red = 255;
        this.Green = 255;
        this.Blue = 0;
      });
    }
  }
}

ページ実装は以下の通りです。
ボタンクリック時の挙動は Command をCustomColorPageViewModeの各ICommandにデータバインディングします。

// CustomColorPage.xaml(ページ定義)
<?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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" 
  prism:ViewModelLocator.AutowireViewModel="True" 
  x:Class="PrismExample1.Views.CustomColorPage" 
  Title="MainPage">
  <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
    <StackLayout Orientation="Horizontal">
      <Label Text="Red:"/>
      <Label Text="{Binding Red}" />
    </StackLayout>
    <StackLayout Orientation="Horizontal">
      <Label Text="Green:"/>
      <Label Text="{Binding Green}" />
    </StackLayout>
    <StackLayout Orientation="Horizontal">
      <Label Text="Blue:"/>
      <Label Text="{Binding Blue}" />
    </StackLayout>
    <StackLayout Orientation="Vertical">
      <Button Text="set White" Command="{Binding WhiteCommand}"/>
      <Button Text="set Black" Command="{Binding BlackCommand}"/>
      <Button Text="set Yellow" Command="{Binding YellowCommand}"/>
    </StackLayout>
  </StackLayout>
</ContentPage>

SetProperty()とは

Prism版ではソースオブジェクトのセッター内で、SetProperty()メソッドというものが使われています(素のXamarin Forms実装では「PropertyChangedEventHandler」を扱う部分)。
SetProperty()メソッドは、Prismライブラリ内の「Prism.Mvvm.BindableBaseクラス(Prismアセンブリ)」で実装されています。
Prismはオープンソースとして以下のGithubでソース一式が公開されています。

github.com

その中で BindableBaseクラス の実装は以下です。

Prism/BindableBase.cs at master · PrismLibrary/Prism · GitHub

2017/1/8時点の実装ソースを抜粋させていただくと以下となります(コメント文は除去しています)。

using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;

namespace Prism.Mvvm
{
  /// <summary>
  /// Implementation of <see cref="INotifyPropertyChanged"/> to simplify models.
  /// </summary>
  public abstract class BindableBase : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual bool SetProperty<T>(
      ref T storage,
      T value,
      [CallerMemberName] string propertyName = null)
    {
      if (object.Equals(storage, value)) return false;

      storage = value;
      this.OnPropertyChanged(propertyName);

      return true;
    }

    protected virtual void OnPropertyChanged(
      [CallerMemberName]string propertyName = null)
    {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged<T>(
      Expression<Func<T>> propertyExpression)
    {
      var propertyName = PropertySupport.ExtractPropertyName(propertyExpression);
      this.OnPropertyChanged(propertyName);
    }
  }
}

SetProperty()メソッドの引数は3つ。
第1引数は ref でプロパティの値を保持する変数(フィールド変数)を受け取ります。
第2引数は 変更後の値 を受け取ります。
第3引数は 変更が発生したプロパティ名を文字列で受け取ります。[CallerMemberName]属性が付けられ、デフォルト値としてnullが指定されています。CallerMemberName属性はC# 5で導入された属性で「呼び出し元のプロパティ名・メソッド名」が割り当てられます。つまり、ソースオブジェクトのプロパティセッターからSetProperry()を呼び出す場合、第3引数は省略しても暗黙的にプロパティ名が指定されます。

では、続けて SetProperty() の内部実装に目を移します。

if (object.Equals(storage, value)) return false;

現在値と変更値の比較を行い、変更がなければそのままリターンします。

storage = value;

値の変更をフィールド変数に代入しています。

this.OnPropertyChanged(propertyName);

OnPropertyChanged()メソッドの呼び出しを行なっています。

OnPropertyChanged()メソッドの実装は以下の通りです。

PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

PropertyChangedは「event PropertyChangedEventHandler」として定義されたイベント変数です。
これは、素のXamarin Formsにおける実装と同様ですね。

まとめ

つまり・・・データバインディング周りの実装に関して、PrismのSetProperty()によって以下のような点がラップされ便利になっています。
* 値の変更チェックをラップしている
* PropertyChanged呼び出しをラップしている
* [CallerMemberName]によってプロパティ名を暗黙的に取得している

XAMLとコンパイルについて(Xamarin Forms)

あけましておめでとうございます。

今年も「自分の知識の整理」と「技術者としてのフェアの精神(ネットで教えてもらうだけでなく、自ら知り得た知識を共有する精神)」でブログを書いていこうと思います。

また、今年は勉強会とかにも積極的に顔を出してみようかな・・・とか思いつつ・・・

ということで、早速今回はXamarin Formsにおける小ネタを・・・

XAMLコンパイル

Xamarin Formsで開発を行う場合、UI定義はXAMLで行います。
UI定義以外のロジック(これはコードビハインドにベタ書きするものから、Prismなどのフレームワークを使ってOOに責務を分担したコードまで含めて)は C#言語 で記述します。
XAML と コードを記述し、ビルドメニューを選択するとコンパイル作業が行われ 各DLL が出力されます。このDLLはアセンブリと呼ばれる、.NETにおけるIL(Intermidiate Language)モジュールになります。この辺りは、.NETについてある程度精通している方ならご存知かと思います。C#実装は当然 IL に変換されます。
一方、XAMLはデフォルトでは「リソース」としてDLL内に埋め込まれます。そして実行時にXAML→ILへのコンパイルが行われます(補足として、実行する上では、最終的にはネイティブコードに変換)。

XamlCompilation属性

結論としては、「XamlCompilation属性」というものを用いるとXAMLをビルド時にコンパイルすることが可能になります。
以下、順を追って説明いたします。

コンパイル後のXAMLを確認する

ではまずは、デフォルトの「ビルド時にはXAMLコンパイルせず、実行時にXAMLコンパイルされる方式」について確認してみます。

①ソリューションの準備
以下のようなウィザードが生成した単純なXamarin Formsソリューションを用意します。

f:id:daigo-knowlbo:20170103115050p:plain

Xamarin FormsのPCLプロジェクトは「CompExample1」です。
CompExample1Page.xamlの定義は以下とします。Labelを2つ配置した単純なXAML定義です。

<?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:CompExample1"
    x:Class="CompExample1.CompExample1Page">
  <Label Text="Welcome to Xamarin Forms!" 
       VerticalOptions="Center" 
       HorizontalOptions="Center" />
  <Label Text="test" />
</ContentPage>

②ビルド
ビルドを行います。
CompExample1/bin/Release/CompExample1.dll が出力されます。
これを ILSpy で逆コンパイルしてみましょう。
結果は以下です。
Resources配下に 対象XAMLがそのまま埋め込まれていることが確認できます。

f:id:daigo-knowlbo:20170103115528p:plain

また、フォームのコードビハインドコード内の InitializeComponent() 中で LoadFromXaml() によりXAMLリソースをロードしています。

f:id:daigo-knowlbo:20170103115548p:plain

XAMLを開発時にコンパイルする(XamlCompilation属性)

次に XamlCompilation属性 を使用してみます。
ソリューションは先程の CompExample1 を使用します。

①XamlCompilation属性の付加
XamlCompilation属性は「アセンブリ(アプリケーション)単位」もしくは「フォーム単位」で指定することができます。

アセンブリ単位の指定には、以下のように名前空間に対して「[assembly: XamlCompilation(XamlCompilationOptions.Compile)]」属性を付加します。

// アセンブリ単位に指定(App.xaml.cs)
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace CompExample1
{
  public partial class App : Application
  {
    ...
  }
  ...
}

フォーム単位の指定には、以下のようにコードビハインドクラスに対して「[XamlCompilation(XamlCompilationOptions.Compile)]」属性を付加します。

// フォーム単位に指定(CompExample1Page.xaml)
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace CompExample1
{
  [XamlCompilation(XamlCompilationOptions.Compile)]
  public partial class CompExample1Page : ContentPage
  {
    public CompExample1Page()
    {
      InitializeComponent();
    }
  }
}

②ビルド
ビルドを行います。
先程と同様にCompExample1/bin/Release/CompExample1.dll を ILSpy で逆コンパイルしてみましょう。
結果は以下です。

f:id:daigo-knowlbo:20170103115607p:plain

Resourcesが無くなりました。
そして、コードビハインドクラスの InitializeComponent() 内にXAMLで定義したUIを構築するためのコードが追加されています。つまり、XAMLはコードに変換され、ILとなりDLLアセンブリに格納されました。

開発時コンパイルのメリット

XAMLを開発時にコンパイルすると以下のようなメリットがあります。

1) コンパイル時にXAML構文チェックが行われる

例えば以下のようにXAML構文上のエラーがあった場合、XamlCompilation属性を使用していないとビルド(コンパイル)は正常終了してしまいます。そして実行時にエラーとなります。
開発ビルド時に構文チェックが行われることで、開発生産性の向上が見込まれます。

... 省略
   <Label Content="test" />   ← WPFは Content でOKでしたが、Xamarin Formsでは Text ですね。
... 省略

2) 実行時のパフォーマンスが高くなる

XAMLコンパイルするタイミングが開発ビルド時になるので、実行時にフォームを表示する際のパフォーマンスが向上します。