Listview with information of 2 or more ObservableCollections

Enrique4toEnrique4to Member ✭✭

Hello:

I Have a ListView in a ContentPage, and 2 ObservableCollections in a ViewModel.

What I want is in the Lisetview, list information from both ObservableCollections.

                        <ListView                    
                            HasUnevenRows="True"
                            IsPullToRefreshEnabled="True"
                            IsRefreshing="{Binding IsRefreshing}"                    
                            ItemsSource="{Binding Jobs}"
                            RefreshCommand="{Binding RefreshJubssCommand}">
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <ViewCell>
                                        <Grid>
                                            <!--<Grid.GestureRecognizers>
                                                <TapGestureRecognizer Command="{Binding JobPageCommand}"/>
                                            </Grid.GestureRecognizers>-->
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="Auto"/>
                                                <ColumnDefinition Width="*"/>
                                                <ColumnDefinition Width=".05*"/>
                                            </Grid.ColumnDefinitions>
                                            <Grid.RowDefinitions>
                                                <RowDefinition Height="Auto"/>
                                            </Grid.RowDefinitions>

                                            <StackLayout
                                                Grid.Column="1"
                                                HorizontalOptions="StartAndExpand"
                                                VerticalOptions="CenterAndExpand">
                                                <Label
                                                    FontAttributes="Bold"
                                                    FontSize="14"
                                                    Text="{Binding Subject}" **<--ObservableCollection Jobs**
                                                    TextColor="LightGray"
                                                    VerticalTextAlignment="End"/>
                                                <StackLayout
                                                    HorizontalOptions="StartAndExpand"
                                                    Orientation="Horizontal"
                                                    VerticalOptions="Start">
                                                    <Label
                                                        FontAttributes="Bold"
                                                        FontSize="12"
                                                        HorizontalOptions="Start"
                                                        Text="{Binding Date_Start}" **<--ObservableCollection Dates**
                                                        TextColor="LightGray"
                                                        VerticalTextAlignment="Start"/>
                                                    <Label
                                                        FontAttributes="Bold"
                                                        FontSize="12"
                                                        HorizontalOptions="StartAndExpand"
                                                        Text="{Binding Hour_Start}" **<--ObservableCollection Dates**
                                                        TextColor="LightGray"
                                                        VerticalTextAlignment="Start"/>
                                                </StackLayout>
                                            </StackLayout>
                                            <Image
                                                Grid.Column="2"
                                                HorizontalOptions="StartAndExpand"
                                                Source="ChevronRight"
                                                VerticalOptions="Center"
                                                WidthRequest="20"/>
                                        </Grid>
                                    </ViewCell>
                                </DataTemplate>
                            </ListView.ItemTemplate>
                        </ListView>

How can I do that

