Xamarin 表单自定义 GridView Tap 和 Long Tap 不能一起工作
Posted
技术标签:
【中文标题】Xamarin 表单自定义 GridView Tap 和 Long Tap 不能一起工作【英文标题】:Xamarin Forms Custom GridView Tap and Long Tap Not Working Together 【发布时间】:2019-06-17 10:50:15 【问题描述】:我想将点击和长按与自定义网格视图一起使用。
点按有效,但长按无效。
一键关闭长按也可以。
请帮帮我。
谢谢。
public class GridView : Grid
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IList), typeof(GridView), default(IList), BindingMode.TwoWay);
public static readonly BindableProperty ItemTappedCommandProperty = BindableProperty.Create(nameof(ItemTappedCommand), typeof(ICommand), typeof(GridView), null);
public static readonly BindableProperty ItemLongTappedCommandProperty = BindableProperty.Create(nameof(ItemLongTappedCommand), typeof(ICommand), typeof(GridView), null);
public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(nameof(ItemTemplate), typeof(DataTemplate), typeof(GridView), default(DataTemplate));
public static readonly BindableProperty MaxColumnsProperty = BindableProperty.Create(nameof(MaxColumns), typeof(int), typeof(GridView), 2);
public static readonly BindableProperty TileHeightProperty = BindableProperty.Create(nameof(TileHeight), typeof(float), typeof(GridView), 220f);//adjusted here reuired height
public GridView()
PropertyChanged += GridView_PropertyChanged;
PropertyChanging += GridView_PropertyChanging;
public IList ItemsSource
get return (IList)GetValue(ItemsSourceProperty);
set SetValue(ItemsSourceProperty, value);
public ICommand ItemTappedCommand
get return (ICommand)GetValue(ItemTappedCommandProperty);
set SetValue(ItemTappedCommandProperty, value);
public ICommand ItemLongTappedCommand
get return (ICommand)GetValue(ItemLongTappedCommandProperty);
set SetValue(ItemLongTappedCommandProperty, value);
public DataTemplate ItemTemplate
get return (DataTemplate)GetValue(ItemTemplateProperty);
set SetValue(ItemTemplateProperty, value);
public int MaxColumns
get return (int)GetValue(MaxColumnsProperty);
set SetValue(MaxColumnsProperty, value);
public float TileHeight
get return (float)GetValue(TileHeightProperty);
set SetValue(TileHeightProperty, value);
private void BuildColumns()
ColumnDefinitions.Clear();
for (var i = 0; i < MaxColumns; i++)
ColumnDefinitions.Add(new ColumnDefinition Width = new GridLength(1, GridUnitType.Star) );
private View BuildTile(object item1)
var template = ItemTemplate.CreateContent() as View;
template.BindingContext = item1;
if (ItemTappedCommand != null)
var tapGestureRecognizer = new TapGestureRecognizer
Command = ItemTappedCommand,
CommandParameter = item1,
;
template.GestureRecognizers.Add(tapGestureRecognizer);
// Tap komutu eziyor.
if (ItemLongTappedCommand != null)
template.Effects.Add(new LongPressedEffect());
LongPressedEffect.SetCommand(template, ItemLongTappedCommand);
//LongPressedEffect.SetCommandParameter(template, item1);
return template;
private void BuildTiles()
// Wipe out the previous row & Column definitions if they're there.
if (RowDefinitions.Any())
RowDefinitions.Clear();
BuildColumns();
Children.Clear();
var tiles = ItemsSource;
if (tiles != null)
var numberOfRows = Math.Ceiling(tiles.Count / (float)MaxColumns);
for (var i = 0; i < numberOfRows; i++)
RowDefinitions.Add(new RowDefinition Height = new GridLength(0, GridUnitType.Auto) );
for (var index = 0; index < tiles.Count; index++)
var column = index % MaxColumns;
var row = (int)Math.Floor(index / (float)MaxColumns);
var tile = BuildTile(tiles[index]);
Children.Add(tile, column, row);
private void GridView_PropertyChanged(object sender, PropertyChangedEventArgs e)
if (e.PropertyName == ItemsSourceProperty.PropertyName)
var items = ItemsSource as INotifyCollectionChanged;
if (items != null)
items.CollectionChanged += ItemsCollectionChanged;
BuildTiles();
if (e.PropertyName == MaxColumnsProperty.PropertyName || e.PropertyName == TileHeightProperty.PropertyName)
BuildTiles();
private void GridView_PropertyChanging(object sender, Xamarin.Forms.PropertyChangingEventArgs e)
if (e.PropertyName == ItemsSourceProperty.PropertyName)
var items = ItemsSource as INotifyCollectionChanged;
if (items != null)
items.CollectionChanged -= ItemsCollectionChanged;
private void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
BuildTiles();
public class LongPressedEffect : RoutingEffect
public LongPressedEffect() : base("MyApp.LongPressedEffect")
public static readonly BindableProperty CommandProperty = BindableProperty.CreateAttached("Command", typeof(ICommand), typeof(LongPressedEffect), (object)null);
public static ICommand GetCommand(BindableObject view)
//do something you want
Console.WriteLine("long press Gesture recognizer has been striked");
return (ICommand)view.GetValue(CommandProperty);
public static void SetCommand(BindableObject view, ICommand value)
view.SetValue(CommandProperty, value);
public static readonly BindableProperty CommandParameterProperty = BindableProperty.CreateAttached("CommandParameter", typeof(object), typeof(LongPressedEffect), (object)null);
public static object GetCommandParameter(BindableObject view)
return view.GetValue(CommandParameterProperty);
public static void SetCommandParameter(BindableObject view, object value)
view.SetValue(CommandParameterProperty, value);
【问题讨论】:
【参考方案1】:我注意到您使用Effect
创建自己的长按命令。但是如果你同时消费了一个TapGestureRecognizer
,它会拦截效果。那么你长按的命令就不会被触发了。
您可以在效果中定义单击事件来实现它们。这是我的效果:
public class PressedEffect : RoutingEffect
public PressedEffect() : base("MyApp.PressedEffect")
public static readonly BindableProperty LongTapCommandProperty = BindableProperty.CreateAttached("LongTapCommand", typeof(ICommand), typeof(PressedEffect), (object)null);
public static ICommand GetLongTapCommand(BindableObject view)
return (ICommand)view.GetValue(LongTapCommandProperty);
public static void SetLongTapCommand(BindableObject view, ICommand value)
view.SetValue(LongTapCommandProperty, value);
public static readonly BindableProperty LongParameterProperty = BindableProperty.CreateAttached("LongParameter", typeof(object), typeof(PressedEffect), (object)null);
public static object GetLongParameter(BindableObject view)
return view.GetValue(LongParameterProperty);
public static void SetLongParameter(BindableObject view, object value)
view.SetValue(LongParameterProperty, value);
public static readonly BindableProperty TapCommandProperty = BindableProperty.CreateAttached("TapCommand", typeof(ICommand), typeof(PressedEffect), (object)null);
public static ICommand GetTapCommand(BindableObject view)
return (ICommand)view.GetValue(TapCommandProperty);
public static void SetTapCommand(BindableObject view, ICommand value)
view.SetValue(TapCommandProperty, value);
public static readonly BindableProperty TapParameterProperty = BindableProperty.CreateAttached("TapParameter", typeof(object), typeof(PressedEffect), (object)null);
public static object GetTapParameter(BindableObject view)
return view.GetValue(TapParameterProperty);
public static void SetTapParameter(BindableObject view, object value)
view.SetValue(TapParameterProperty, value);
Android 实现:
[assembly: ResolutionGroupName("MyApp")]
[assembly: ExportEffect(typeof(androidPressedEffect), "PressedEffect")]
namespace PressedEffectDemo.Droid
public class AndroidPressedEffect : PlatformEffect
private bool _attached;
public static void Initialize()
public AndroidPressedEffect()
protected override void OnAttached()
if (!_attached)
if (Control != null)
Control.LongClickable = true;
Control.LongClick += Control_LongClick;
Control.Click += Control_Click;
else
Container.LongClickable = true;
Container.LongClick += Control_LongClick;
Container.Click += Control_Click;
_attached = true;
private void Control_Click(object sender, EventArgs e)
var command = PressedEffect.GetTapCommand(Element);
command?.Execute(PressedEffect.GetTapParameter(Element));
private void Control_LongClick(object sender, Android.Views.View.LongClickEventArgs e)
var command = PressedEffect.GetLongTapCommand(Element);
command?.Execute(PressedEffect.GetLongParameter(Element));
protected override void OnDetached()
if (_attached)
if (Control != null)
Control.LongClickable = true;
Control.LongClick -= Control_LongClick;
Control.Click -= Control_Click;
else
Container.LongClickable = true;
Container.LongClick -= Control_LongClick;
Control.Click -= Control_Click;
_attached = false;
iOS 实现:
[assembly: ResolutionGroupName("MyApp")]
[assembly: ExportEffect(typeof(iosPressedEffect), "PressedEffect")]
namespace PressedEffectDemo.iOS
public class iOSPressedEffect : PlatformEffect
private bool _attached;
private readonly UILongPressGestureRecognizer _longPressRecognizer;
private readonly UITapGestureRecognizer _tapRecognizer;
public iOSPressedEffect()
_longPressRecognizer = new UILongPressGestureRecognizer(HandleLongClick);
_tapRecognizer = new UITapGestureRecognizer(HandleClick);
protected override void OnAttached()
if (!_attached)
Container.AddGestureRecognizer(_longPressRecognizer);
Container.AddGestureRecognizer(_tapRecognizer);
_attached = true;
private void HandleClick()
var command = PressedEffect.GetTapCommand(Element);
command?.Execute(PressedEffect.GetTapParameter(Element));
private void HandleLongClick(UILongPressGestureRecognizer recognizer)
if (recognizer.State == UIGestureRecognizerState.Ended)
var command = PressedEffect.GetLongTapCommand(Element);
command?.Execute(PressedEffect.GetLongParameter(Element));
protected override void OnDetached()
if (_attached)
Container.RemoveGestureRecognizer(_longPressRecognizer);
Container.RemoveGestureRecognizer(_tapRecognizer);
_attached = false;
最后,您可以像这样在 XAML 上使用它们:
<StackLayout>
<Grid HeightRequest="200"
BackgroundColor="Green"
local:PressedEffect.TapCommand="Binding TapCommand"
local:PressedEffect.LongTapCommand="Binding LongTapCommand">
<Grid.Effects>
<local:PressedEffect />
</Grid.Effects>
</Grid>
</StackLayout>
您可以参考我的示例here。
【讨论】:
以上是关于Xamarin 表单自定义 GridView Tap 和 Long Tap 不能一起工作的主要内容,如果未能解决你的问题,请参考以下文章
创建自定义导航栏渲染器以在 xamarin 表单 IOS 项目中添加自定义后退按钮图标