Estoy codificando un proyecto Xamarin.Forms y tengo una vista de lista, pero cada vez que muestro contenido oculto, por ejemplo, hago que una entrada sea visible, la ViewCell se superpone a la que está debajo.

¿Hay alguna manera de que pueda .Update() la vista de lista o algo para actualizarla y hacer que encajen?

Sin embargo, no quiero que la actualización vuelva a la cima.

Parece que Android puede actualizar automáticamente la altura cuando muestro algo.

Intenté usar HasUnevenRows="True" pero eso aún no lo solucionó.

Código:

Message.xaml

<StackLayout>
        <local:PostListView x:Name="MessageView" HasUnevenRows="True" IsPullToRefreshEnabled="True" Refreshing="MessageView_Refreshing" SeparatorVisibility="None" BackgroundColor="#54a0ff">
            <local:PostListView.ItemTemplate>
                <DataTemplate>
                    <local:PostViewCell>
                        <StackLayout>
                            <Frame CornerRadius="10" Padding="0" Margin="10, 10, 10, 5" BackgroundColor="White">
                                <StackLayout>
                                    <StackLayout x:Name="MessageLayout" BackgroundColor="Transparent" Padding="10, 10, 15, 10">
                                        ...
                                        <Label Text="{Binding PostReply}" FontSize="15" TextColor="Black" Margin="10, 0, 0, 10" IsVisible="{Binding ShowReply}"/>
                                        <StackLayout Orientation="Vertical" IsVisible="{Binding ShowReplyField}" Spacing="0">
                                            <Entry Text="{Binding ReplyText}" Placeholder="Reply..." HorizontalOptions="FillAndExpand" Margin="0, 0, 0, 5"/>
                                            ...
                                        </StackLayout>
                                        <StackLayout x:Name="MessageFooter" Orientation="Horizontal" IsVisible="{Binding ShowBanners}">
                                            <StackLayout Orientation="Horizontal">
                                                ...
                                                <Image x:Name="ReplyIcon" Source="reply_icon.png" HeightRequest="20" HorizontalOptions="StartAndExpand" IsVisible="{Binding ShowReplyButton}">
                                                    <Image.GestureRecognizers>
                                                        <TapGestureRecognizer Command="{Binding ReplyClick}" CommandParameter="{Binding .}"/>
                                                    </Image.GestureRecognizers>
                                                </Image>
                                                ... 
                                            </StackLayout>
                                            ...
                                        </StackLayout>
                                    </StackLayout>
                                </StackLayout>
                            </Frame>
                        </StackLayout>
                    </local:PostViewCell>
                </DataTemplate>
            </local:PostListView.ItemTemplate>
        </local:PostListView>
    </StackLayout>

Message.cs

    using Newtonsoft.Json;
 using SocialNetwork.Classes;
 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace SocialNetwork
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MessagePage : ContentPage
{
    public MessagePage()
    {
        InitializeComponent();
        LoadPage();
    }

    private async void LoadPage()
    {
        await LoadMessages();
    }

    private async void RefreshPage()
    {
        await LoadMessages();
        MessageView.EndRefresh();
    }

    private async Task LoadMessages()
    {
        //*Web Request*
        MessageView.ItemsSource = FormatPosts(this, Navigation, page_result);
        ...
    }

    public IList<MessageObject> FormatPosts(Page page, INavigation navigation, string json)
    {
        IList<MessageObject> Posts = new List<MessageObject>() { };
        var messages = JsonConvert.DeserializeObject<List<Message>>(json);

        foreach (var message in messages)
        {
            MessageObject mo = MessageObject.CreateMessage(...);
            Posts.Add(mo);
        }

        return Posts;
    }
    public async void ShowOptionActions(string id, string poster_id, object message)
    {
        ...
    }

    public async void ShowReportOptions(string id, string poster_id)
    {
        ...
    }

    public void SubmitReplyClick(string id, object msg)
    {
        ...
    }

    public async void SendReplyAsync(string id, object msg, string reply)
    {
        await SendReply(id, msg, reply);
    }

    public void ReplyCommandClick(string id, object msg)
    {
        MessageObject message = (MessageObject) msg;
        message.ShowReplyField = message.ShowReplyField ? false : true;
        //Update Cell Bounds
    }

    private async Task SendReply(string id, object msg, string reply)
    {
        MessageObject message = (MessageObject)msg;
        ...
        message.PostReply = reply;

        //Update Cell Bounds
    }

    public async void LikeMessageClick(string id, object message)
    {
        await LikeMessage(id, message);
    }

    private async Task LikeMessage(string id, object msg)
    {
       ...
    }

    public async void DeleteMessage(string id, object msg)
    {
        MessageObject message = (MessageObject)msg;

        message.ShowBanners = false;
        message.ShowReply = false;

        ...

        //Update Cell Bounds
    }

    public async Task ReportMessage(...)
    {
        ...
    }

    private void MessageView_Refreshing(object sender, EventArgs e)
    {
        RefreshPage();
    }
}

public class MessageObject : INotifyPropertyChanged
{
    private Boolean showBannersValue = true;
    private string replyValue = String.Empty;
    private bool showReplyValue;
    private bool showReplyButtonValue;
    private bool showReplyFieldValue;
    private Command replyCommandValue;
    private Command replySubmitValue;
    private string replyTextValue;
     ...

    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private MessageObject(...)
    {
        ...
    }

    public static MessageObject CreateMessage(...)
    {
        return new MessageObject(...);
    }

