ぴーさんログ

だいたいXamarin.Formsのブログ

【Xamarin.Forms 2.2】Embedded Platform-Specific Controls in Xamarin.Forms

Xamarin.Forms 2.2から Layout.ChildrenContentView.Content に各プラットフォームのネイティブコントロールを埋め込めるようになり、Evolve 2016のキーノートでも紹介されました。 このフィーチャーを使うとCustomRendererよりもカジュアルにネイティブコントロールを利用できます。

キーノートのブログ

Live from Evolve: Faster and Easier Mobile App Development with Xamarin.Forms | Xamarin Blog

ドキュメント

Native Embedding - Xamarin

条件

ネイティブコントロールの埋め込みを行うにはSharedプロジェクト内のコードで、プラットフォーム毎に#ifディレクティブで記述します。PCLプロジェクトでは使用できません。

サンプルコード

次のようなコードをSharedプロジェクトに追加すると、ネイティブコントロールを埋め込む事ができます。

using System;
using Xamarin.Forms;

#if __IOS__
using Xamarin.Forms.Platform.iOS;
using UIKit;
#elif __ANDROID__
using Xamarin.Forms.Platform.Android;
using Android.Widget;
#endif

namespace XFApp25
{
    public class MyPage : ContentPage
    {
        public MyPage ()
        {
            var text = "Embedded Platform-Specific Control in Xamarin.Forms";

            var stackLayout = new StackLayout {
                VerticalOptions = LayoutOptions.Center,
                HorizontalOptions = LayoutOptions.Center,
            };
#if __IOS__
            var uiLabel = new UILabel {
                MinimumFontSize = 14f,
                Lines = 0,
                LineBreakMode = UILineBreakMode.WordWrap,
                Text = text + "(iOS)",
            };
            // StackLayoutにUILabelが追加できる!
            stackLayout.Children.Add (uiLabel);
            // これもOK
            //Content = uiLabel.ToView ();
#elif __ANDROID__
            var textView = new TextView(Forms.Context) {
                Text = text + "(Android)",
                TextSize = 14,
            };
            stackLayout.Children.Add (textView);
            // これもOK
            //Content = textView.ToView ();
#endif
            Content = stackLayout;
        }
    }
}

仕組み

LayoutExtensionsクラスに定義された2つの拡張メソッドが使用します。

  • ToView - ネイティブコントロールをラップしてXamarin.FormsのViewに変換する拡張メソッド
  • Add - IList<View>、つまりLayout.Childrenに対する拡張メソッド(内部でToViewを呼ぶ)

これらの拡張メソッドを使うためには各名前空間の参照が必要です。

  • iOS – Xamarin.Forms.Platform.iOS
  • Android – Xamarin.Forms.Platform.Android
  • Windows Runtime – Xamarin.Forms.Platform.WinRT
  • Universal Windows Platform (UWP) – Xamarin.Forms.Platform.UWP

ToView拡張メソッドがネイティブコントロールを NaitiveViewWrapper に変換し、 NaitiveViewWrapperRenderer が汎用的なViewRendererとして働く、という具合ですね。 込み入ったコントロールのために、サイズ決定時に呼ばれるデリゲートなどを渡すことも出来ます。

【Xamarin.Forms 2.3プレビュー】Xamarin.Forms Themesを試そうとしたらまだ使えなかったので代わりにMerged Dictionaryの解説する

この記事はThemesの話のようでありながら、その実Xamarin.Forms版Merged Dictionaryの話です。

Xamarin.Forms Themes はEvolve 2016のキーノートで紹介された新機能の1つ、その目標は「デフォルトで美しいUIを提供すること」です。

Live from Evolve: Faster and Easier Mobile App Development with Xamarin.Forms | Xamarin Blog

すでにドキュメントも完備されてます。

Xamarin.Forms Themes - Xamarin

デフォルトとして提供されるLight/Darkテーマを使うにはNuGetからこれらをダウンロードします。

  • Xamarin.Forms.Theme.Base
  • Xamarin.Forms.Theme.Light / Dark

使い方はこんな感じ

<?xml version="1.0" encoding="utf-8"?>
<Application xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="EvolveApp.App"
             xmlns:light="clr-namespace:Xamarin.Forms.Themes;assembly=Xamarin.Forms.Theme.Light">
    <Application.Resources>
        <ResourceDictionary MergedWith="light:LightThemeResources" />
    </Application.Resources>
