Reputation: 83
The above header refers.
I am trying to implement a chatting app. There are no compilation errors but whenever it starts running and gets to this line:
MessageList.ScrollTo(viewModel.Messages.Last(), null, ScrollToPosition.End, true);
it triggers this error:
"Invalid target position".
This is the stack trace:
"Ex = {Java.Lang.IllegalArgumentException: Invalid target position at Java.Interop.JniEnvironment+InstanceMethods.CallVoidMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x00068] in /Use..."
This is the ChatView.xaml file where MessageList is defined:
<?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:selectors="clr-namespace:Chat.Selectors"
xmlns:converters="clr-namespace:Chat.Converters"
x:Class="Chat.Views.ChatView">
<ContentPage.Resources>
<selectors:ChatMessageSelector x:Key="SelectMessageTemplate" />
<converters:Base64ToImageConverter x:Key="ToImage" />
</ContentPage.Resources>
<ScrollView>
<ScrollView.Orientation>
<OnPlatform x:TypeArguments="ScrollOrientation">
<On Platform="iOS" Value="Vertical" />
<On Platform="Android" Value="Neither" />
</OnPlatform>
</ScrollView.Orientation>
<Grid x:Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="1" />
<RowDefinition>
<RowDefinition.Height>
<OnPlatform x:TypeArguments="GridLength">
<On Platform="iOS" Value="50" />
<On Platform="Android" Value="100" />
</OnPlatform>
</RowDefinition.Height>
</RowDefinition>
</Grid.RowDefinitions>
<CollectionView x:Name="MessageList" ItemsSource="{Binding Messages}"
ItemTemplate="{StaticResource SelectMessageTemplate}">
<CollectionView.Resources>
<ResourceDictionary>
<DataTemplate x:Key="SimpleText">
<Grid Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Frame StyleClass="remoteMessage" HasShadow="false" BackgroundColor="#F04D6A">
<StackLayout>
<Label Text="{Binding Username}" StyleClass="chatHeader" FontAttributes="Bold"/>
<Label Text="{Binding Text}" StyleClass="chatText" />
</StackLayout>
</Frame>
</Grid>
</DataTemplate>
<DataTemplate x:Key="LocalSimpleText">
<Grid Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Frame Grid.Column="1" StyleClass="localMessage" HasShadow="false" BackgroundColor="#24A43B">
<StackLayout>
<Label Text="{Binding Username}" StyleClass="chatHeader" FontAttributes="Bold"/>
<Label Text="{Binding Text}" StyleClass="chatText" />
</StackLayout>
</Frame>
</Grid>
</DataTemplate>
<DataTemplate x:Key="UserConnected">
<StackLayout Padding="10" BackgroundColor="#33000000" Orientation="Horizontal">
<Label Text="{Binding Username}" StyleClass="chatHeader" VerticalOptions="Center" />
<Label Text="connected" StyleClass="chatText" VerticalOptions="Center" />
</StackLayout>
</DataTemplate>
<DataTemplate x:Key="Photo">
<Grid Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackLayout>
<Label Text="{Binding Username}" StyleClass="chatHeader" />
<Image Source="{Binding Url}" Aspect="AspectFill"
HeightRequest="150" HorizontalOptions="Fill" />
</StackLayout>
</Grid>
</DataTemplate>
<DataTemplate x:Key="LocalPhoto">
<Grid Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackLayout Grid.Column="1">
<Label Text="{Binding Username}" StyleClass="chatHeader" />
<Image Source="{Binding Base64Photo, Converter={StaticResource ToImage}}"
Aspect="AspectFill" HeightRequest="150" HorizontalOptions="Fill" />
</StackLayout>
</Grid>
</DataTemplate>
</ResourceDictionary>
</CollectionView.Resources>
</CollectionView>
<BoxView Grid.Row="1" HeightRequest="1" BackgroundColor="#33000000" />
<Grid Grid.Row="2" Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<Image Source="photo.png"
VerticalOptions="Center" HorizontalOptions="Center" HeightRequest="30">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Photo}" />
</Image.GestureRecognizers>
</Image>
<Entry Text="{Binding Text}" Grid.Column="1"
ReturnCommand="{Binding Send}" />
<Image Grid.Column="2" Source="send.png"
VerticalOptions="Center" HorizontalOptions="Center" HeightRequest="30" >
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Photo}" />
</Image.GestureRecognizers>
</Image>
</Grid>
</Grid>
</ScrollView>
</ContentPage>
This is the ChatViewModel.cs file:
using Acr.UserDialogs;
using Chat.Services;
using Chat.Messages;
using Plugin.Media;
using Plugin.Media.Abstractions;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
namespace Chat.ViewModels
{
public class ChatViewModel : BaseViewModel
{
private readonly IChatService chatService;
public ObservableCollection<Message> Messages { get; private set; }
public ChatViewModel(IChatService chatService)
{
this.chatService = chatService;
Messages = new ObservableCollection<Message>();
chatService.NewMessage += ChatService_NewMessage;
Task.Run(async () =>
{
if (!chatService.IsConnected)
{
await chatService.CreateConnection();
}
await chatService.SendMessage(new UserConnectedMessage(User));
});
}
private string text;
public string Text
{
get => text;
set => Set(ref text, value);
}
public ICommand Send => new Command(async () =>
{
var message = new SimpleTextMessage(User)
{
Text = this.Text
};
Messages.Add(new LocalSimpleTextMessage(message));
await chatService.SendMessage(message);
Text = string.Empty;
});
public ICommand Photo => new Command(async () =>
{
var options = new PickMediaOptions
{
CompressionQuality = 50
};
var photo = await CrossMedia.Current.PickPhotoAsync();
UserDialogs.Instance.ShowLoading("Uploading photo");
var stream = photo.GetStream();
var bytes = ReadFully(stream);
var base64photo = Convert.ToBase64String(bytes);
var message = new PhotoMessage(User)
{
Base64Photo = base64photo,
FileEnding = photo.Path.Split('.').Last()
};
Messages.Add(message);
await chatService.SendMessage(message);
UserDialogs.Instance.HideLoading();
});
private void ChatService_NewMessage(object sender, Events.NewMessageEventArgs e)
{
Device.BeginInvokeOnMainThread(() =>
{
if (!Messages.Any(x => x.Id == e.Message.Id))
{
Messages.Add(e.Message);
}
});
}
private byte[] ReadFully(Stream input)
{
using (MemoryStream ms = new MemoryStream())
{
ms.CopyTo(input);
return ms.ToArray();
}
}
private string username;
public string Username { get => username; set => Set(ref username, value); }
private ImageSource url;
public ImageSource Url { get => url; set => Set(ref url, value); }
private ImageSource base64Photo;
public ImageSource Base64Photo { get => base64Photo; set => Set(ref base64Photo, value); }
}
}
This is the ChatView.xaml.cs file:
using Chat.ViewModels;
using System;
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
using Xamarin.Forms.Xaml;
namespace Chat.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ChatView : ContentPage
{
private ChatViewModel viewModel;
public ChatView(ChatViewModel viewModel)
{
this.viewModel = viewModel;
InitializeComponent();
On<Xamarin.Forms.PlatformConfiguration.iOS>().SetUseSafeArea(true);
viewModel.Messages.CollectionChanged += Messages_CollectionChanged;
BindingContext = viewModel;
}
protected override void OnAppearing()
{
base.OnAppearing();
var safeArea = On<Xamarin.Forms.PlatformConfiguration.iOS>().SafeAreaInsets();
MainGrid.HeightRequest = this.Height - safeArea.Top - safeArea.Bottom;
}
private void Messages_CollectionChanged(object sender,
System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
try
{
int Kounter = viewModel.Messages.Count;
//if (Kounter == 1)
//{
//MessageList.ScrollTo(viewModel.Messages.First(), null, ScrollToPosition.Start, true);
//}
//else
//{
MessageList.ScrollTo(viewModel.Messages.Last(), null, ScrollToPosition.End, true);
//}
}
catch (Exception Ex)
{
Console.WriteLine(Ex);
}
}
}
}
I am using Xamarin.forms version 5.0.0.2515 on Visual Studio version 17.3.
So, what is the issue and how do I sort it out?
Upvotes: 1
Views: 1011
Reputation: 1
For Chat page I did next: CollectionView Rotation=180 and Content Rotation=180. Works perfect
Upvotes: 0
Reputation: 21243
Instead of manually scrolling, set CollectionView
's ItemsUpdatingScrollMode
property.
From Control scroll position when new items are added:
<CollectionView ItemsUpdatingScrollMode="KeepLastItemInView">
Upvotes: 1