TabbedPage with DataTemplate and ListView with DataTemplate

Hello, I'm trying to make an app in which I need a TabbedPage type view in which DataTemplate is used, each tab must have a ListView with DataTemplate that will load the information of an API, forgot to mention that the information of the pestabas is also given by the consumption of an API.

I am having difficulties because I do not know how to make the ListView information load modularly and depending on the tab it is in.
Someone has an idea of how I can impliment this behavior using MVVM.

This is my view approach.

<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:ViewModels="clr-namespace:PoolCarePlus.ViewModels"
        x:Class="PoolCarePlus.Views.CurrentStatusDevicesView"
        ItemsSource="{Binding Things}">
<TabbedPage.ItemTemplate>
    <DataTemplate>
        <ContentPage Title="{Binding description}">
            <StackLayout>
                <Label Text="Sensors"
                       HorizontalTextAlignment="Center"
                       FontSize="Medium" />
                <ListView ItemsSource="{Binding Sensors}">
                    <ListView.BindingContext>
                        <ViewModels:CurrentStatusDeviceDetailedViewModel />
                    </ListView.BindingContext>
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <ViewCell>
                                <StackLayout Orientation="Horizontal">
                                    <Label Text="{Binding idKeyDataParameter}" />
                                </StackLayout>
                            </ViewCell>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </StackLayout>
        </ContentPage>
    </DataTemplate>
</TabbedPage.ItemTemplate>

Things: It is a list in the CurrentStatusDeviceViewModel viewmodel, in which the Thing objects that refer to the tabs are loaded.
description: It is a property of the Thing objects that contains the title of the tab.
ViewModels: CurrentStatusDeviceDetailedViewModel: It is a second viewmodel where I intend to load the ListView information of each tab.
Sensors: It is a list in the viewmodel: CurrentStatusDeviceDetailed where the sensors of each Thing must be loaded, which are the ones that feed the ListView of the tabs.
idKeyDataParameter: It is a property of a Data object it has the information that I want to show in the cells of the ListView.

The big problem I have is that I can not make the ListView information load correctly with this scheme, I thought that the second viewmodel should receive the id that identifies each Thing and bring the data, epro I can not get everything to run in the sequential way that I think and paint my sight correctly.

I hope someone can help me with this.
Thanks.

