Md Idiake
Md Idiake

Reputation: 83

Programmatically scrolling a CollectionView to end causes exception "Invalid Target Position"

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

Answers (2)

Andrey Kotsyuba
Andrey Kotsyuba

Reputation: 1

For Chat page I did next: CollectionView Rotation=180 and Content Rotation=180. Works perfect

Upvotes: 0

ToolmakerSteve
ToolmakerSteve

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

Related Questions