问题描述
关于这个问题,我进行了很多搜索,坦率地说,我对此非常满意。我有一个聊天应用程序。在此应用上,有一个视图,其中有来自我和其他聊天成员的消息。从技术上讲,它是一个具有ItemTemplate的ListView,具有一个Binded(DataTemplateSelector)类,该类根据规则返回ViewCells(显示的消息是mine还是OTHERS)
消息(入站或出站)位于单独的文件中。
当前,TapGestureRecognizer无法正常运行,并且命令ChooseNametoMentionCommand无法启动
在很多类似的问题中,TapGestureRecognizer不能像这样在ListView上工作:
TapGestureRecognizer not working inside ListView
那里(以及任何其他相关主题)对于Command不起作用的答案是:
- 在命令绑定上使用
Source={x:Reference MessagesListView}
但是当我使用这个建议时,我的结尾是:
Xamarin.Forms.Xaml.XamlParseException: 'Position 30:21. Can not find the object referenced by MessagesListView'
我该如何在我的情况下使用它(在单独的文件中定义ViewCell)重要说明-我正在使用MVVM方法,并且不想在ViewCell的代码背后做任何事情(那么我什至可以使用Tapped事件。我ve进行了测试。这种方法当然有效:))
这是我的代码:
Mainviewmodel.cs
public class Mainviewmodel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableRangeCollection<MessageModel> Messages { get; set; }
public Command ChooseNametoMentionCommand { get; set; }
public string NewMessage {get; set;}
public Mainviewmodel()
{
Messages = new ObservableRangeCollection<MessageModel>();
ChooseNametoMentionCommand = new Command<string>(async (t) => await ChooseNametoMention(t));
}
private Task ChooseNametoMention(string name)
{
this.NewMessage += $"@{name}";
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this,new PropertyChangedEventArgs(propertyName));
}
}
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="Chat.ClientLibrary.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converters="clr-namespace:Chat.ClientLibrary.Converters"
xmlns:local="clr-namespace:Chat.ClientLibrary.CustomCells"
xmlns:partials="clr-namespace:Chat.ClientLibrary.Views.Partials"
BackgroundColor="White"
x:Name="MainChatPage">
<ContentPage.Resources>
<ResourceDictionary>
<local:MyDataTemplateSelector x:Key="MessageTemplateSelector"/>
</ResourceDictionary>
</ContentPage.Resources>
/* REMOVED UNNECESSARY code */
<Grid RowSpacing="0" ColumnSpacing="0">
<Grid.RowDeFinitions>
<RowDeFinition Height="50" />
<RowDeFinition Height="*" />
<RowDeFinition Height="1" />
<RowDeFinition Height="Auto" />
</Grid.RowDeFinitions>
<ListView
Grid.Row="1"
FlowDirection="RightToLeft"
Rotation="180"
x:Name="MessagesListView"
ItemTemplate="{StaticResource MessageTemplateSelector}"
ItemsSource="{Binding Messages}"
HasUnevenRows="True"
ItemSelected="MyListView_OnItemSelected"
ItemTapped="MyListView_OnItemTapped"
SeparatorVisibility="None" />
/* REMOVED UNNECESSARY code */
</Grid>
</ContentPage>
MyDataTemplateSelector.cs
class MyDataTemplateSelector : DataTemplateSelector
{
public MyDataTemplateSelector()
{
// Retain instances!
this.incomingDataTemplate = new DataTemplate(typeof(IncomingViewCell));
this.outgoingDataTemplate = new DataTemplate(typeof(OutgoingViewCell));
}
protected override DataTemplate OnSelectTemplate(object item,BindableObject container)
{
var messageVm = item as MessageModel;
if (messageVm == null)
return null;
return messageVm.IsOwnMessage ? this.incomingDataTemplate : this.outgoingDataTemplate;
}
private readonly DataTemplate incomingDataTemplate;
private readonly DataTemplate outgoingDataTemplate;
}
IncomingViewCell.xaml
(我不会发布OutgoingViewCell-几乎一样;)不同的颜色)
<?xml version="1.0" encoding="utf-8" ?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Chat.ClientLibrary.Views.CustomCells.IncomingViewCell"
xmlns:forms9patch="clr-namespace:Forms9Patch;assembly=Forms9Patch">
<Grid ColumnSpacing="2"
Padding="5"
FlowDirection="LeftToRight"
Rotation="180"
>
<Grid.ColumnDeFinitions>
<ColumnDeFinition Width="40"></ColumnDeFinition>
<ColumnDeFinition Width="*"></ColumnDeFinition>
<ColumnDeFinition Width="40"></ColumnDeFinition>
</Grid.ColumnDeFinitions>
<Grid.RowDeFinitions>
<RowDeFinition Height="Auto"></RowDeFinition>
<RowDeFinition Height="*"></RowDeFinition>
<RowDeFinition Height="Auto"></RowDeFinition>
</Grid.RowDeFinitions>
<Label Grid.Row="0" Grid.Column="1" HorizontalTextAlignment="Start" Text="{Binding UserName}" TextColor="Blue">
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Path= BindingContext.ChooseNametoMentionCommand,Source={x:Reference MessagesListView}}" CommandParameter="{Binding UserName}" />
</Label.GestureRecognizers>
</Label>
/* REMOVED UNNECESSARY code */
</Grid>
</ViewCell>
[EDIT 12:12 01.10.2020] 我忘了把MainPage.cs放在这里,所以在这里:
MainPage.cs
public partial class MainPage : ContentPage
{
Mainviewmodel vm;
public MainPage()
{
this.BindingContext = vm = new Mainviewmodel();
InitializeComponent();
}
void MyListView_OnItemSelected(object sender,selecteditemchangedEventArgs e)
{
MessagesListView.SelectedItem = null;
}
void MyListView_OnItemTapped(object sender,ItemTappedEventArgs e)
{
MessagesListView.SelectedItem = null;
}
}
添加了ListOnItemTapped事件(我忘了它-因为它是从某个聊天示例中获取的。但是我不认为它会破坏任何内容。当我直接为Label添加OnTapped时-它确实起作用了
解决方法
由于缺少代码,我制作了一个类似的示例供您参考,以在ListView ViewCell中使用TapGestureRecognizer
。
Xaml:
<ContentPage
x:Class="Selector.HomePage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Selector"
x:Name="MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="validPersonTemplate">
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*" />
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label
FontAttributes="Bold"
Text="{Binding Name}"
TextColor="Green">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.TapCommand,Source={x:Reference MainPage}}" CommandParameter="false" />
</Label.GestureRecognizers>
</Label>
<Label
Grid.Column="1"
Text="{Binding DateOfBirth,StringFormat='{0:d}'}"
TextColor="Green" />
<Label
Grid.Column="2"
HorizontalTextAlignment="End"
Text="{Binding Location}"
TextColor="Green" />
</Grid>
</ViewCell>
</DataTemplate>
<DataTemplate x:Key="invalidPersonTemplate">
<ViewCell>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*" />
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label
FontAttributes="Bold"
Text="{Binding Name}"
TextColor="Red">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.TapCommand,StringFormat='{0:d}'}"
TextColor="Red" />
<Label
Grid.Column="2"
HorizontalTextAlignment="End"
Text="{Binding Location}"
TextColor="Red" />
</Grid>
</ViewCell>
</DataTemplate>
<local:PersonDataTemplateSelector
x:Key="personDataTemplateSelector"
InvalidTemplate="{StaticResource invalidPersonTemplate}"
ValidTemplate="{StaticResource validPersonTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Margin="20">
<Label
FontAttributes="Bold"
HorizontalOptions="Center"
Text="ListView with a DataTemplateSelector" />
<ListView
x:Name="listView"
Margin="0,20,0"
ItemTemplate="{StaticResource personDataTemplateSelector}" />
</StackLayout>
</ContentPage>
PersonDataTemplateSelector.cs:
public class PersonDataTemplateSelector : DataTemplateSelector
{
public DataTemplate ValidTemplate { get; set; }
public DataTemplate InvalidTemplate { get; set; }
protected override DataTemplate OnSelectTemplate (object item,BindableObject container)
{
return ((Person)item).DateOfBirth.Year >= 1980 ? ValidTemplate : InvalidTemplate;
}
}
Person.cs:
public class Person
{
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
public string Location { get; set; }
}
隐藏代码:
public Command TapCommand
{
get
{
return new Command(val =>
{
DisplayAlert("Alert",val.ToString(),"OK");
});
}
}
public HomePage()
{
InitializeComponent();
var people = new List<Person>
{
new Person { Name = "Kath",DateOfBirth = new DateTime(1985,11,20),Location = "France" },new Person { Name = "Steve",DateOfBirth = new DateTime(1975,1,15),Location = "USA" },new Person { Name = "Lucas",DateOfBirth = new DateTime(1988,2,5),Location = "Germany" },new Person { Name = "John",DateOfBirth = new DateTime(1976,new Person { Name = "Tariq",DateOfBirth = new DateTime(1987,10),Location = "UK" },new Person { Name = "Jane",DateOfBirth = new DateTime(1982,8,30),new Person { Name = "Tom",DateOfBirth = new DateTime(1977,3,Location = "UK" }
};
listView.ItemsSource = people;
this.BindingContext = this;
}
截屏:
已更新:
使用资源字典创建单独的文件。我在这两个文件中更改了命令的绑定路径。
MyResourceDictionary.xaml:
<?xml version="1.0" encoding="UTF-8" ?>
<ResourceDictionary
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Selector">
<DataTemplate x:Key="validPersonTemplate">
<ViewCell x:Name="MyCell">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*" />
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label
FontAttributes="Bold"
Text="{Binding Name}"
TextColor="Green">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=Parent.BindingContext.TapCommand,Source={x:Reference MyCell}}" CommandParameter="false" />
</Label.GestureRecognizers>
</Label>
<Label
Grid.Column="1"
Text="{Binding DateOfBirth,StringFormat='{0:d}'}"
TextColor="Green" />
<Label
Grid.Column="2"
HorizontalTextAlignment="End"
Text="{Binding Location}"
TextColor="Green" />
</Grid>
</ViewCell>
</DataTemplate>
</ResourceDictionary>
MyResourceDictionary2.xaml:
<?xml version="1.0" encoding="UTF-8" ?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<DataTemplate x:Key="invalidPersonTemplate">
<ViewCell x:Name="MyCell2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.4*" />
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.3*" />
</Grid.ColumnDefinitions>
<Label
FontAttributes="Bold"
Text="{Binding Name}"
TextColor="Red">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=Parent.BindingContext.TapCommand,Source={x:Reference MyCell2}}" CommandParameter="false" />
</Label.GestureRecognizers>
</Label>
<Label
Grid.Column="1"
Text="{Binding DateOfBirth,StringFormat='{0:d}'}"
TextColor="Red" />
<Label
Grid.Column="2"
HorizontalTextAlignment="End"
Text="{Binding Location}"
TextColor="Red" />
</Grid>
</ViewCell>
</DataTemplate>
</ResourceDictionary>
更改ContentPage:
<?xml version="1.0" encoding="UTF-8" ?>
<ContentPage
x:Class="Selector.HomePage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Selector">
<ContentPage.Resources>
<ResourceDictionary>
<ResourceDictionary Source="MyResourceDictionary.xaml" />
<ResourceDictionary Source="MyResourceDictionary2.xaml" />
<local:PersonDataTemplateSelector
x:Key="personDataTemplateSelector"
InvalidTemplate="{StaticResource invalidPersonTemplate}"
ValidTemplate="{StaticResource validPersonTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Margin="20">
<Label
FontAttributes="Bold"
HorizontalOptions="Center"
Text="ListView with a DataTemplateSelector" />
<ListView
x:Name="listView"
Margin="0,0"
ItemTemplate="{StaticResource personDataTemplateSelector}" />
</StackLayout>
</ContentPage>
选择器和视图模型无变化,请检查。如果您对此问题仍有疑问,请随时告诉我。
,添加一个类ViewModelLocator,我使用MVVM Light
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
}
public MainViewModel MainVM
{
get { return ServiceLocator.Current.GetInstance<MainViewModel>(); }
}
}
然后,您可以使用BindingContext而不使用页面的引用
BindingContext="{Binding Path=MainVM,Source={StaticResource VMLocator}}"
App.Xaml代码
xmlns:vm="clr-namespace:xxx.xx.ViewModels"
<Application.Resources>
<vm:ViewModelLocator x:Key="VMLocator" />
</Application.Resources>
用法
选项1:
您想将标签绑定到列表视图中,但是列表视图的绑定上下文指向集合使用此标签。
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding YourVM.YourCommand,Source={StaticResource VMLocator}}" CommandParameter="{Binding UserName}" />
</Label.GestureRecognizers>
选项2:
您要将其绑定到当前页面的Viewmodel(带有页面引用)
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="xxxx.xx.xx.App"
x:Name="MyViewName">
<Label Text="My Text" IsVisible="{Binding Path=BindingContext.IsLoading,Source={x:Reference MyViewName},Mode=TwoWay}"/>
选项2: