-1

Suppose I have the following classes:

class Person
{
    public string Name { get; set; }
    public IList<Expense> Expenses { get; set; }
    public int Total => Expenses.Sum(o => o.Amount);
}

class Expense
{
    public string ExpenseName { get; set; }
    public int Amount { get; set; }
}

And then I have a collection of persons: IList<Person> Persons

How can I design an XAML page in Xamarin, in order to show something like this?:

UI sample

rettiseert
  • 117
  • 2
  • 4

2 Answers2

1

You can use Label to display the variable number of expenses, In the end of item, add "\n" to wrap.

The running screenshot like following img.

enter image description here

First, here is my layout.

<StackLayout>
        <ListView x:Name="EmployeeView" HasUnevenRows="True"
            ItemsSource="{Binding Persons}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout>
                            <Label FontSize="Large" Text="{Binding Name}"></Label>
                            <Label FontSize="Body" Text="{Binding ExpensesList}"></Label>
                            <Label FontSize="Body" Text="{Binding Total}"></Label>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>

Here is layout's background code.

 public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            this.BindingContext =new MyViewModel();
        }
    }

Here is my viewModel. I add some testing data to viewModel, And add "\n" to the end of every expense's text.

using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace App83
{
    internal class MyViewModel
    {
        public  ObservableCollection<Person> Persons { get; set; }
        public MyViewModel()
        {
            Persons = new ObservableCollection<Person>();

            ObservableCollection<Expense> expenses= new ObservableCollection<Expense>();
            expenses.Add(new Expense() { Amount=1, ExpenseName="name1" });
            expenses.Add(new Expense() { Amount = 1, ExpenseName = "name2" });
            expenses.Add(new Expense() { Amount = 1, ExpenseName = "name3" });
            expenses.Add(new Expense() { Amount = 1, ExpenseName = "name4" });


            ObservableCollection<Expense> expenses2 = new ObservableCollection<Expense>();
            expenses2.Add(new Expense() { Amount = 1, ExpenseName = "name1" });

            ObservableCollection<Expense> expenses3 = new ObservableCollection<Expense>();


            string expensesList1="";
            for (int i = 0; i < expenses.Count; i++)
            {
                if(i== (expenses.Count - 1))
                {

                    expensesList1 += expenses[i].ToString();
                }
                else
                {
                    expensesList1 += expenses[i].ToString() + "\n";
                }
               
                
            }


            string expensesList2 = "";
            for (int i = 0; i < expenses2.Count; i++)
            {
                if (i == (expenses2.Count - 1))
                {

                    expensesList2 += expenses2[i].ToString();
                }
                else
                {
                    expensesList2 += expenses2[i].ToString() + "\n";
                }


            }
            string expensesList3 = "";
            for (int i = 0; i < expenses3.Count; i++)
            {
                if (i == (expenses3.Count - 1))
                {

                    expensesList3 += expenses3[i].ToString();
                }
                else
                {
                    expensesList3 += expenses3[i].ToString() + "\n";
                }


            }


            
            Person per =new Person( ) { Name="test1", Expenses= expenses, expensesList= expensesList1 };

            Person per2 = new Person() { Name = "test2", Expenses = expenses2,expensesList= expensesList2 };

            Person per3 = new Person() { Name = "test3", Expenses = expenses3, expensesList = expensesList3 };

            Persons.Add(per);
            Persons.Add(per2);
            Persons.Add(per3);
        }
    }
}

Here is my edited Model. AddExpensesList perperty to show variable number of expenses, keep the ExpenseName and Amountin the same line, I override the ToString method.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace App83
{
   public class Person: INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        public Person() {

          

        }
        public string Name { get; set; }

        public string expensesList;
      
        public string ExpensesList
        {
            set
            {
                if (expensesList != value)
                {
                    expensesList = value;
                    OnPropertyChanged("Image");

                }
            }
            get
            {
                return expensesList;
            }
        }

        public IList<Expense> Expenses { get; set; }
        public int Total => Expenses.Sum(o => o.Amount);
    }

    public class Expense
    {
        public string ExpenseName { get; set; }
        public int Amount { get; set; }

        public override string ToString()
        {

            return ExpenseName+"  "+Amount;
        }
    }
}

Leon
  • 8,404
  • 2
  • 9
  • 52
  • Implementing it with labels looks very easy, but lacks some flexibility because it would be hard to display columns with uniform width. If I can't manage to find another "nicer" way then I'll fall back to labels. Thanks. – rettiseert Apr 10 '21 at 19:48
0

Within your DataTemplate you can add another View that supports the ItemSource property.

So it could be another ListView, CollectionView or a StackLayout. The latter is great if you know that you won't have too many items under expense as otherwise it might cause serious app performance issues.

So inside of your DataTemplate just add

<CollectionView ItemSource={Binding Expenses}>
   <DataTemplate>
     //your UI goes here
.
.
.
SomeStudent
  • 2,856
  • 1
  • 22
  • 36
  • I didn't know I could do that. It's my first couple of weeks on Xamarin and there is so much to learn... I'll try this approach next week. Thanks. – rettiseert Apr 10 '21 at 19:51