</Application>

早速試そうとしたところ肝心のNuGetパッケージがまだ配信されていなかったので、代わりのThemesの前提となっている ResourceDictionary.MergedWith について解説します。

Xamarin.Forms版Merged Dictionary

ResourceDictionary.MergedWith はXamarin.Forms 2.3から追加されるプロパティです。他のResourceDictionaryクラスを指定することでリソースを連結します。おおよそWPFなどのMergedDictionariesに相当する機能ですね。

WPFなどWindowsXAMLプラットフォームとの比較

  • Windows
    • ResourceDictionary.MergedDictionaries はコレクション型で複数のResourceDictionaryを結合できる。
    • .xamlファイルを直接ResourceDictionaryとして指定できる。
  • Xamarin.Forms
    • ResourceDictionary.MergedWith に指定できるのは1個だけ。(プロパティの型はType)
    • .xamlファイルは指定できない、あくまでコンパイル可能なResourceDictionary派生型である必要がある。

Windows系は分割・集約、Xamarin.Formsは継承・カスタマイズに向いていると言えるでしょう。

さて、Themesの使い方と照らし合わせるとお気づきでしょうか?

そう、詰まるところXamarin.Forms Themesの正体はよくカスタムされたResourceDictionaryなのであります。 (StyleClass実装も含んでいるはずですが、その辺はThemesが使えるようになった際に改めて見ていきましょう)

Xamarin.Forms 2.3からはResourceDictionaryクラスのseald指定が外れるので自分でもThemeが作れます。

例えばこんな感じ

MyTheme.xaml

<?xml version="1.0" encoding="UTF-8"?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    x:Class="XFApp24.MyTheme">
    <Style TargetType="Label">
        <Setter Property="TextColor" Value="Red"/>
    </Style>
</ResourceDictionary>

MyTheme.xaml.cs

using Xamarin.Forms;

namespace XFApp24
{
    public partial class MyTheme : ResourceDictionary
    {
        public MyTheme ()
        {
            InitializeComponent ();
        }
    }
}

ContentPageなどのテンプレートから改造するとXAMLでカスタムResourceDictionaryが書けます。(要InitializeComponent)

これを使ってみると

f:id:ticktack623:20160430004453j:plain

XAMLプレビューにも反映されました。

早くNuGetにThemes配信されないかなー。

【Evolve 2016】Xamarin.Forms Previewerを試してみた!

Xamarin Evolve 2016のキーノートで遂に来ました!

念願のXAMLプレビューが!Xamarin.Formsに!

呼称は Xamarin.Forms Previewer だそうです。

というわけで早速試してみた様子がこちら

後でXamarin Studioを再起動したらAndroidもプレビュー出来るようになりました。(でもImage表示できてない気がする....まぁまだAplha版だし)

必要な環境はXamarin StudioのAlphaチャネル最新版とXamarin.Forms 2.3プレビュー版、そしてその前提環境としてXcodeを最新にしておく必要があるみたい。

もうちょっと詳しく

※Xamarin.Forms Previewerはまだ正式リリースされた機能ではないので、不安定だったりバギーだったりするかも知れません。その点はご留意ください。(2016/4/29現在)

Xamarin.Forms Previewerの公式情報をXamarinのブログで確認してみましょう。

Live from Evolve: Faster and Easier Mobile App Development with Xamarin.Forms

You can get the Xamarin.Forms Previewer today from the alpha updater channel in Xamarin Studio for Mac. To use the Xamarin.Forms previwer, your project will need to reference Xamarin.Forms 2.3 prerelease NuGet.

ふんふん、必要なのは AlphaチャンネルMac版Xamarin StudioとXamarin.Forms 2.3のプレビュー版ですね。Visual StudioやUWPはおいおいサポートされるでしょう。

Xamarin StudioをAlphaチャンネルへ

メニューから「Xamarin Studio > Check for Updates」を開いてAlphaチャンネルに切り替えます。

f:id:ticktack623:20160429130930j:plain (画像はすでにAlphaチャンネルのものです)

更新データをダウンロード出来たらXamarin Studioを再起動。

