shuhelohelo’s blog

Xamarin.Forms多めです.

Xamarin.Formsでカスタムコントロールを作る

www.matatabi-ux.com

www.mfractor.com

複数のコントロールを組み合わせて一つのコントロールとしてまとめたい,とかいった標準コントロールにない見た目や動作をするコントロールを作りたい場合に,カスタムコントロールの出番です.

きっと色々と複雑なことができて,その場合は実装も色々複雑なことになると思うのですが,この記事ではカスタムコントロールを作成する基本的な手順だけを紹介したいと思います.

というかそのぐらいしか紹介できないのですが,シンプルなものなら難しくないよ,ということを言いたいのです.

この記事で紹介すること

  • 雛形の作り方
  • カスタムコントロールの使い方
  • プロパティの公開方法
  • バインド可能なプロパティ(BindableProperty)にする

環境

雛形を作る

Xamarin.Formsのブランクアプリを作ってから,まずは共有コード側に新しい項目としてContentViewを追加します.

f:id:shuhelohelo:20191028204817p:plain

この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が表示されます.

f:id:shuhelohelo:20191029091738p:plain

プロパティの公開方法

やってみるとわかると思いますが,このままではMyControlは入力を受け付けるコントロールなのに,肝心のTextPlaceholderといったプロパティを持っていません.

この時点でMyControlが持っているプロパティはBackgroundColorなどの共通のプロパティのみです.

そこで,MyControlにプロパティを追加します.

プロパティの追加自体は簡単です.MyControlのコードビハインドにpublicなクラスを追加します.

    public partial class MyControl : ContentView
    {
        public string Text { get; set; } //Textプロパティを追加

        public MyControl()
        {
            InitializeComponent();
        }
    }

これでMyControlタグでインテリセンスでTextプロパティが表示されるようになります.

f:id:shuhelohelo:20191029114754p:plain

しかし,このままでは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プロパティの変更が機能しているかを確認するためには,もう一つ設定が必要です.

カスタムコントロールのデザイン時の描画を有効にする必要があります.

shuhelohelo.hatenablog.com

ソリューションをビルドすると,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プロパティ)に入れる処理を書くことで,バインド可能なプロパティの完成です.