在 ListView 中点击 Gesture 手势识别器不工作
Tap Gesture Gesture Recognizer in ListView not working
我的 ViewModel 中有这个:
public class MyClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int taps = 0;
ICommand tapCommand;
public MyClass()
{
tapCommand = new Command(OnTapped);
}
public ICommand TapCommand
{
get { return tapCommand; }
}
void OnTapped(object s)
{
taps++;
Debug.WriteLine("parameter: " + s);
}
}
xaml 中的这个:
<Image Source="delete.jpg" HeightRequest="20" WidthRequest="20">
<Image.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TapCommand}"
CommandParameter="Image1" />
</Image.GestureRecognizers>
</Image>
但是当点击图像时,输出日志中没有任何内容。我缺少什么?
Note 1: I've followed the guide here
Note 2: I'm debugging on Android Device
Update 1: full xaml here
在 xaml 文件的后端,确保将 BindingContext 设置为视图模型:类似这样的内容:
xaml.cs
public partial class TapInsideFrameXaml : ContentPage
{
public TapInsideFrameXaml ()
{
InitializeComponent ();
// The TapViewModel contains the TapCommand which is wired up in Xaml
BindingContext = new TapViewModel ();
}
}
同时检查:
在菜单 > 工具 > 选项 > 调试 > 常规:
- 确保未选中 "Redirect all output window text to the immediate window"
在项目属性 > 构建中:
- 配置:调试
- "Define DEBUG constant"勾选
- "Define TRACE constant"勾选
关于输出window:
- 显示来自:Debug
的输出
- 右键单击输出 window 并确保选中 "Program output"
还要确保您使用的是 F5 而不是 Ctrl f5 进行调试
您声明了 PropertyChangedEvent
事件,但您从未真正使用过它。试试这个:
public class MyClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int taps = 0;
ICommand tapCommand;
public MyClass()
{
tapCommand = new Command(OnTapped);
}
public ICommand TapCommand
{
get { return tapCommand; }
}
void OnTapped(object s)
{
taps++;
Debug.WriteLine("parameter: " + s);
OnPropertyChanged();
}
protected virtual void OnPropertyChanged ([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler (this, new PropertyChangedEventArgs (propertyName));
}
}
你没有提供任何背后的代码,所以我不得不创建我自己的(见下文)。
如果注释掉 ListView IsEnabled="False"
,你也会节省我 15 分钟
当您设置 Command="{Binding TapCommand}
时,TapCommand 对应于列表项的 TapCommand,而不是模型本身。所以,你有两个选择
- 您可以在 MyItem 中实现点击(我不认为您想要那样,所以它的部分代码在 MyItem class 的下方注释)
- 定义图像本身的上下文绑定。
因为我们的视图模型可以被创建几次——我们不希望这样,所以最好的解决方案是定义视图模型的静态资源并在页面的任何地方使用它。
解决方案如下(您只需要修改一些命名空间并将 delte.png 更改为您的 delete.jpg):
第xml页
<?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:local="clr-namespace:ButtonRendererDemo;assembly=ButtonRendererDemo"
x:Class="ButtonRendererDemo.ImageTapComplexPage"
BindingContext="{StaticResource viewModel}">
<ContentPage.Resources>
<ResourceDictionary>
<local:TapComplexViewModel x:Key="viewModel"/>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<Label Text="text2" VerticalOptions="Center" HorizontalOptions="Center" />
<StackLayout Padding="0,20,0,20">
<Button Text="text" Clicked="onclick" BackgroundColor="#009688"></Button>
</StackLayout>
<Label Text="List Type" VerticalOptions="Center" HorizontalOptions="Center" />
<ListView x:Name="lstItems" RowHeight="60" ItemsSource="{Binding Items}" > <!--IsEnabled="False"-->
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" HorizontalOptions="Fill" Padding="0,2,0,2">
<StackLayout Padding="5,5,5,5">
<Image Source="{Binding source}" HorizontalOptions="End" HeightRequest="40" WidthRequest="40" />
</StackLayout>
<StackLayout Orientation="Vertical" Spacing="1">
<Label Text = "{Binding name}" HeightRequest="20" FontAttributes="Bold"/>
<Label Text = "{Binding data, StringFormat='{0:F0}'}" />
</StackLayout>
<StackLayout HorizontalOptions="EndAndExpand" Padding="0,0,15,0" Orientation="Horizontal">
<Image Source="delete.png" HeightRequest="20" WidthRequest="20">
<Image.GestureRecognizers>
<!--<TapGestureRecognizer
Command="{Binding TapCommand}"
CommandParameter="Image1" />-->
<TapGestureRecognizer Command="{Binding Source={StaticResource viewModel}, Path=TapCommand}" CommandParameter="{Binding name}" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
代码隐藏
namespace ButtonRendererDemo
{
public partial class ImageTapComplexPage : ContentPage
{
public ImageTapComplexPage()
{
InitializeComponent();
}
void onclick(object sender, EventArgs args)
{
}
}
public class TapComplexViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int taps = 0;
ICommand tapCommand;
public ObservableCollection<MyItem> Items { get; private set; }
public TapComplexViewModel()
{
Items = new ObservableCollection<MyItem>()
{
new MyItem { name="First", source="Icon.png", data=0.5f },
new MyItem { name="Second", source="Icon.png", data=0.6f },
new MyItem { name="Third", source="Icon.png", data=0.7f }
};
tapCommand = new Command(OnTapped);
}
public ICommand TapCommand
{
get { return tapCommand; }
}
void OnTapped(object s)
{
taps++;
Debug.WriteLine("parameter: " + s);
}
}
public class MyItem
{
public string name { get; set; }
public string source { get; set; }
public float data { get; set; }
//public ICommand TapCommand
//{
// get { return new Command(() => { }); }
//}
}
}
我的 ViewModel 中有这个:
public class MyClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int taps = 0;
ICommand tapCommand;
public MyClass()
{
tapCommand = new Command(OnTapped);
}
public ICommand TapCommand
{
get { return tapCommand; }
}
void OnTapped(object s)
{
taps++;
Debug.WriteLine("parameter: " + s);
}
}
xaml 中的这个:
<Image Source="delete.jpg" HeightRequest="20" WidthRequest="20">
<Image.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TapCommand}"
CommandParameter="Image1" />
</Image.GestureRecognizers>
</Image>
但是当点击图像时,输出日志中没有任何内容。我缺少什么?
Note 1: I've followed the guide here
Note 2: I'm debugging on Android Device
Update 1: full xaml here
在 xaml 文件的后端,确保将 BindingContext 设置为视图模型:类似这样的内容:
xaml.cs
public partial class TapInsideFrameXaml : ContentPage
{
public TapInsideFrameXaml ()
{
InitializeComponent ();
// The TapViewModel contains the TapCommand which is wired up in Xaml
BindingContext = new TapViewModel ();
}
}
同时检查:
在菜单 > 工具 > 选项 > 调试 > 常规:
- 确保未选中 "Redirect all output window text to the immediate window"
在项目属性 > 构建中:
- 配置:调试
- "Define DEBUG constant"勾选
- "Define TRACE constant"勾选
关于输出window:
- 显示来自:Debug 的输出
- 右键单击输出 window 并确保选中 "Program output"
还要确保您使用的是 F5 而不是 Ctrl f5 进行调试
您声明了 PropertyChangedEvent
事件,但您从未真正使用过它。试试这个:
public class MyClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int taps = 0;
ICommand tapCommand;
public MyClass()
{
tapCommand = new Command(OnTapped);
}
public ICommand TapCommand
{
get { return tapCommand; }
}
void OnTapped(object s)
{
taps++;
Debug.WriteLine("parameter: " + s);
OnPropertyChanged();
}
protected virtual void OnPropertyChanged ([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler (this, new PropertyChangedEventArgs (propertyName));
}
}
你没有提供任何背后的代码,所以我不得不创建我自己的(见下文)。 如果注释掉 ListView IsEnabled="False"
,你也会节省我 15 分钟当您设置 Command="{Binding TapCommand}
时,TapCommand 对应于列表项的 TapCommand,而不是模型本身。所以,你有两个选择
- 您可以在 MyItem 中实现点击(我不认为您想要那样,所以它的部分代码在 MyItem class 的下方注释)
- 定义图像本身的上下文绑定。 因为我们的视图模型可以被创建几次——我们不希望这样,所以最好的解决方案是定义视图模型的静态资源并在页面的任何地方使用它。 解决方案如下(您只需要修改一些命名空间并将 delte.png 更改为您的 delete.jpg):
第xml页
<?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:local="clr-namespace:ButtonRendererDemo;assembly=ButtonRendererDemo"
x:Class="ButtonRendererDemo.ImageTapComplexPage"
BindingContext="{StaticResource viewModel}">
<ContentPage.Resources>
<ResourceDictionary>
<local:TapComplexViewModel x:Key="viewModel"/>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<Label Text="text2" VerticalOptions="Center" HorizontalOptions="Center" />
<StackLayout Padding="0,20,0,20">
<Button Text="text" Clicked="onclick" BackgroundColor="#009688"></Button>
</StackLayout>
<Label Text="List Type" VerticalOptions="Center" HorizontalOptions="Center" />
<ListView x:Name="lstItems" RowHeight="60" ItemsSource="{Binding Items}" > <!--IsEnabled="False"-->
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" HorizontalOptions="Fill" Padding="0,2,0,2">
<StackLayout Padding="5,5,5,5">
<Image Source="{Binding source}" HorizontalOptions="End" HeightRequest="40" WidthRequest="40" />
</StackLayout>
<StackLayout Orientation="Vertical" Spacing="1">
<Label Text = "{Binding name}" HeightRequest="20" FontAttributes="Bold"/>
<Label Text = "{Binding data, StringFormat='{0:F0}'}" />
</StackLayout>
<StackLayout HorizontalOptions="EndAndExpand" Padding="0,0,15,0" Orientation="Horizontal">
<Image Source="delete.png" HeightRequest="20" WidthRequest="20">
<Image.GestureRecognizers>
<!--<TapGestureRecognizer
Command="{Binding TapCommand}"
CommandParameter="Image1" />-->
<TapGestureRecognizer Command="{Binding Source={StaticResource viewModel}, Path=TapCommand}" CommandParameter="{Binding name}" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
代码隐藏
namespace ButtonRendererDemo
{
public partial class ImageTapComplexPage : ContentPage
{
public ImageTapComplexPage()
{
InitializeComponent();
}
void onclick(object sender, EventArgs args)
{
}
}
public class TapComplexViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int taps = 0;
ICommand tapCommand;
public ObservableCollection<MyItem> Items { get; private set; }
public TapComplexViewModel()
{
Items = new ObservableCollection<MyItem>()
{
new MyItem { name="First", source="Icon.png", data=0.5f },
new MyItem { name="Second", source="Icon.png", data=0.6f },
new MyItem { name="Third", source="Icon.png", data=0.7f }
};
tapCommand = new Command(OnTapped);
}
public ICommand TapCommand
{
get { return tapCommand; }
}
void OnTapped(object s)
{
taps++;
Debug.WriteLine("parameter: " + s);
}
}
public class MyItem
{
public string name { get; set; }
public string source { get; set; }
public float data { get; set; }
//public ICommand TapCommand
//{
// get { return new Command(() => { }); }
//}
}
}