ぴーさんログ

だいたいXamarin.Formsのブログ

Xamarin.Formsにコントリビュートしよう

先日、Xamarin.FormsのVisualSateManagerの解説を書いている途中で、バグを見つけて修正PRを投げました。 せっかくなのでどんな感じだったのか共有します。レッツ貢献!

この件。

概要

  • バグを見つける
  • ソースコードの問題個所を確認する
  • Reproductionを用意する
  • Issueを登録する
  • Pull Requestする
  • 自動テストがコケる
  • 取り込まれる

バグを見つける

Xamarin.Forms 3.0.0-pre2を使って、暗黙的に変化するVisualStateのサンプルコードを書いていたところVisualElement.IsFocusedが変化したときの挙動がおかしい事に気づきました。

ソースコードの問題個所を確認する

条件演算時が逆になってます。修正も簡単なのでバグ報告と一緒に修正PRも投げることにしました。

// 実際のコード
VisualStateManager.GoToState(element, isFocused
    ? VisualStateManager.CommonStates.Normal
    : VisualStateManager.CommonStates.Focused);

// 正しくはこう
//VisualStateManager.GoToState(element, isFocused
// ? VisualStateManager.CommonStates.Focused
// : VisualStateManager.CommonStates.Normal);

Reproductionを用意する

バグ報告にあたってReproduction、つまり最小の再現環境を用意します。 バグ報告者、Reproductionは義務です。え、Reproductionが無い?ZAPZAPZAP!次のバグ報告者はきっとうまくやってくれるでしょう。

もともとサンプルコードを書いていたところだったので、関係ないところを削ってGitHub置いておきます。

Issueを登録する

Xamarin.FormsのリポジトリにはIsuue登録のテンプレートがあります。

### Description

説明を書きます。

### Steps to Reproduce

再現手順を書きます。

1. 
2. 
3. 

### Expected Behavior

期待する動作を書きます。

### Actual Behavior

実際の動作(バグ挙動)を書きます。

### Basic Information

バグが発生する環境のバージョンなどを書きます。関係ないのは削ってもOK。

- Version with issue:
- Last known good version:
- IDE:
- Platform Target Frameworks: 
  - iOS:  
  - Android: 
  - UWP:  
- Android Support Library Version:
- Nuget Packages:
- Affected Devices:

### Screenshots

見た目に関する問題の場合はスクリーンショットがあると良い。

### Reproduction Link

最小の再現環境へのリンクを張ります。

実際に登録したIssueがコチラ

バグが報告されると中の人がTriageというGitHubのProjectsに乗せて行程管理するみたい。 「Ready For Work」にあるやつは修正PR受付中かな?

Pull Requestする

Pull Requestにもテンプレートがあります。

### Description of Change ###

変更内容の説明を書きます。

### Bugs Fixed ###

修正するIssueの番号を書きます。

### API Changes ###

Publicなクラスやメソッドが増えたり減ったりシグネチャが変わったりする場合は列挙します。何も無い場合はNoneで。

### Behavioral Changes ###

挙動が変わる場合は列挙します。何も無い場合はNoneで。

### PR Checklist ###

PRするときにやることリスト。

- [ ] Has tests (if omitted, state reason in description)
- [ ] Rebased on top of master at time of PR
- [ ] Changes adhere to coding standard
- [ ] Consolidate commits as makes sense

(「Checklistは自己申告なのか?」とか「あからさまにbool逆だっただけなのにテストいるのか?」と悩んだ結果、とりあえず未チェックのままPRしたら結局そのままマージされてしまった。)

実際のPull Requestがコチラ

masterリポジトリ充てにPRしたところ、リリース用ブランチ(3.0.0)宛てに変えてくれと言われる。分岐点までresetしてから3.0.0ブランチの先頭にrebaseし、GitHubの機能で宛先を3.0.0ブランチに変更。

自動テストがコケる

気が付いたら自動ビルドが走っていて(中の人が手動でキックするっぽい?)、数十分後に自動テストが走っていました。 次の日、「UITestxxxxが失敗した」とコメントがついてました。

取り込まれる

件のUITestを確認すると私の修正とは関係なく(そりゃそうだ)、typo修正が不完全でAutomatonId不一致で失敗しているだけでした。 その旨を返答すると数時間後にマージされました。(見返すと英文間違っててヤバイ)

反省

  • 一行程度の修正でも横着せずにブランチを切る。
    • rebaseが面倒になった。
  • CheckListは自分でチェックしていい。
  • 雰囲気英文は見直す。

おまけ

Xamarin.Forms CSS Support

Xamarin.Forms 3.0からCSS Supportが追加されます。(ASP .NET辺りの人たちにリーチしていく狙いらしい?)

