CMPalmer
CMPalmer

Reputation: 8633

Cannot add elements to a CollectionView on Xamarin Forms iOS without hiding/deforming existing items

Here is a sample app that illustrates my problem (full source at: https://github.com/cmpalmer66/CollectionViewSample)

It works fine on UWP and Android, but on iOS, if you click the Add Label or Add Entry buttons, the preloaded list of 20 items disappears (or shrinks to 0 height). Adding more controls still just displays the last one, moving down the page. Occasionally, one of the previously added controls will show up at random.

Basically just wondering if I'm doing something wrong or if this is a bug.

MainPage.xaml

<StackLayout>
    <Button Text="Add Label" Clicked="Button1_OnClicked" />
    <Button Text="Add Entry" Clicked="Button2_OnClicked" />

    <CollectionView x:Name="MyCollectionView">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <ContentView Content="{Binding .}"></ContentView>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
</StackLayout>

MainPage.xaml.cs:

using System.Collections.ObjectModel;
using System.ComponentModel;
using Xamarin.Forms;

namespace CollectionViewSample
{
    [DesignTimeVisible(false)]
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            MyCollectionView.BindingContext = this;
            MyCollectionView.SetBinding(ItemsView.ItemsSourceProperty, nameof(ControlList));
            // Adding these 20 items all at once works fine
            for (var x = 0; x < 20; x++)
            {
                ControlList.Add(new Label {Text = $"Added at {ControlList.Count}"});
            }
        }

        public ObservableCollection<View> ControlList { get; set; } = new ObservableCollection<View>();

        private void Button1_OnClicked(object sender, EventArgs e)
        {
            // Adding a single control to the ControlList will mess up the display of the previously
            // added items on iOS
            ControlList.Add(new Label {Text = $"Added at {ControlList.Count}"});
        }

        private void Button2_OnClicked(object sender, EventArgs e)
        {
            // Adding a single control to the ControlList will mess up the display of the previously
            // added items on iOS
            ControlList.Add(new Entry {Text = $"Added at {ControlList.Count}"});
        }
    }
}

Upvotes: 0

Views: 826

Answers (1)

Leo Zhu
Leo Zhu

Reputation: 15031

Below is a simple sample:

Create a ViewTemplateSelector :

public class ViewTemplateSelector : DataTemplateSelector
{
    public DataTemplate LebelTemplate { get; set; }
    public DataTemplate EntryTemplate { get; set; }
    protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
    {
        return ((CollectionData)item).Type == 1 ? LebelTemplate : EntryTemplate;
    }
}  

then use in your page xaml:

<ContentPage.Resources>
    <ResourceDictionary>
        <DataTemplate x:Key="labelTemplate">
        
                <Label Text="{Binding Name}"/>
  
        </DataTemplate>
        <DataTemplate x:Key="entryTemplate">

                <Entry Text="{Binding Name}"/>

        </DataTemplate>
        <local:ViewTemplateSelector x:Key="viewTemplateSelector"
            LebelTemplate="{StaticResource labelTemplate}"
            EntryTemplate="{StaticResource entryTemplate}" />
    </ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
    <StackLayout>
        <Button Text="Add Label" Clicked="Button1_Clicked" />
        <Button Text="Add Entry" Clicked="Button2_Clicked" />

        <CollectionView x:Name="MyCollectionView"  ItemTemplate="{StaticResource viewTemplateSelector}" >

        </CollectionView>
    </StackLayout>
</ContentPage.Content>

create a data mode ,add a Type to determine which template to use (here 1 means add Label,2 means add Entry):

public class CollectionData
{
    public string Name { get; set; }
    public int Type { get; set; }
}

in your page.xaml.cs:

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        MyCollectionView.BindingContext = this;
        MyCollectionView.SetBinding(ItemsView.ItemsSourceProperty, nameof(ControlList));
        // Adding these 20 items all at once works fine
        for (var x = 0; x < 20; x++)
        {
             ControlList.Add(new CollectionData { Name = $"Added at {ControlList.Count}",Type = 1 });
        }
    }

    public ObservableCollection<CollectionData> ControlList { get; set; } = new ObservableCollection<CollectionData>();

    private void Button1_OnClicked(object sender, EventArgs e)
    {
        // Adding a single control to the ControlList will mess up the display of the previously
        // added items on iOS
         ControlList.Add(new CollectionData { Name = $"Added at {ControlList.Count}",Type = 1 });
    }

    private void Button2_OnClicked(object sender, EventArgs e)
    {
        // Adding a single control to the ControlList will mess up the display of the previously
        // added items on iOS
         ControlList.Add(new CollectionData { Name = $"Added at {ControlList.Count}",Type = 2 });
    }
}

Upvotes: 1

Related Questions