Answers

  • lavilasolavilaso Member ✭✭

    I think I have a possible solution to this and it is the following:
    In my ViewModel CurrentStatusDeviceDetailed request a parameter, which will be Things.id, the problem is that I do not know how to pass this parameter to the constructor in XAML.
    I know it must be in this part:

    <ListView.BindingContext>
        <ViewModels:CurrentStatusDeviceDetailedViewModel />
    </ListView.BindingContext>
    

    Can someone tell me how to pass the parameter?

  • LandLuLandLu Member, Xamarin Team Xamurai

    If you do want to use two view models to handle this you can make the second ViewModels:CurrentStatusDeviceDetailedViewModel as the property of the tabbed page view model. According to your description, I create my view models here for you referring to:

    public class CurrentStatusDeviceViewModel
    {
        public ObservableCollection<TabViewModel> Things { set; get; }
    
        public CurrentStatusDeviceViewModel()
        {
            // Here I create three demo pages
            Things = new ObservableCollection<TabViewModel>();
            for (int i=0; i<3; i++)
            {
                Things.Add(new TabViewModel(i.ToString()) { description = "description" + i });
            }
        }
    }
    
    public class TabViewModel
    {
        public string description { set; get; }
    
        public string TabID { set; get; }
    
        public CurrentStatusDeviceDetailedViewModel DetailedViewModel { set; get; }
    
        public TabViewModel(string tabID)
        {
            TabID = tabID;
    
            // Pass Tab ID to the second view model
            DetailedViewModel = new CurrentStatusDeviceDetailedViewModel(tabID);
        }
    }
    
    public class CurrentStatusDeviceDetailedViewModel
    {
        public ObservableCollection<SensorModel> Sensors { set; get; }
        public string CurrentID { set; get; }
    
        public CurrentStatusDeviceDetailedViewModel(string tabId)
        {
            CurrentID = tabId;
    
            // I simulate the lists here
            Sensors = new ObservableCollection<SensorModel>();
            for (int i=0; i<10; i++)
            {
                Sensors.Add(new SensorModel { idKeyDataParameter = CurrentID + i });
            }
        }
    }
    
    public class SensorModel
    {
        public string idKeyDataParameter { set; get; }
    }
    

    Then set this model to your tabbed page's binding context:

    public partial class MyTabbedPage : TabbedPage
    {
    
        public MyTabbedPage ()
        {
            InitializeComponent();
    
            BindingContext = new CurrentStatusDeviceViewModel();
        }
    }
    

    At last your xaml could be like this to show this data:

    <TabbedPage.ItemTemplate>
        <DataTemplate>
            <ContentPage Title="{Binding description}">
                <StackLayout>
                    <Label Text="Sensors"
                    HorizontalTextAlignment="Center"
                    FontSize="Medium" />
                    <ListView ItemsSource="{Binding DetailedViewModel.Sensors}">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <ViewCell>
                                    <StackLayout Orientation="Horizontal">
                                        <Label Text="{Binding idKeyDataParameter}" />
                                    </StackLayout>
                                </ViewCell>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </StackLayout>
            </ContentPage>
        </DataTemplate>
    </TabbedPage.ItemTemplate>
    
  • lavilasolavilaso Member ✭✭
    edited November 2018

    @LandLu said:
    If you do want to use two view models to handle this you can make the second ViewModels:CurrentStatusDeviceDetailedViewModel as the property of the tabbed page view model. According to your description, I create my view models here for you referring to:

    public class CurrentStatusDeviceViewModel
    {
        public ObservableCollection<TabViewModel> Things { set; get; }
    
        public CurrentStatusDeviceViewModel()
        {
            // Here I create three demo pages
            Things = new ObservableCollection<TabViewModel>();
            for (int i=0; i<3; i++)
            {
                Things.Add(new TabViewModel(i.ToString()) { description = "description" + i });
            }
        }
    }
    
    public class TabViewModel
    {
        public string description { set; get; }
    
        public string TabID { set; get; }
    
        public CurrentStatusDeviceDetailedViewModel DetailedViewModel { set; get; }
    
        public TabViewModel(string tabID)
        {
            TabID = tabID;
            
            // Pass Tab ID to the second view model
            DetailedViewModel = new CurrentStatusDeviceDetailedViewModel(tabID);
        }
    }
    
    public class CurrentStatusDeviceDetailedViewModel
    {
        public ObservableCollection<SensorModel> Sensors { set; get; }
        public string CurrentID { set; get; }
    
        public CurrentStatusDeviceDetailedViewModel(string tabId)
        {
            CurrentID = tabId;
    
            // I simulate the lists here
            Sensors = new ObservableCollection<SensorModel>();
            for (int i=0; i<10; i++)
            {
                Sensors.Add(new SensorModel { idKeyDataParameter = CurrentID + i });
            }
        }
    }
    
    public class SensorModel
    {
        public string idKeyDataParameter { set; get; }
    }
    

    Then set this model to your tabbed page's binding context:

    public partial class MyTabbedPage : TabbedPage
    {
            
        public MyTabbedPage ()
        {
            InitializeComponent();
    
            BindingContext = new CurrentStatusDeviceViewModel();
        }
    }
    

    At last your xaml could be like this to show this data:

    <TabbedPage.ItemTemplate>
        <DataTemplate>
            <ContentPage Title="{Binding description}">
                <StackLayout>
                    <Label Text="Sensors"
                    HorizontalTextAlignment="Center"
                    FontSize="Medium" />
                    <ListView ItemsSource="{Binding DetailedViewModel.Sensors}">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <ViewCell>
                                    <StackLayout Orientation="Horizontal">
                                        <Label Text="{Binding idKeyDataParameter}" />
                                    </StackLayout>
                                </ViewCell>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </StackLayout>
            </ContentPage>
        </DataTemplate>
    </TabbedPage.ItemTemplate>
    

    Creo entender en parte tu respuesta y he modificado mi codigo para que al instanciar CurrentStatusDevices se cree una coleccion de CurrentStatusDeviceDetailed por, una por cada Thing, para luego enlazar la coleccion que existirá en cada uno de ellos, esta será la coleccion Sensors.
    Sigo fracasando.
    Creo que uno de los problemas es que en mi XAML al definir el ItemSource de la TabbedPage en Things quedo atrapado en esta coleccion y luego al establecer el binding en ItemSource del ListView no sé como hacer que sea a la coleccion de CurrentStatusDeviceDetailedViewModel.
    Esta vez para mayor entendimiento dejo todas mis clases y el XAML:

    XAML:

    <?xml version="1.0" encoding="utf-8" ?>
    <TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            xmlns:ViewModels="clr-namespace:PoolCarePlus.ViewModels"
            x:Class="PoolCarePlus.Views.CurrentStatusDevicesView"
            ItemsSource="{Binding Things}">
    <TabbedPage.BindingContext>
        <ViewModels:CurrentStatusDevicesViewModel />
    </TabbedPage.BindingContext>
    <TabbedPage.ItemTemplate>
        <DataTemplate>
            <ContentPage Title="{Binding description}">
                <StackLayout>
                    <Label Text="Sensors"
                           HorizontalTextAlignment="Center"
                           FontSize="Medium" />
                    <ListView ItemsSource="{Binding CurrentStatuses.Sensors}">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <ViewCell>
                                    <StackLayout Orientation="Horizontal">
                                        <Label Text="{Binding idKeyDataParameter}" />
                                    </StackLayout>
                                </ViewCell>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </StackLayout>
            </ContentPage>
        </DataTemplate>
    </TabbedPage.ItemTemplate>
    

    CurrentStatusDevicesViewModel:

    public class CurrentStatusDevicesViewModel : Notificable
    {
        private ObservableCollection<CurrentStatusDeviceDetailedViewModel> currentStatuses;
    
        public ObservableCollection<CurrentStatusDeviceDetailedViewModel> CurrentStatuses
        {
            get { return currentStatuses; }
            set
            {
                currentStatuses = value;
                OnPropertyChanged();
            }
        }
    
        private ObservableCollection<PcThing> things;
    
        public ObservableCollection<PcThing> Things
        {
            get { return things; }
            set
            {
                things = value;
                OnPropertyChanged();
            }
        }
    
        public CurrentStatusDevicesViewModel()
        {
            LoadThings();
            foreach (var t in Things)
            {
                CurrentStatuses.Add(new CurrentStatusDeviceDetailedViewModel(t.idCode));
            }
        }
    }
    

    CurrentStatusDeviceDetailedViewModel:

    public class CurrentStatusDeviceDetailedViewModel : Notificable
    {
    
        private ObservableCollection<PcData> sensors;
    
        public ObservableCollection<PcData> Sensors
        {
            get { return sensors; }
            set
            {
                sensors = value;
                OnPropertyChanged();
            }
        }
    
        public CurrentStatusDeviceDetailedViewModel(string id)
        {
            LoadSensorsAsync(id);
        }
    
        private async void LoadSensorsAsync(string id)
        {
            Sensors = new ObservableCollection<PcData>(await App.ApiManager.GetSensorsValues(id));
        }
    }
    
  • LandLuLandLu Member, Xamarin Team Xamurai

    @lavilaso CurrentStatusDeviceDetailedViewModel should be defined in your PcThing just like my TabViewModel. Since your Tabbed page's ItemsSource is Things, it should be a part of the Things not the same hierarchy with Things.
    Moreover please try to translate your post to English, so that we can understand your meanings more correctly.
    If you do have difficulty understanding data binding, please refer to my sample here. I simulate the data for you.

  • lavilasolavilaso Member ✭✭

    @LandLu said:
    @lavilaso CurrentStatusDeviceDetailedViewModel should be defined in your PcThing just like my TabViewModel. Since your Tabbed page's ItemsSource is Things, it should be a part of the Things not the same hierarchy with Things.
    Moreover please try to translate your post to English, so that we can understand your meanings more correctly.
    If you do have difficulty understanding data binding, please refer to my sample here. I simulate the data for you.

    Thank you very much for answering.
    I understand that each CurrentStatusDeviceDetailedViewModel must be inside PcThing.
    I will look at the example.
    I have been exploring making the Source of Binding be x:Reference page and Path=CurrentStatuses.Sensors, this by adding x:Name="page" to the TabbedPage, similar to what they recommend here, although I still do not have a satisfactory result.
    Finally I am sorry to have forgotten to translate the answer.

Sign In or Register to comment.