Answers

  • JamesLaveryJamesLavery GBBeta, University ✭✭✭✭✭
    You will need to combine the two collections into one and bind to that.
  • Enrique4toEnrique4to Member ✭✭
    > @JamesLavery said:
    > You will need to combine the two collections into one and bind to that.

    I'm Sorry, I'm kind of new on this, can you show me how to do that, 'couse I'm try observablecollection.Union, but it doesn't works....
  • JamesLaveryJamesLavery GBBeta, University ✭✭✭✭✭
    Please post your viewmodel/pagemodel code where you are retrieving/populating your observable collections.
  • yelinzhyelinzh Member, Xamarin Team Xamurai

    You can create a new Model class and put the two model in the class. Then you just need to create and bind one ObservableCollection.
    As following code shows:

    Model.cs

    namespace App6
    {
        public class Model_Collection
        {
            public Model_1 Model_1 { get; set; }
    
            public Model_2 Model_2 { get; set; }
    
        }
    
        public class Model_1 {
            public string Text { get; set; }
        }
    
        public class Model_2
        {
            public string Content { get; set; }
        }
    }
    

    page.xaml.cs

    public partial class Page4 : ContentPage
    {
        ObservableCollection<Model_Collection> list = new ObservableCollection<Model_Collection>();
        public Page4()
        {
            InitializeComponent();
    
            list.Add(new Model_Collection() { Model_1 = new Model_1 { Text = "text_1" }, Model_2 = new Model_2 { Content = "content_1" } });
            list.Add(new Model_Collection() { Model_1 = new Model_1 { Text = "text_2" }, Model_2 = new Model_2 { Content = "content_2" } });
            list.Add(new Model_Collection() { Model_1 = new Model_1 { Text = "text_3" }, Model_2 = new Model_2 { Content = "content_3" } });
    
            listview.ItemsSource = list;
        }
    }
    

    page.xaml

    <StackLayout>
        <Label Text="Two ObservableCollection Binding" />
        <ListView x:Name="listview">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*"/>
                                <RowDefinition Height="*"/>
                            </Grid.RowDefinitions>
                            <Label x:Name="label1" Text="{Binding Model_1.Text}" HorizontalOptions="CenterAndExpand" Grid.Row="0" Grid.Column="0"/>
                            <Label x:Name="label" Text="{Binding Model_2.Content}" HorizontalOptions="CenterAndExpand" Grid.Row="1" Grid.Column="0"/>
                        </Grid>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
    
  • voidstreamvoidstream FRMember ✭✭✭
    edited April 16

    @Enrique4to said:
    > @JamesLavery said:
    > You will need to combine the two collections into one and bind to that.

    I'm Sorry, I'm kind of new on this, can you show me how to do that, 'couse I'm try observablecollection.Union, but it doesn't works....

    If you just use Add/Remove from the ObservableCollection you don't need to Notify your view, but if you write "your Collection = anything" you should implement INotifyPropertyChanged. Don't forget this, this rules is for all properties binded :smile:

    public class MyViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        private ObservableCollection<object> _myCollection;
        public ObservableCollection<object> MyCollection
        {
            get => _myCollection
            set => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyCollection)));
        }       
    }
    

    You can use a Framework (like PRISM) for easier implementation and for others features

  • Enrique4toEnrique4to Member ✭✭

    @JamesLavery said:
    Please post your viewmodel/pagemodel code where you are retrieving/populating your observable collections.

    Shure:

    Page:

                            <ListView                    
                                HasUnevenRows="True"
                                IsPullToRefreshEnabled="True"
                                IsRefreshing="{Binding IsRefreshing}"                    
                                ItemsSource="{Binding Jobs}"
                                RefreshCommand="{Binding RefreshJubssCommand}">
                                <ListView.ItemTemplate>
                                    <DataTemplate>
                                        <ViewCell>
                                            <Grid>
                                                <!--<Grid.GestureRecognizers>
                                                    <TapGestureRecognizer Command="{Binding JobPageCommand}"/>
                                                </Grid.GestureRecognizers>-->
                                                <Grid.ColumnDefinitions>
                                                    <ColumnDefinition Width="Auto"/>
                                                    <ColumnDefinition Width="*"/>
                                                    <ColumnDefinition Width=".05*"/>
                                                </Grid.ColumnDefinitions>
                                                <Grid.RowDefinitions>
                                                    <RowDefinition Height="Auto"/>
                                                </Grid.RowDefinitions>
    
                                                <StackLayout
                                                    Grid.Column="1"
                                                    HorizontalOptions="StartAndExpand"
                                                    VerticalOptions="CenterAndExpand">
                                                    <Label
                                                        FontAttributes="Bold"
                                                        FontSize="14"
                                                        Text="{Binding Subject}" **<--ObservableCollection Jobs**
                                                        TextColor="LightGray"
                                                        VerticalTextAlignment="End"/>
                                                    <StackLayout
                                                        HorizontalOptions="StartAndExpand"
                                                        Orientation="Horizontal"
                                                        VerticalOptions="Start">
                                                        <Label
                                                            FontAttributes="Bold"
                                                            FontSize="12"
                                                            HorizontalOptions="Start"
                                                            Text="{Binding Date_Start}" **<--ObservableCollection Dates**
                                                            TextColor="LightGray"
                                                            VerticalTextAlignment="Start"/>
                                                        <Label
                                                            FontAttributes="Bold"
                                                            FontSize="12"
                                                            HorizontalOptions="StartAndExpand"
                                                            Text="{Binding Hour_Start}" **<--ObservableCollection Dates**
                                                            TextColor="LightGray"
                                                            VerticalTextAlignment="Start"/>
                                                    </StackLayout>
                                                </StackLayout>
                                                <Image
                                                    Grid.Column="2"
                                                    HorizontalOptions="StartAndExpand"
                                                    Source="ChevronRight"
                                                    VerticalOptions="Center"
                                                    WidthRequest="20"/>
                                            </Grid>
                                        </ViewCell>
                                    </DataTemplate>
                                </ListView.ItemTemplate>
                            </ListView>
    

    ViewModel:

        public class UserHomeViewModel : BaseViewModel
        {
            #region Services
            private ApiService apiService;
            #endregion
    
            #region Attributes
    
            private ObservableCollection<DatesItemViewModel>dates;
            private ObservableCollection<JobsItemViewModel> Jobs;
    
            private T_trabajos trabajo;
            private T_trabajocitas cita;
    
            #endregion
    
            #region Properties
    
            public List<T_jobs> JobsList { get; set; }
            public List<T_jobdates> CitasList { get; set; }
    
            public T_trabajos Job
            {
                get { return this.job; }
                set { SetValue(ref this.job, value); }
            }
            public T_trabajocitas Date
            {
                get { return this.date; }
                set { SetValue(ref this.date, value); }
            }
    
            public ObservableCollection<T_trabajos> Trabajos
            {
                get { return this.trabajos; }
                set { SetValue(ref this.trabajos, value); }
            }
            public ObservableCollection<DatesItemViewModel> Dates
            {
                get { return this.citas; }
                set { this.citas = value; }
            }
    
            #endregion
    
            #region Constructors
            public UserHomeViewModel()
            {
                this.apiService = new ApiService();
                Task.Run(async () => { await this.LoadCitas(); }).Wait();
            }
    
            #endregion
    
            #region Commands
    
            public ICommand RefreshDatesCommand
            {
                get
                {                
                    return new RelayCommand(RefreshDatesList);
                }
            }
    
            #endregion
    
            #region Methods
    
            private async Task LoadDates()
            {
                this.IsRefreshing = true;
    
                var connection = await this.apiService.CheckConnection();
                if (!connection.IsSuccess)
                {
                    this.IsRefreshing = false;
                    await App.Current.MainPage.DisplayAlert(
                        Languages.Error,
                        connection.Message,
                        "OK");
                    return;
    
                }
    
                var urlApi = App.Current.Resources["UrlAPI"].ToString();
                var prefix = App.Current.Resources["UrlPrefix"].ToString();
                var controller = App.Current.Resources["UrlT_jobsController"].ToString();
    
                var response = await this.apiService.GetList<T_Jobs>(urlApi, prefix, controller);
                if (!response.IsSuccess)
                {
                    this.IsRefreshing = true;
                    await App.Current.MainPage.DisplayAlert(
                        Languages.Error,
                        response.Message,
                        "OK");
                    return;
                }
                var joblist = (List<T_Jobs>)response.Result;
                this.JobsList = joplist.Where(c => c.Id_Customer == this.customer.Id_Customer).ToList();
    
                var date = this.DatesList.Select(c => new DatesItemViewModel
                {
                    Id_Job = c.Id_Job,
                    Id_Customer = c.Id_Customer,
                    subject = c.subject,
                    Id_local = c.Id_local,
                    Strat_Price = c.Strat_Price,
                    Advance_Price = c.Advance_Price,
                });
    
                this.Dates = new ObservableCollection<DatesItemViewModel>(date.OrderBy(c => c.F_Start));
    
                controller = App.Current.Resources["UrlT_jobdatesController"].ToString();
    
                response = await this.apiService.GetList<T_JobDates>(urlApi, prefix, controller);
                if (!response.IsSuccess)
                {
                    this.IsRefreshing = true;
                    await App.Current.MainPage.DisplayAlert(
                        Languages.Error,
                        response.Message,
                        "OK");
                    return;
                }
                var list = (List<T_JobDates>)response.Result;
                this.DatesList = list.Where(c => c.Id_Customer == this.customer.Id_Customer).ToList();
    
                var date = this.DatesList.Select(c => new DatesItemViewModel
                {
                    Id_Date = c.Id_Date,
                    Id_Job = c.Id_Job,
                    Id_Customer = c.Id_Customer,
                    Id_Artist = c.Id_Artist,
                    Date_Start = c.Date_Start,
                    Hour_Start = c.Hour_Start,
                    Date_End = c.Date_End,
                    Hour_End = c.Hour_End,
    
                });
    
                this.Dates = new ObservableCollection<DatesItemViewModel>(date.OrderBy(c => c.F_Start));
    
                this.IsRefreshing = false;
    
            }
            public void RefreshCitaList()
            {
                Task.Run(async () => { await this.LoadDates(); }).Wait();
            }
            #endregion
            }
    
            #endregion
    
        }
    

    As you can see, I'm using a web service with a Azure SQL Database connection, so the ObservableCollections only get data from the DataBase.

    I hope tha code helps to understand what I want...

    Thanks

  • Enrique4toEnrique4to Member ✭✭

    @yelinzh said:
    You can create a new Model class and put the two model in the class. Then you just need to create and bind one ObservableCollection.
    As following code shows:

    Model.cs

    namespace App6
    {
        public class Model_Collection
        {
            public Model_1 Model_1 { get; set; }
    
            public Model_2 Model_2 { get; set; }
    
        }
    
        public class Model_1 {
            public string Text { get; set; }
        }
    
        public class Model_2
        {
            public string Content { get; set; }
        }
    }
    

    page.xaml.cs

    public partial class Page4 : ContentPage
    {
        ObservableCollection<Model_Collection> list = new ObservableCollection<Model_Collection>();
        public Page4()
        {
            InitializeComponent();
    
            list.Add(new Model_Collection() { Model_1 = new Model_1 { Text = "text_1" }, Model_2 = new Model_2 { Content = "content_1" } });
            list.Add(new Model_Collection() { Model_1 = new Model_1 { Text = "text_2" }, Model_2 = new Model_2 { Content = "content_2" } });
            list.Add(new Model_Collection() { Model_1 = new Model_1 { Text = "text_3" }, Model_2 = new Model_2 { Content = "content_3" } });
    
            listview.ItemsSource = list;
        }
    }
    

    page.xaml

    <StackLayout>
        <Label Text="Two ObservableCollection Binding" />
        <ListView x:Name="listview">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*"/>
                                <RowDefinition Height="*"/>
                            </Grid.RowDefinitions>
                            <Label x:Name="label1" Text="{Binding Model_1.Text}" HorizontalOptions="CenterAndExpand" Grid.Row="0" Grid.Column="0"/>
                            <Label x:Name="label" Text="{Binding Model_2.Content}" HorizontalOptions="CenterAndExpand" Grid.Row="1" Grid.Column="0"/>
                        </Grid>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
    

    It doesn't works for me... can you explain it with WebService connected to an Azure SQL DataBase?
    Thanks!!

  • Enrique4toEnrique4to Member ✭✭

    @voidstream said:

    @Enrique4to said:
    > @JamesLavery said:
    > You will need to combine the two collections into one and bind to that.

    I'm Sorry, I'm kind of new on this, can you show me how to do that, 'couse I'm try observablecollection.Union, but it doesn't works....

    If you just use Add/Remove from the ObservableCollection you don't need to Notify your view, but if you write "your Collection = anything" you should implement INotifyPropertyChanged. Don't forget this, this rules is for all properties binded :smile:

    public class MyViewModel : INotifyPropertyChanged
    {
    public event PropertyChangedEventHandler PropertyChanged;

    private ObservableCollection _myCollection;
    public ObservableCollection MyCollection
    {
    get => _myCollection
    set => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyCollection)));
    }
    }

    You can use a Framework (like PRISM) for easier implementation and for others features

    I'm already using this as BaseViewModel... Thanks.

  • voidstreamvoidstream FRMember ✭✭✭

    @Enrique4to said:

    @voidstream said:

    @Enrique4to said:
    > @JamesLavery said:
    > You will need to combine the two collections into one and bind to that.

    I'm Sorry, I'm kind of new on this, can you show me how to do that, 'couse I'm try observablecollection.Union, but it doesn't works....

    If you just use Add/Remove from the ObservableCollection you don't need to Notify your view, but if you write "your Collection = anything" you should implement INotifyPropertyChanged. Don't forget this, this rules is for all properties binded :smile:

    public class MyViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        private ObservableCollection<object> _myCollection;
        public ObservableCollection<object> MyCollection
        {
            get => _myCollection
            set => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyCollection)));
        }       
    }
    

    You can use a Framework (like PRISM) for easier implementation and for others features

    I'm already using this as BaseViewModel... Thanks.

    It's fixed? If not care about the current thread.

    You are calling Task.Run(), result you are leaving the main thread (UI Thread). You should use BeginInvokeOnMainThread when your code is firing the UI. For example

    Device.BeginInvokeOnMainThread(() =>
    {
        this.Dates = new ObservableCollection<DatesItemViewModel>(date.OrderBy(c => c.F_Start));
         this.IsRefreshing = false;
    }
    

    Don't use .Wait()
    Use configureAwait(false) if you don't need switch context

    More, watch this:
    https://docs.microsoft.com/en-US/dotnet/api/system.threading.cancellationtokensource?view=netframework-4.8
    https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource-1?view=netframework-4.8

  • voidstreamvoidstream FRMember ✭✭✭
    @Enrique4to You should inject your ApiService by constructor to your viewmodel (Dependency injection vu constructor). Have fun 😊
Sign In or Register to comment.