しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

Xamarin でコードビハインド、ViewModel を使わずに GestureRecognizer に反応させたい

Xamarin.Forms のコントロールは Tapped イベントすら持ってないので、Trigger でインタラクションを記述することがまだ難しいです;

なので Tapped のジェスチャに反応する機能を拡張する GestureRecognizer を利用してコントロールを作ってみたいと思います

普通に GestureRecognizer を利用する場合、コードビハインドでイベントハンドラを追加したり、ViewModel の Command をデータバインドするみたいです

ただ、コントロールとして使いまわす場合 XAML だけで完結するような記述ができると非常にラクなので、できればそうしたいところ・・・

今回の修正は下記の網掛け部分

f:id:matatabi_ux:20150118205731p:plain

まずは GestureRecognizerBehavior というビヘイビアを作ります

/// <summary>
/// GestureRecognizer に反応する Behavior
/// </summary>
public class GestureRecognizeBehavior : Behavior<View>
{
    /// <summary>
    /// アタッチされた View
    /// </summary>
    private View bindable = null;

    /// <summary>
    /// アタッチされた GestureRecognizer の BindableProperty
    /// </summary>
    public static readonly BindableProperty GestureRecognizerProperty =
        BindableProperty.Create<GestureRecognizeBehavior, TapGestureRecognizer>(p => p.GestureRecognizer, null);

    /// <summary>
    /// アタッチされた GestureRecognizer
    /// </summary>
    public TapGestureRecognizer GestureRecognizer
    {
        get { return (TapGestureRecognizer)this.GetValue(GestureRecognizerProperty); }
        set { this.SetValue(GestureRecognizerProperty, value); }
    }

    /// <summary>
    /// Behavior がアタッチされた時の処理
    /// </summary>
    /// <param name="bindable">アタッチされた View</param>
    protected override void OnAttachedTo(View bindable)
    {
        base.OnAttachedTo(bindable);
        this.bindable = bindable;
        if (this.bindable == null || this.GestureRecognizer == null)
        {
            return;
        }
        this.GestureRecognizer.Tapped += this.OnGestured;
        this.bindable.GestureRecognizers.Add(this.GestureRecognizer);
    }

    /// <summary>
    /// GestureRecognizer のイベントハンドラ
    /// </summary>
    /// <param name="sender">イベント発行者</param>
    /// <param name="args">イベント引数</param>
    private void OnGestured(object sender, EventArgs args)
    {
        // 背景色を反転
        this.bindable.BackgroundColor = Color.FromRgba(
            1.0d - this.bindable.BackgroundColor.R,
            1.0d - this.bindable.BackgroundColor.G,
            1.0d - this.bindable.BackgroundColor.B,
            this.bindable.BackgroundColor.A);
    }

    /// <summary>
    /// Behavior がデタッチされた時の処理
    /// </summary>
    /// <param name="bindable"></param>
    protected override void OnDetachingFrom(View bindable)
    {
        if (this.bindable != null && this.GestureRecognizer != null)
        {
            this.bindable.GestureRecognizers.Remove(this.GestureRecognizer);
            this.GestureRecognizer.Tapped -= this.OnGestured;
            this.GestureRecognizer = null;
            this.bindable = null;
        }
        base.OnDetachingFrom(bindable);
    }
}

View がアタッチされたら BindableProperty に設定されている GestureRecognizer を View に追加してイベントを監視するようにしています

処理の内容は Tapped ジェスチャに反応して BackgroundColor を反転しているだけ

汎用的に作ろうと思ったらなぜか Interface にも 基底クラスの GestureRecognizer にも Command やイベントの定義がなく使えませんでした; どちらにせよ GestureRecognizer が TapGestureRecognizer しかないので、これに限定した実装にしてしまいました・・・今後整備されそうだけども

お次に 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:b="clr-namespace:XamarinControl.Behaviors;assembly=XamarinControl"
             x:Class="XamarinControl.Views.TopPage">
  <BoxView 
    BackgroundColor="#FFFF8469"
    WidthRequest="200"
    HeightRequest="200"
    HorizontalOptions="Center"
    VerticalOptions="Center">
    <BoxView.Behaviors>
      <b:GestureRecognizeBehavior>
        <b:GestureRecognizeBehavior.GestureRecognizer>
          <TapGestureRecognizer NumberOfTapsRequired="1" />
        </b:GestureRecognizeBehavior.GestureRecognizer>
      </b:GestureRecognizeBehavior>
    </BoxView.Behaviors>
  </BoxView>
</ContentPage>

Behavior と GestureRecognizer を記述・・・コードビハインドも ViewModel も使ってませんね

とりあえず実行

f:id:matatabi_ux:20150118210833p:plain

タップしたら色が交互に切り替わるようになりました!