    public Boolean ShowBanners
    {
        get
        {
            return this.showBannersValue;
        }

        set
        {
            if (value != this.showBannersValue)
            {
                this.showBannersValue = value;
                NotifyPropertyChanged();
            }
        }
    }

    public Boolean ShowReplyField
    {
        get
        {
            return this.showReplyFieldValue;
        }

        set
        {
            if(value != this.showReplyFieldValue)
            {
                this.showReplyFieldValue = value;
                NotifyPropertyChanged();
            }
        }
    }

    public string PostReply
    {
        get
        {
            return this.replyValue;
        }

        set
        {
            if (value != this.replyValue)
            {
                this.replyValue = value;
                NotifyPropertyChanged();
            }
        }
    }

    public Boolean ShowReply
    {
        get
        {
            return this.showReplyValue;
        }

        set
        {
            if(value != this.showReplyValue)
            {
                this.showReplyValue = value;
                NotifyPropertyChanged();
            }
        }
    }

    public Boolean ShowReplyButton
    {
        get
        {
            return this.showReplyButtonValue;
        }

        set
        {
            if (value != this.showReplyButtonValue)
            {
                this.showReplyButtonValue = value;
                NotifyPropertyChanged();
            }
        }
    }

      public string ReplyText
    {
        get
        {
            return this.replyTextValue;
        }

        set
        {
            if(value != this.replyTextValue)
            {
                this.replyTextValue = value;
                NotifyPropertyChanged();
            }
        }
    }

    public Command ReplyClick
    {
        get
        {
            return this.replyCommandValue;
        }

        set
        {
            if (value != this.replyCommandValue)
            {
                this.replyCommandValue = value;
                NotifyPropertyChanged();
            }
        }
    }

    ...

}
}
1
Dan 7 mar. 2018 a las 08:14

3 respuestas

La mejor respuesta

Guarde su IList<MessageObject> que se devuelve desde su método FormatPosts en un campo IList<MessageObject> _messages = new List<MessageObject>()

Y use el siguiente fragmento para actualizar ListView cuando lo necesite, incluye una verificación para ver si el dispositivo se ejecuta en iOS:

if(Device.RuntimePlatform == Device.iOS) 
{ 
    MessageView.ItemsSource = null; 
    MessageView.ItemsSource = _messages; 
}
1
Raimo 14 mar. 2018 a las 04:18

Especialmente con iOS, hay problemas para cambiar el tamaño de las filas en un ListView según los cambios de las celdas (consulte aquí). Hay un método ForceUpdateSize en Cell, que debería notificar a ListView que el tamaño de la celda ha cambiado, lo que debería hacer que ListView cambie el tamaño de sus filas.

1
Paul Kertscher 7 mar. 2018 a las 06:53

Oh, me enfrenté a lo mismo. Supongo que solo necesita agregar esto en algún lugar de su vista de lista:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <Grid Grid.row='0'>
    ...
    </Grid>

 // This, in my case, makes my cell expand when it's true. Normal behavior
    <Grid Grid.row='1' isVisible="{Binding Expand}">
    ...
    </Grid>
</Grid>

Además, si desea actualizar las celdas individualmente, uso una Custom ObservableCollection:

public class CustomObservableCollection<T> : ObservableCollection<T>
    {
        public CustomObservableCollection() { }
        public CustomObservableCollection(IEnumerable<T> items) : this()
        {
            foreach(var item in items)
                this.Add(item);
        }
        public void ReportItemChange(T item)
        {
            NotifyCollectionChangedEventArgs args =
                new NotifyCollectionChangedEventArgs(
                    NotifyCollectionChangedAction.Replace,
                    item,
                    item,
                    IndexOf(item));
            OnCollectionChanged(args);
        }
    }

Con un ListView personalizado para hacer ItemClickCommand:

public class CustomListView : ListView
    {
#pragma warning disable 618
        public static BindableProperty ItemClickCommandProperty = BindableProperty.Create<CustomListView, ICommand>(x => x.ItemClickCommand, null);
#pragma warning restore 618

        public CustomListView(ListViewCachingStrategy cachingStrategy = ListViewCachingStrategy.RetainElement) :
                base(cachingStrategy)
        {
            this.ItemTapped += this.OnItemTapped;
        }

        public ICommand ItemClickCommand
        {
            get { return (ICommand)this.GetValue(ItemClickCommandProperty); }
            set { this.SetValue(ItemClickCommandProperty, value); }
        }

        private void OnItemTapped(object sender, ItemTappedEventArgs e)
        {
            if(e.Item != null && this.ItemClickCommand != null && this.ItemClickCommand.CanExecute(e.Item))
            {
                this.ItemClickCommand.Execute(e.Item);
                this.SelectedItem = null;
            }
        }
    }

Luego en xaml:

...
...
<Customs:CustomListView
      HasUnevenRows="true"
      ItemsSource="{Binding PersonList}"
      IsPullToRefreshEnabled="True"
      RefreshCommand="{Binding DoRefreshCommand}"
      ItemClickCommand="{Binding ItemClickCommand}">
...
...
</Customs:CustomListView>

Finalmente:

public Command<Person> ItemClickCommand { get; set; }
...
ItemClickCommand = new Command<Person>(SelectionExecute);
...
private void SelectionExecute(Person arg)
        {
            arg.Expand = !arg.Expand;

            foreach(var item in PersonList)
            {
                if(item.Key == arg.Id)// you will change this probably 
                    item.ReportItemChange(arg);
            }
        }

Espero que ayude un poco :)

0
Alan Jones Rios 13 mar. 2018 a las 10:40