一部制限はあるものの大体の機能がそのまま使えるようです、よってこの記事ではXamarin.Forms固有の事情を中心に説明します。 詳しく知りたい人は元のPull Requestを読んでください。

セレクタ関係

要素の指定

CSSにおけるHTML要素名の指定はC#クラス名の指定に置き換えられます。大文字小文字は無視されます。

/* CSS */
boxview {
    background-color: pink;
}

BoxViewクラスの背景色がピンクになります。

StyleClass

CSSにおけるclass属性にはStyleClassプロパティが対応します。(代わりにclassプロパティでも良いです)

/* CSS */
.centerelement {
    background-color: red;
}
<!-- XAML -->
<StackLayout>
    <BoxView />
    <BoxView StyleClass="centerelement" />
    <BoxView />
</StackLayout>

StyleId

CSSにおけるid属性にはStyleIdプロパティが対応します。

/* CSS */
#speciallabel {
    height: 100;
    width: 100;
    background-color: blue;
}
<!-- XAML -->
<Label Text="I'm special!" StyleId="speciallabel" />

Xamarin.Forms固有のセレクタ

要素名(C#クラス名)の先頭に^を付けるとそのクラス、およびその派生クラスへの指定となります。

/* CSS */
^contentpage {
    padding: 20;
    background-color: orange;
}

この場合、ContentPageクラスとその派生クラスへの指定となります。通常Xamarin.Formsの各画面はContentPageの派生クラスとして作るため、このようにすれば汎用的なスタイルしてができます。

スタイルシートの読み込み方

XAML

ResourcesプロパティにStyleSheet要素を追加すると、そのViewと子要素にスタイルシートが適用されます。

<ContentPage x:Class="CssSample.CssSamplePage">
    <ContentPage.Resources>
        <StyleSheet Source="/CSS/style.css" />
    </ContentPage.Resources>
</ContentPage>

ファイルからスタイルシートを読み込む場合、StyleSheet要素のSourceにCSSファイルのパスを記述します。このCSSファイルはEmbeddedResourceにする必要があります。 Sourceに指定するUriにはそのコントロールからの相対パス、または/で始まるプロジェクトルートからの絶対パスが使用できます。

f:id:ticktack623:20180319122000p:plain

この画像の場合では../CSS/style.cssまたは/CSS/style.cssが有効となります。

<ContentPage x:Class="CssSample.CssSamplePage">
    <ContentPage.Resources>
        <StyleSheet>
<![CDATA[
^contentpage {
    padding: 20;
    background-color: orange;
}

stacklayout > boxview {
    margin: 3;
    background-color: pink;
}
]]>
        </StyleSheet>
    </ContentPage.Resources>
</ContentPage>

CDATAセクションを使ってXAML内に記述することもできます。

C#

Xamarin.Forms.StyleSheets.StyleSheet.FromAssemblyResource()を使うとEmbeddedResourceからCSSファイルを読み込めます。

StyleSheet styleSheet = StyleSheet.FromAssemblyResource(this.GetType().GetTypeInfo().Assembly, "CssSample.CSS.style.css");
Resources.Add(styleSheet);

StyleSheet.FromReader()でTextReaderから読み込むことも可能。

using (var reader = new StringReader(cssString))
    myPage.Resources.Add(StyleSheet.FromReader(reader));

CSSとFlexLayout

以前に紹介したFlexLayoutを使うとCSSでもレイアウト指定ができます。

/* CSS */
.section-title {
    color: #778;
    font-style: bold;
    font-size: 20;
    margin: 6 0 0 6;
}
.grid {
    flex-wrap: wrap;
    flex-direction: row;
    align-items: stretch;
    height: 180;    
}
.grid-cell {
    flex-grow: 0;
    background-color: #eed;
    text-align: center;
    font-size: small;
    color: #556;
}
.full {
    flex-basis: 100%;
}
.half {
    flex-basis: 50%;
    background-color: cornflowerblue;
}
.third {
    flex-basis: 33.33%;
    background-color: pink;
}
.fourth {
    flex-basis: 25%;
    background-color: lightblue;
}
.auto {
    flex-grow: 1;
    background-color: coral;

}
<!-- 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="CssSample.FlexCssSamplePage">

    <ContentPage.Resources>
        <StyleSheet Source="../CSS/flexstyle.css" />
    </ContentPage.Resources>
    
    <ScrollView>
        <StackLayout>
            <Label Text="Basic Grids" StyleClass="section-title" />
            <FlexLayout StyleClass="grid">
                <Label StyleClass="grid-cell, full" Text="full" />
                <Label StyleClass="grid-cell, half" Text="1/2" />
                <Label StyleClass="grid-cell, half" Text="1/2" />
                <Label StyleClass="grid-cell, fourth" Text="1/4" />
                <Label StyleClass="grid-cell, half" Text="1/2" />
                <Label StyleClass="grid-cell, fourth" Text="1/4" />
                <Label StyleClass="grid-cell, third" Text="1/3" />
                <Label StyleClass="grid-cell, third" Text="1/3" />
                <Label StyleClass="grid-cell, third" Text="1/3" />
                <Label StyleClass="grid-cell, fourth" Text="1/4" />
                <Label StyleClass="grid-cell, fourth" Text="1/4" />
                <Label StyleClass="grid-cell, fourth" Text="1/4" />
                <Label StyleClass="grid-cell, fourth" Text="1/4" />
                <Label StyleClass="grid-cell, fourth" Text="1/4" />
                <Label StyleClass="grid-cell, third" Text="1/3" />
                <Label StyleClass="grid-cell, auto" Text="left-orver" />
            </FlexLayout>
        </StackLayout>
    </ScrollView>
</ContentPage>

上のスタイルシートとXAMLでこんなレイアウトが組めます。

f:id:ticktack623:20180319122730j:plain:w350

Xamarin.AndroidでFindViewByIdを省略するやつ

が追加されるらしいです。(Kotlin Android Extensionsでできるアレ)

f:id:ticktack623:20180311000306j:plain

(Miguelの資料より引用)

仕組みとしてはXamarin.Formsで行われている 「.xmalファイルが変更された際にmsbuildタスクが走り、x:Nameの付けられたView要素に対応するフィールドをコード生成する」 と同様なようです。

ファイルテンプレートも一緒に更新されると思いますが、xamlと違って必ずしもコードビハインドのファイルが1つに定まるわけではないので厄介そう🤔

Xamarin.Forms.FlexLayout

いつの間にかXamarin.FormsのmasterブランチにFlexLayoutが生えていたので試してみました。

ロードマップでは Q1 2018 Pre-Release - Q2 2018 Stable として予定されている機能です。もうすぐver.2.6.0-preとしてNuGetで普通にダウンロードできるようになると思います。

What is FlexLayout ?

端的に言うとCSSにおけるFlexboxのXamarin.Forms版がFlexLayoutです。

Xamarin.Forms標準ではこ要素を折り返して並べてくれるようなLayuoutが無いので、それだけの用途でも便利そうですね。

内部的には

  • xamarin/flexを移植した Xamarin.Flex 名前空間
  • Xamarin.Flex.Item を利用して子要素をレイアウトする FlexLayout 、レイアウト指定用のEnumやTypeConverter

といった構成になっています。

FlexLayoutのレイアウト指定

FlexLayoutがCSSに置けるFlexコンテナ、FlexLayoutの子要素がFlexアイテムに相当します。

Flexコンテナに設定するプロパティ

CSSでFlexコンテナに設定できるプロパティは、Xamarin.FormsではFlexLayouクラスのプロパティとして実装されています。

Flexbox(CSS) FlaxLayout(XF)
flex-direction Direction
flex-wrap Wrap
justify-content JustifyContent
align-items AlignItems
align-content AlignContent
対応なし Position

Position はCSSのFlexboxと異なる部分、RelativeAbsoluteを指定できます。

  • Relative:FlexLayoutのルールで配置される位置や大きさが決まる。
  • Absolute:FlexアイテムのLeft,Top,Right,Bottomによって位置と大きさが決まる。(ようなんですが、それらを設定する手段が無いように見える...)

Flexアイテムに設定するプロパティ

CSSでFlexアイテムに設定できるプロパティは、Xamarin.FormsではFlexLayouクラスのAttachedPropertyとして実装されています。

Flexbox(CSS) FlaxLayout(XF)
order Order
flex-grow Grow
flex-shrink Shrink
align-self AlignSelf
flex-basis Basis

参考

JXUGC #23 Xamarin 無料化一周年記念勉強会!で喋ってきました

2017/5/27(土)に開催されたJXUGC #23 Xamarin 無料化一周年記念勉強会!で喋ってきました。

Xamarin.Formsがさらにオープンになる変更がリリースノートでも特に言及される気配がないのでちょっと解説しました。

Xamarin.Forms 2.3.5(現時点ではプレビュー版)以降をターゲットにすれば、誰でも勝手に Xamarin.Forms.Platform.WinForms をリリースできるようになるステキな変更ですよ?

Xamarin Live Playerの仕組みを想像してた

今朝起きるとBuild 2017の2日目にXamarin Live Player(以降XLPと省略します)という技術が発表されていたので、今日はその仕組みについて想像していました。

5/12 午前

この時点では、どうやらXLPは「Mac不要でiOSアプリをビルドしてデバッグできる」技術らしいと把握していました。

「またまた皆騙されちゃってー、iOSアプリをビルドするならMacは必要、見えないところにいるだけでしょう」と想像した図がこちら。

断っておくとこの想像図は間違っています。(そもそも「iOSアプリをビルドする技術」という理解が間違っている)

一応、この想像図について解説すると

iOSアプリのビルド

現在でもmachinecloudのようなMacを貸し出すクラウドサービスやVisual Studio Mobile Centerを利用することで手元にMacマシンが無くてもiOSアプリをビルドすることが可能です。

iPhoneへのアプリの配信

MicrosoftはHokkeyAppというサービスを持っていて、その中にはテスト用アプリの配布機能も含まれます。iPhoneへのアプリ配布もできそう。

デバッグ

以前からwi-fi経由でのリモートデバッグは可能でした。

とまあ、既存技術の組み合わせでできそうな気がしました。間違ってるんですけどね。

5/12 午後

新しい情報を得て考えを改めます。

ここでXLPの制限事項の存在を認識します。

Xamarin Live Playerの制限事項

Xamarin公式のGuidesによると、主だった物として以下のような制限がある模様。

  • Android: AXMLファイル(Xamarin.AndroidにおけるAndroidリソースの拡張子)がサポートされません。
  • iOS: storyboadの一部の機能やXIBがサポートされません。
  • サブクラスの実装ができません(?)

なるほど、リソースやレイアウトファイルはビルド時にAndroid/XcodeのSDKによって処理されていたはずです。 これらがサポートされないということはXLPではアプリをビルドしないと考えるべきでしょう。

Continuous

ContinuousはiOS上で動作するC#、F#用のIDE(統合開発環境)です。 iPadやiPhoneで書いたコードがその場で実行できるわけです。

ところで、iOSアプリでは実行時コンパイルができません。そのためContinuousは読み込んだコード解析して、それを再現する処理を実行しているはずです。

つまり、こんなコードを書かれた場合

// ユーザーが各コード
UIlabel label = new UILabel();
label.Text = "hello world";

このようなコードに相当する処理を実行しているということです。

// Continuousが実行する処理
object variable = Activator.CreateInstance(Type.GetType("UILabel"));
Type.GetType("UILabel").GetProperty("Text").SetValue(variable, "hello word", null);

ユーザーがどのクラスを使用するか不明なため、主要なライブラリはアプリに同梱しておく必要があります。その証左としてContinuousのアプリサイズは201MBあります、かなり大きいですね。

さて、話をXamarin Live Playerに戻します。

制限事項より、Android/XcodeのSDKを使ってビルドされたアプリケーションパッケージが実行されていないことが読み取れました。ということは、Visual Studioで書かれたソースコードはContinuousのようにインタープリタ形式で実行されていると考えられます。

ならば、XLPのiOS版アプリは通常よりもはるかに多きいサイズであるはず。 (補足:通常のXamarin.iOSアプリでは参照されないクラスを削ってサイズを小さくしている)

実際 197MB ありました。うん、これはもう間違いなくContinuousと同じ仕組みですね。 (ちなみにAndroidはアプリと別にライブラリをインストールできるし、実行時にコンパイルできるのでもっと小さいです、30MBくらいだそうな)

まとめ

  • XLPはVisual Studio(for Mac)から転送されたソースコードをインタープリタ形式で実行している。
  • Android/Xcode SDKを使ってビルドしないので通常のXamarinアプリとはできることがかなり違う。
    • 動作する仕組みも違うので実機デバッグの代わりにはならない。
  • 系譜としてはむしろXamarin Workbookのような一種のplaygroundに近い気がする。
    • 身もふたもない言い方をすると強化されたGorilla Player。
  • デバッグ可能な点は新しい。
  • 通常のアプリ開発に必要な開発端末のセットアップ無しでXamarinのコードを(制限付きで)実行できるのはちょっとうれしい。
  • XLPでiOSアプリを作ることはできない。もちろん公開もできない。
  • Macを買わずにWindowsだけでiOSアプリをリリースできるなどという妄想は捨てる。

ちなみに、この記事を書いている時点でもまだXLP試せてません。何しろコレなもので。

Microsoft MVP for Visual Studio and Development Technologies を受賞しました

2017年3月づけ(?)でMicrosoft MVP Awardを受賞しました。

受賞カテゴリはVisual Studio and Development Technologiesです。

今年の2月からMicrosoft MVP Award Programが改定され、年4回だった審査&表彰が毎月になりました。私はその第二陣ということになります。

kogelog.com

どうやら受賞する人数も増えているようですし全人類がMS MVPになるもの時間の問題ですね。

以前は受賞からしばらくするとできると聞いていたMVP受賞者の個人ページもすでに用意されており、Microsoft MVP Award Programの高速に回していくんだという意思が感じられます。

「お前なら受かるやろ」と言ってくださった方々に後押しされての受賞とあいなりました。皆様ありがとうございます!