質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.49%

Xamarin.FormのGridにC#コード側からタップイベントを追加するのは可能か

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,004

score 6

Xamarin.FormおよびC#初心者です。
初歩的なところで詰まっているのかもしれませんが、お力をお貸しください。

前提・実現したいこと

商品一覧画面から任意の商品行をタップ/ロングタップで別画面に遷移、というアプリを作っています。

最終的に実現したいことは、商品行をタップ/ロングタップした場合にそれぞれ異なるイベントを紐づけること、です。
以下ページを参考にジェスチャの設定をしようとしています。
https://docs.microsoft.com/ja-jp/xamarin/xamarin-forms/app-fundamentals/gestures/tap
https://www.buildinsider.net/mobile/xamarintips/0035

現在、XAMLでタップイベントを追加することは出来ているのですが(以下xamlソースのコメントアウト箇所がそれに当たります)
ロングタップの方はカスタムレンダラーを使ってC#で実装するしかなさそうだったので、
それであればタップもC#側で実装できないかなと思い試してみたところ、うまくいきません。

何か自分が見落としているところなどあれば、教えていただけないでしょうか。
よろしくお願いいたします。
(Gridに対してC#側で紐づけが難しければ、タップはXamlで実装、ロングタップは実装を断念するつもりです。)

該当のソースコード

以下ソースで実際にデバッグしたとき、一覧の任意の商品をタップしても無反応の状態。

                var grid = new Grid();
                var tapGestureRecognizer = new TapGestureRecognizer();
                tapGestureRecognizer.Tapped += (s, e) => {
                    ListBodyList_ItemTapped(s,e);
                };
                grid.GestureRecognizers.Add(tapGestureRecognizer);
                <ScrollView x:Name="MyScrollView"
                            Style="{StaticResource MyScrollView}">
                    <!--一覧-->
                    <StackLayout x:Name="ListBodyList"
                             BindableLayout.ItemsSource="{Binding _BodyList}"
                             Orientation="Vertical">
                        <BindableLayout.ItemTemplate>
                            <DataTemplate>
                                <Grid RowSpacing="0" 
                                      BackgroundColor="{Binding BodyStatusColor}"
                                      HeightRequest="80"
                                      Margin="0"
                                      Padding="0">
                                    <!--<Grid.GestureRecognizers>
                                        <TapGestureRecognizer Tapped="ListBodyList_ItemTapped"></TapGestureRecognizer>
                                    </Grid.GestureRecognizers>-->
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*" />
                                        <ColumnDefinition Width="130" />
                                    </Grid.ColumnDefinitions>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="25" />
                                        <RowDefinition Height="*" />
                                    </Grid.RowDefinitions>
                                    <Label Grid.Row="0" Grid.Column="0"
                                    Text="{Binding ShoCd, StringFormat=' {0:00000} '}"
                                    Style="{StaticResource lblShoCd}" />
                                    ...(一部省略)...
                                    <Label Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
                                    Text="{Binding ShoName}"
                                    Style="{StaticResource lblShoNm}" 
                                    LineBreakMode="CharacterWrap" />
                                </Grid>
                            </DataTemplate>
                        </BindableLayout.ItemTemplate>
                    </StackLayout>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

StackLayoutChildrenプロパティで子のView(Grid)を取得できるので、そのViewに対して、TapGestureRecognizerを追加すればいいです。(BindingContextの設定は、この処理の前に行なっておいてください)

foreach (var view in ListBodyList.Children)
{
    var tapGestureRecognizer = new TapGestureRecognizer();
    tapGestureRecognizer.Tapped += (s, e) => {
        ListBodyList_ItemTapped(s,e);
    };
    view.GestureRecognizers.Add(tapGestureRecognizer);
}

また、ロングタップを実装するのであれば、XamarinFormsGestureなどのライブラリがあるので、利用を検討してみてもいいかもしれません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/09/23 13:40

    f-miyuさん
    ご回答いただきありがとうございます!
    ご提示いただいた内容で無事にタップイベントをC#側から実装することが叶いました。
    試行錯誤中にStackLayoutにタップを仕込むことは出来ていたのですが、その子に対して設定するという発想に至れませんでした。
    とてもシンプルで明快な回答です!
    本質問のベストアンサーにさせていただきます。本当にありがとうございました。

    ※おすすめいただいたXamarinFormsGesture、拝見しました。
     世の名にはこんな便利なライブラリもあったのですね。
     諸事情で今回外部ライブラリをnuget導入できないのが惜しいです・・もう少し頑張ってみます!

    キャンセル

+1

DataTemplateの中身箇所をContentViewとして切り出せば、XAMLで名前を付けていたコントロールをコードビハインドから参照可能のようです。
動作確認のために適当なPersonクラスを別途作って当てはめてますので、その辺りは適宜読み替えてください。
下記に記していないものは生成時から変更はないです。

MainPage.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="BlankApp2.Views.MainPage"
             xmlns:views="clr-namespace:BlankApp2.Views"
             Title="{Binding Title}">

    <ScrollView>
        <StackLayout BindableLayout.ItemsSource="{Binding Persons}" Orientation="Vertical">
            <BindableLayout.ItemTemplate>
                <DataTemplate>
                    <views:DataTemplateArea />
                </DataTemplate>
            </BindableLayout.ItemTemplate>
        </StackLayout>
    </ScrollView>

</ContentPage>

DataTemplateArea.xaml

<?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:prism="http://prismlibrary.com"
             prism:ViewModelLocator.AutowireViewModel="True"
             x:Class="BlankApp2.Views.DataTemplateArea">

    <Grid x:Name="Hoge">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="130" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="25" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Label Grid.Row="0" Grid.Column="0" Text="{Binding Name1}" />
        <Label Grid.Row="0" Grid.Column="1" Text="{Binding Price1}" />
        <Label Grid.Row="1" Grid.Column="0" Text="{Binding Name2}" />
        <Label Grid.Row="1" Grid.Column="1" Text="{Binding Price2}" />
    </Grid>

</ContentView>

DataTemplateArea.xaml.cs

using System;
using System.Diagnostics;
using Xamarin.Forms;

namespace BlankApp2.Views
{
    public partial class DataTemplateArea : ContentView
    {
        public DataTemplateArea()
        {
            InitializeComponent();

            var tapGestureRecognizer = new TapGestureRecognizer();
            tapGestureRecognizer.Tapped += (s, e) => 
            {
                Debug.WriteLine("Tapped!!!");
            };
            this.Hoge.GestureRecognizers.Add(tapGestureRecognizer);
        }
    }
}

App.xaml.cs

using Prism;
using Prism.Ioc;
using BlankApp2.ViewModels;
using BlankApp2.Views;
using Xamarin.Essentials.Interfaces;
using Xamarin.Essentials.Implementation;
using Xamarin.Forms;

namespace BlankApp2
{
    public partial class App
    {
        public App(IPlatformInitializer initializer)
            : base(initializer)
        {
        }

        protected override async void OnInitialized()
        {
            InitializeComponent();

            await NavigationService.NavigateAsync("NavigationPage/MainPage");
        }

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterSingleton<IAppInfo, AppInfoImplementation>();

            containerRegistry.RegisterForNavigation<NavigationPage>();
            containerRegistry.RegisterForNavigation<MainPage, MainPageViewModel>();
            //containerRegistry.RegisterForNavigation<DataTemplateArea, DataTemplateAreaViewModel>();
            // ↑Prismを使用していると勝手に追加されるので削除しましょう
        }
    }
}

下記ページがContentView作成で参考になると思います。
Xamarin.Forms で Prism と ReactiveProperty で MVVM な自作コントロールを作りたい(1)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/09/23 13:29

    takapi_csさん
    ご回答いただきありがとうございます!
    単純に名前を付けただけではコードビハインドからはうまく呼ぶことが出来ずこの手は諦めておりましたので(本当は呼べて、何かが足りてなかっただけなのかもしれませんが)今回のtakapi_csさんの「タップを仕込みたい部分を外に出す」発想には膝を打ちました。
    ご丁寧にサンプルのソースもありがとうございます!
    とても理解の助けになりました。
    (恥ずかしながら提示頂いた内容でPrismなるフレームワークの存在も初めて知りました…)

    裏から操作したいものを外出しする、というやり方は他の場合でも応用がききそうなので、
    この機会に覚えておきたいと思います。本当にありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る