I've searched a lot about this issue and frankly, I am stack on that one. I have a chat application. On this app there is a view where there are messages both from Me and other Chat members. Technically speaking it is a ListView with ItemTemplate with has a class Binded (DataTemplateSelector) which returns ViewCells basing on the rule (whether the message to display is MINE or OTHERS)
The message (Inbound or Outbound) is in separate files.
Currently, TapGestureRecognizer is not working and the command ChooseNameToMentionCommand is not firing
There is a lot of "similar" questions where TapGestureRecognizer is not working on ListView like this one:
TapGestureRecognizer not working inside ListView
The answer there (and on any other correlated topic) for Command not working is:
- use
Source={x:Reference MessagesListView}
on your Command binding
But when I use this suggestion I am ending with:
Xamarin.Forms.Xaml.XamlParseException: 'Position 30:21. Can not find the object referenced by MessagesListView'
How can i use this in my case (with ViewCell defined in separate file) Important note - i am using MVVM approach and don't want to do anything in codebehind of ViewCell (then i could even use Tapped event. I've tested that. Of course this approach works:) )
Here is my code:
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
(i will not post OutgoingViewCell - it is almost the same;) Different colors)
<?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] I forgot to put here MainPage.cs so here it is:
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;
}
}
There is for the ListOnItemTapped event added (i forgot about it - because it was taken from some chat example. But i've don't suppose it breaks anything. When i've added OnTapped for Label directly - it did work.