Xamarin.Formsでカスタムコントロールを作る
複数のコントロールを組み合わせて一つのコントロールとしてまとめたい,とかいった標準コントロールにない見た目や動作をするコントロールを作りたい場合に,カスタムコントロールの出番です.
きっと色々と複雑なことができて,その場合は実装も色々複雑なことになると思うのですが,この記事ではカスタムコントロールを作成する基本的な手順だけを紹介したいと思います.
というかそのぐらいしか紹介できないのですが,シンプルなものなら難しくないよ,ということを言いたいのです.
この記事で紹介すること
- 雛形の作り方
- カスタムコントロールの使い方
- プロパティの公開方法
- バインド可能なプロパティ(BindableProperty)にする
環境
- Visual Studio 2019 16.4 preview2
- Xamarin.Forms 4.3
雛形を作る
Xamarin.Formsのブランクアプリを作ってから,まずは共有コード側に新しい項目としてContentView
を追加します.
このContentView内で様々なコントロールを組み合わせて,お好みのコントロールを作ります.
ContentViewでは通常のPageを作るときと同様に,LabelやButtonを配置し,デザイナで見た目を確認する事もできます.
ここでは例としてImageとEntryを重ねて表示し,なんとなく背景画像つきの入力欄のようなものを作ってみました.
<?xml version="1.0" encoding="UTF-8"?> <ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:d="http://xamarin.com/schemas/2014/forms/design" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="CustomControlPractice.MyControl"> <ContentView.Content> <Grid> <Image Source="icon.png"/> <Entry Placeholder="てすと"/> </Grid> </ContentView.Content> </ContentView>
カスタムコントロールの使い方
ではこのカスタムコントロールをアプリケーションの中で使ってみます.
使い方はシンプルで,作成したカスタムコントロールをタグとして記述するだけです.
まずMainPage.xamlを開きます.
ここでカスタムコントロールを使えるようにするために,名前空間を追加する必要があります.
MainPageのContentPageタグにいくつも名前空間の指定が並んでいると思います.
そこに以下のように,先程作成したカスタムコントロールの名前空間を追加します.
xmlns:local="clr-namespace:CustomControlPractice"
このlocal
の部分は自分の好きな名前にしてください.この名前を使って名前空間内のオブジェクトにアクセスできます.
カスタムコントロールを呼び出すには,標準コントロールと同じようにタグを書くだけです.先程のコントロールはMyControl
というクラス名をつけたので,タグ名はMyControl
です.
<local:MyControl/>
これでMainPageにMyControlが表示されます.
プロパティの公開方法
やってみるとわかると思いますが,このままではMyControlは入力を受け付けるコントロールなのに,肝心のText
やPlaceholder
といったプロパティを持っていません.
この時点でMyControlが持っているプロパティはBackgroundColor
などの共通のプロパティのみです.
そこで,MyControlにプロパティを追加します.
プロパティの追加自体は簡単です.MyControlのコードビハインドにpublicなクラスを追加します.
public partial class MyControl : ContentView { public string Text { get; set; } //Textプロパティを追加 public MyControl() { InitializeComponent(); } }
これでMyControlタグでインテリセンスでTextプロパティが表示されるようになります.
しかし,このままではTextプロパティに値を設定したとしてもMyControlには何も表示されません.MyControlのTextプロパティの値が,内部のEntryのTextプロパティに結びついていないからです.
MyControlのTextプロパティに値が設定されたときに,内部のEntryのTextプロパティに同じ値を設定するようにします.
これは難しくありません.TextプロパティのSetterを使って,内部のEntry(entryという名前をつけておきました)のTextプロパティに値をセットするだけです.
private string _text; public string Text { get => _text; set { _text = value; this.entry.Text = _text; } }
MainPageのデザイナ上でTextプロパティの変更が機能しているかを確認するためには,もう一つ設定が必要です.
カスタムコントロールのデザイン時の描画を有効にする必要があります.
ソリューションをビルドすると,MyControlのTextプロパティの変更がデザイナにも反映されます.
バインド可能なプロパティ(BindableProperty)にする
カスタムコントロールにプロパティを追加しましたが,データバインディングには対応していません.
プロパティをバインド可能にするためには,以下のようにします.
public string Text { get; set; } //プロパティ //プロパティをバインド可能にする記述 public static readonly BindableProperty TextProperty = BindableProperty.Create( propertyName: nameof(Text), //バインド可能にしたいプロパティ名を指定 returnType: typeof(string), //プロパティの型を指定 declaringType: typeof(MyControl), //プロパティが所属するクラスを指定 defaultValue: "", defaultBindingMode: BindingMode.TwoWay, //デフォルトのバインディングの種類 propertyChanged: TitlePropertyChanged //プロパティの値が変更されたときに実行されるメソッドを指定 ); //プロパティの値が変更されたときに実行される private static void TitlePropertyChanged(BindableObject bindable, object oldValue, object newValue) { var control = (MyControl)bindable; control.entry.Text = newValue.ToString(); }
これはもうワンセットで覚えたほうがいい気がします.
カスタムコントロールに追加したいプロパティをpublicなプロパティとして定義します.
BindableProperty型のstaticなフィールドを定義し,そこにBindablePropertyのインスタンスを入れています.ここでバインド可能にしたいプロパティを指定し,値が変更されたときに実行されるstaticメソッドを指定します.
プロパティの値が変更されたときに実行されるstaticメソッドを定義します.ここでMyControlのプロパティの値を内部のコントロールのプロパティ(ここではEntryのTextプロパティ)に入れる処理を書くことで,バインド可能なプロパティの完成です.