Xcodeが古い場合はこちらも最新版に更新しておきます。更新後に一度Xcodeを起動して関連ツールの更新も忘れずに。

Xamarin.Fromsのプロジェクトを作成

Xamarin.Fromsの新規プロジェクト(PCL)を作成します。(Sharedプロジェクトで作ると.xmalファイルを編集出来なくなって詰みました)

f:id:ticktack623:20160429131245j:plain

f:id:ticktack623:20160429131254j:plain

ここで一度ビルドしてHello Worldが動くことを確認。(iOSシミュレータを再起動する必要があるかも)

各プロジェクトにXamarin.Forms 2.3プレビューを追加

各プロジェクトの「パッケージ」をダブルクリックしてNuGetパッケージマネージャを開きます。 「Show pre-release packages」をチェックして、「xamarin.forms」と検索すると、プレビュー版のXamarin.Formsが見つかるのでこれをAdd Packageします。(PCLプロジェクトでのNuGetパッケージの追加が失敗することがありました。その場合はXamarin.Forms一旦削除してから入れ直します。)

f:id:ticktack623:20160429131344j:plain

ここでも一度ビルドしてHello Worldが動くことを確認。

XAML Previewerを試す

PCLプロジェクトに「Forms ContentPage Xaml」を追加。

f:id:ticktack623:20160429131400j:plain

XAMLファイルの変更がプレビューに反映されない時は、プロジェクトをビルドしたりxamlファイルを開き直すと解消される模様です。

f:id:ticktack623:20160428175311j:plain

Xamarin.Forms向けのMap機能拡張ライブラリを作ってます

https://github.com/P3PPP/MapExtensions

コンセプトはXamarin.Forms 2.1.0で追加されたEffectsを利用して、Mapコントロールを継承することなく機能を追加するというもの。 つまり、従来のカスタムMapにも適用可能。

NuGetにもすでに上がってます。

https://nuget.org/packages/Xam.MapExtensions.Forms.Plugin/

現状は地図のクリックイベントをハンドルする機能が使えます。(iOSAndroid、UWPに対応)

var map = new Xamarin.Forms.Maps.Map();
var clickBehavior = new MapExtensions.Forms.Plugin.ClickBehavior();
map.Behaviors.Add(clickBehavior);

var page = new ContentPage
{
    Content = map,
};

clickBehavior.MapClicked += (s, e) =>
    page.DisplayAlert("MapClickd", $"{e.Position.Latitude}, {e.Position.Longitude}", "OK");

clickBehavior.MapLongClicked += (s, e) =>
    page.DisplayAlert("MapLongClicked", $"{e.Position.Latitude}, {e.Position.Longitude}", "OK");

clickBehavior.MapClidkedCommand = new Command<MapExtensions.Forms.Plugin.MapClickedEventArgs>(e =>
    page.DisplayAlert("MapClickdCommand", $"{e.Position.Latitude}, {e.Position.Longitude}", "OK"));

clickBehavior.MapLongClidkedCommand = new Command<MapExtensions.Forms.Plugin.MapClickedEventArgs>(e =>
    page.DisplayAlert("MapLongClickedCommand", $"{e.Position.Latitude}, {e.Position.Longitude}", "OK"));

使い方:

  • Xamarin.Forms.Maps.MapMapExtensions.Forms.Plugin.ClickBehavior を追加。
  • ClickBehavior 経由でクリックイベントをハンドルします。
  • MapClickedEventArgs にクリックした地図上の座標が入っているのでよしなに(ピンを立てたり)。

次は座標のコレクションを渡したらPolylineをオーバーレイしてくれるような機能を追加する予定。

(まだ機能とかちゃんと煮詰めてないのにうっかりver. 1.0でリリースしちゃったよ...)

【Xamarin.Forms 2.2.0(プレビュー)】UWP Maps

Xamarin.Forms 2.2.0からUWPでもXamarin.Forms.Mapsがサポートされます。 (っていうか今まで無かったんだ...)

ざっくりとした使い方

他のプラットフォームと同様、 Xamarin.Forms.Forms.Init() の直後に Xamarin.FormsMaps.Init() を呼ぶ必要があります。 UWPの場合はパラメータとしてBing Maps Developer Centerで発行するAPIキーを渡す必要があるようです。

