6
votes

I am trying to create a list in xamarin.forms that is a bit complex. The user should be able to click on an item in the list and it should expand into something somewhat bigger. The bigger item should now display some additional UI components that are associated specifically with this view.

I wonder how I should approach this problem. It's about a listview that has items of dynamic size. Upon click the item will display additional views related to the item. Should this be done in Xamarin.ios and Xamarin.droid, or is it recommended to try and achieve this in xamarin.forms?

I'll post a picture, it is not good and might need some magnification but it shows 4 items. The 3rd one is expanded and therefore you can see the spinner and button on it.

Only one item can be expanded at a time(I might have to handle that in the ViewModel) and upon pressing another item the old one should be hidden.

The list

Edit:

Thanks to Rohit I started implementing a solution in Xamarin.Forms but it doesn't really work still, just some small problems in how the row is expanded. See picture below. I'm skipping the spinner and just using a button for simplicity. The expanded row overlaps the row below itself. First picture is before click, second is after clicking on the item called "Two".

View

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="simpletest.PlayGroundPage">
    <ContentPage.Content>
        <StackLayout>
            <ListView VerticalOptions="FillAndExpand" HasUnevenRows="True" ItemsSource="{Binding AllItems}" SelectedItem="{Binding MySelectedItem}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout VerticalOptions="FillAndExpand">
                            <StackLayout VerticalOptions="FillAndExpand" Orientation="Horizontal">
                                <Label Text="{Binding MyText}" />
                                <Image Source="{Binding MyImage}" />
                            </StackLayout>
                            <Button Text="button1" IsVisible="{Binding IsExtraControlsVisible}" />  
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

Item

    public class Item : INotifyPropertyChanged
    {


        public Item(string text, string image, int id)
        {
            _myText = text;
            _myImage = image;
            _id = id;
            _isExtraControlsVisible = false;

        }

        private string _myText;
        private string _myImage;
        private bool _isExtraControlsVisible;
        private int _id;

        public event PropertyChangedEventHandler PropertyChanged;

        public int Id { get { return _id; } set { _id = value; } }

        public string MyText
        {
            get { return _myText; }
            set { _myText = value; OnPropertyChanged("MyText"); }
        }
        public string MyImage
        {
            get { return _myImage; }
            set { _myImage = value; OnPropertyChanged("MyImage"); }
        }
        public bool IsExtraControlsVisible
        {
            get { return _isExtraControlsVisible; }
            set { _isExtraControlsVisible = value; OnPropertyChanged("IsExtraControlsVisible"); }
        }

        private void OnPropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }
    }

ViewModel:

    class PlayGroundViewModel : INotifyPropertyChanged
    {
        private Item _mySelectedItem;



        public PlayGroundViewModel(ObservableCollection<Item> allItems)
        {
            AllItems = allItems;
            _mySelectedItem = allItems.First();
        }
        public ObservableCollection<Item> AllItems { get; set; }

        public Item MySelectedItem
        {
            get { return _mySelectedItem; } //Added a field for this one, mainly for debugging.
            set
            {

                foreach (Item x in AllItems) //Changed to non-linq since it is not a list.
                {
                    x.IsExtraControlsVisible = false;
                }

                if (value != null)
                {
                    foreach (Item x in AllItems)
                    {
                        if (x.Id.Equals(value.Id))
                        {
                            x.IsExtraControlsVisible = true;
                            _mySelectedItem = x;
                        }
                    }
                }

                SetChangedProperty("MySelectedItem");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void SetChangedProperty(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }

    }

CodeBehind:

    public partial class PlayGroundPage : ContentPage
    {
        public PlayGroundPage()
        {
            InitializeComponent();
            ObservableCollection<Item> items = new ObservableCollection<Item>();
            items.Add(new Item("One", "", 0));
            items.Add(new Item("Two", "", 1));
            items.Add(new Item("Three", "", 2));
            PlayGroundViewModel weekViewModel = new PlayGroundViewModel(items);
            BindingContext = weekViewModel;
        }
    }

Before clicking on a row

After having clicked

1
I have done this before. If you're using MVVM and you have your ItemsSource bound to an IList on your ViewModel, then you just need to bind the IsVisible property of the Button and Spinner to 2 bool properties on your ViewModel. When the user clicks the item, this should call an ICommand on your ViewModel and you can set both those IsVisible properties to true.Jon
@Mangist can you show your code?Jeremy Thompson
@JeremyThompson can you try my suggestion first?Jon
@Mangist Why? I am not the OP.Jeremy Thompson
@JeremyThompson ? and?Jon

1 Answers

1
votes

You can implement it in the following way using XAML, ViewModel, ObservableCollection.

XAML :

<ListView VerticalOption="FillAndExpand" HasUnevenRows="True" 
 ItemsSource="{Binding AllItems}" SelectedItem="{Binding MySelectedItem}" >
 <ListView.ItemTemplate>
   <DataTemplate>
      <ViewCell>
         <StackLayout VerticalOptions="FillAndExpand">
            <StackLayout VerticalOptions="FillAndExpand" Orientation="Horizontal">
               <Label Text="{Binding MyText}" />
               <Image Source="{Binding MyImage}" />
            </StackLayout>
            <Button Text="button" IsVisible="{Binding IsExtraControlsVisible}" />
            <Spinner IsVisible="{Binding IsExtraControlsVisible}" />
         </StackLayout>
      </ViewCell> 
   </DataTemplate>
 </ListView.ItemTemplate>
</ListView>

ViewModel :

public ObservableCollection<Item> AllItems
{
    get { return _allItems; }
    set
    {
        _allItems = value;
        OnPropertyChanged();
     }
}
public Item MySelectedItem
{
    get { return _mySelectedItem; }
    set
    {
        _mySelectedItem = value;
        OnPropertyChanged();

        foreach (var item in AllItems)
        {
            item.IsExtraControlsVisible = false;
        }
        var selectedItem = AllItems.FirstOrDefault(x => x.Equals(value));
        if (selectedItem != null)
        {
            selectedItem.IsExtraControlsVisible = true;
        }
    }
}

Item.cs :

public class Item : INotifyPropertyChanged
{
   private string _myText;
   private string _myImage;
   private bool _isExtraControlsVisible;
   private int _id;

   public int Id { get; set; }
   public string MyText 
   {
       get{ return _myText; } 
       set
       { _myText = value; 
          OnPropertyChanged();
       } 
   }
   public string MyImage
   {
       get{ return _myImage; } 
       set
       {
            _myImage = value; 
            OnPropertyChanged();
       } 
   }
   public bool IsExtraControlsVisible
   {
       get{ return _isExtraControlsVisible; } 
       set
       {
           _isExtraControlsVisible = value;
           OnPropertyChanged();
       } 
   }
}

Please find the demo here - XamarinForms_Dynamic_ListView_Item.