まず、Xamarin.FormsのプロジェクトテンプレートにXamarin.Forms.Maps 2.2.0のNuGetパッケージを追加します。

次に、UWPプロジェクトのApp.Xaml.csを修正。

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
  ~ 省略 ~
        Xamarin.Forms.Forms.Init(e);
        // Bing Maps Developer Centerで発行したAPIキーでXamarin.Forms.Mapsを初期化
        Xamarin.FormsMaps.Init("YOUR-API-KEY");
  ~ 省略 ~
}

さらに、共通コードプロジェクトのAppクラスを修正。

public App()
{
    MainPage = new ContentPage {
        Content = new Xamarin.Forms.Maps.Map(),
    };
}

実行結果

f:id:ticktack623:20160417225557j:plain

(全ての道はローマに通ず)

リンク

Request a maps authentication key - Windows app development

【Xamarin.Forms 2.2.0(プレビュー)】CarouselView

Xamarin.Forms 2.2.0 から CarouselView クラスが追加されます。

CarouselView は従来の CarouselPage を置き換える物で、CarouselPageは将来的に非推奨となります。

サンプル

基本的な使い方は ListView に似ています。しかし、DataTemplateの中身を CellではなくView にする必要がある点に注意が必要です。

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="XFApp20.MyPage">
    <ContentPage.Content>
        <CarouselView HorizontalOptions="Center"
                      VerticalOptions="Center"
                      HeightRequest="150"
                      WidthRequest="150"
                      ItemsSource="{Binding}">
            <!-- DataTemplateの中身はView派生クラス -->
            <CarouselView.ItemTemplate>
                <DataTemplate>
                    <StackLayout>
                        <BoxView HorizontalOptions="FillAndExpand"
                                 VerticalOptions="FillAndExpand"
                                 Color="{Binding Color}" />
                        <Label HorizontalOptions="Center"
                               Text="{Binding Text}" />
                    </StackLayout>
                </DataTemplate>
            </CarouselView.ItemTemplate>
        </CarouselView>
    </ContentPage.Content>
</ContentPage>

コードビハインド

public MyPage()
{
    InitializeComponent();

    BindingContext = new ViewModel[] {
        new ViewModel {
            Color = Color.Red,
            Text = "Red",
        },
        new ViewModel {
            Color = Color.Blue,
            Text = "Blue",
        },
        new ViewModel {
            Color = Color.Green,
            Text = "Green",
        },
        new ViewModel {
            Color = Color.Purple,
            Text = "Purple",
        },
        new ViewModel {
            Color = Color.Silver,
            Text = "Silver",
        },
        new ViewModel {
            Color = Color.Pink,
            Text = "Pink",
        },
    };
}
public class ViewModel
{
    public Color Color { get; set; }
    public string Text { get; set; }
}

ItemTemplateのセッティングをC#で書くとこのようになります。

carouselView.ItemTemplate = new DataTemplate(typeof(CustomView));

// または

carouselView.ItemTemplate = new DataTemplate(() => {
    var boxView = new BoxView(){
        VerticalOptions = LayoutOptions.FillAndExpand,
        HorizontalOptions = LayoutOptions.FillAndExpand,
    };
    boxView.SetBinding(BoxView.ColorProperty, "Color");
    var label = new Label{
        HorizontalOptions = LayoutOptions.Center,
    };
    label.SetBinding(Label.TextProperty, "Text");
    return new StackLayout {
        Children = {
            boxView,
            label,
        },
    };
});

実行結果

f:id:ticktack623:20160413091209g:plain:w250

【Xamarin.Forms】BindableProperty.Create() non-generic版のコードスニペット

以前、BindablePropertyを楽に作るコードスニペットを公開しました。

その中で使っていたBindableProperty.Create()のgeneric版がobsolete化したので、新しくnon-generic版のスニペットを公開します。

詳細な登録手順はgeneric版の記事を合わせてご参照ください。

Xamarin Studio用

Preferences > テキストエディタ > コード テンプレート から追加します。

Visual Studio

「BindableProperty.snippet」の名前で保存。 「C:\Users\ ユーザー名 \Documents\ VisualStudioのバージョン \Code Snippets\Visual C#\My Code Snippets」にファイルをコピー。

ツール > コードスニペットマネージャー から追加します。