Forum Xamarin.Forms
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

Bind an ObservableCollectoin from a Listview to a pickers itemssource

mndevelopmentsmndevelopments Member ✭✭
edited October 17 in Xamarin.Forms

Hello

I've just switched from blank Xamarin.forms to Xamarin.forms with MVVM and I'm having trouble transferring a list from one view to another.

The list view gets items from database and stores new items there as well, and I want to bind the a pickers itemssource to that list, displaying the name of each item.

After that, when you select and item from the picker, it should post the data too to different labels - 1. displaying the name, and 2. displaying an value.

**The model the list is based on is: **

using System;

namespace LommeKonstruktoren.Models
    {
        public class Item
        {
            public string Id { get; set; }
            public string Name { get; set; }
            public string UValue { get; set; }
            public string Description { get; set; }

        }
    }

**And I'm calling the items in the ListViews ViewModel like so: **

namespace LommeKonstruktoren.ViewModels
{
    public class ItemsViewModel : BaseViewModel
    {
        private Item _selectedItem;

        public ObservableCollection<Item> Items { get; }
        public Command LoadItemsCommand { get; }
        public Command AddItemCommand { get; }
        public Command<Item> ItemTapped { get; }

        public ItemsViewModel()
        {
            Title = "Bygningsdele";
            Items = new ObservableCollection<Item>();
            LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());

            ItemTapped = new Command<Item>(OnItemSelected);

            AddItemCommand = new Command(OnAddItem);
        }

        async Task ExecuteLoadItemsCommand()
        {


            try
            {
                Items.Clear();
                var items = await DataStore.GetItemsAsync(true);
                foreach (var item in items)
                {
                    Items.Add(item);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
            finally
            {
                IsBusy = false;
            }


        }

        public void OnAppearing()
        {
            IsBusy = true;
            SelectedItem = null;
        }

        public Item SelectedItem
        {
            get => _selectedItem;
            set
            {
                SetProperty(ref _selectedItem, value);
                OnItemSelected(value);
            }
        }

        private async void OnAddItem(object obj)
        {
            await Shell.Current.GoToAsync(nameof(NewItemPage));


        }

        async void OnItemSelected(Item item)
        {
            if (item == null)
                return;

            // This will push the ItemDetailPage onto the navigation stack
            await Shell.Current.GoToAsync($"{nameof(ItemDetailPage)}?{nameof(ItemDetailViewModel.ItemId)}={item.Id}");
        }
    }
}

**In my page with the picker I'm trying above ExecuteLoadItemCommand as well, with the following XAML: **

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage Title="U-værdiberegner" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="LommeKonstruktoren.Views.UValuePage" xmlns:local="clr-namespace:LommeKonstruktoren.ViewModels" xmlns:model="clr-namespace:LommeKonstruktoren.Models" x:Name="CalculateUValueOage">

<RefreshView x:DataType="local:UValueViewModel" Command="{Binding LoadItemsCommand}">
    <StackLayout Padding="20,10" x:DataType="model:Item">

        <Label Text="{Binding Name}" FontSize="Medium" FontAttributes="Bold" />
        <Label Text="Der startes indefra" FontSize="Small" FontAttributes="Italic" /> <Button Text="Ny picker" />
        <Picker x:Name="picker1" Title="VÆLG" ItemsSource="{Binding Items}" ItemDisplayBinding="{Binding Name}"/>

        <Picker Title="Vælg bygningsdel" ItemsSource="{Binding Items}" ItemDisplayBinding="{Binding Name}" />
        <Picker Title="Vælg bygningsdel" ItemsSource="{Binding Items}" ItemDisplayBinding="{Binding .}" />
        <Picker Title="Vælg bygningsdel" ItemsSource="{Binding Items}" ItemDisplayBinding="{Binding .}" />
        <Picker Title="Vælg bygningsdel" ItemsSource="{Binding Items}" ItemDisplayBinding="{Binding .}" />
        <Picker Title="Vælg bygningsdel" ItemsSource="{Binding Items}" ItemDisplayBinding="{Binding .}" />

    </StackLayout>

</RefreshView>

</ContentPage>

How would I show the list as the pickers items in easiest way? And should I try to do this without MVVM instead?

Best Answer

  • mndevelopmentsmndevelopments Member ✭✭
    Accepted Answer

    I solved it :)! I wasn't calling the LoadItemsCommand, which I solved using a RefreshView.

    The XAML looks like this:

    <?xml version="1.0" encoding="utf-8"?>
    <ContentPage Title="{Binding Title}" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:local="clr-namespace:LommeKonstruktoren.ViewModels" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="LommeKonstruktoren.Views.UValuePage"  xmlns:model="clr-namespace:LommeKonstruktoren.Models" x:Name="BrowseUValuePage">
        <ContentPage.ToolbarItems>
            <ToolbarItem Text="Add" Command="{Binding AddItemCommand}" />
        </ContentPage.ToolbarItems>
        <!--
          x:DataType enables compiled bindings for better performance and compile time validation of binding expressions.
          https://docs.microsoft.com/xamarin/xamarin-forms/app-fundamentals/data-binding/compiled-bindings
        -->
        <RefreshView x:DataType="local:UValueViewModel" Command="{Binding LoadItemsCommand}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
            <StackLayout>
            <Label Text="Indtast bygningsdele" FontSize="Medium" FontAttributes="Bold" />
            <Label Text="Der startes indefra" FontSize="Small" FontAttributes="Italic" /> <Button Text="Ny picker" />
            <Picker x:Name="picker1" Title="VÆLG" ItemsSource="{Binding Items}" ItemDisplayBinding="{Binding Name}"/>
    
            <Picker Title="Vælg bygningsdel" ItemsSource="{Binding Items}" ItemDisplayBinding="{Binding Name}"  />
            <Picker Title="Vælg bygningsdel" ItemsSource="{Binding Items}"  ItemDisplayBinding="{Binding Name}"/>
            <Picker Title="Vælg bygningsdel" ItemsSource="{Binding Items}"  ItemDisplayBinding="{Binding Name}"/>
            <Picker Title="Vælg bygningsdel" ItemsSource="{Binding Items}"  ItemDisplayBinding="{Binding Name}"/>
            <Picker Title="Vælg bygningsdel" ItemsSource="{Binding Items}"  ItemDisplayBinding="{Binding Name}"/>
            </StackLayout>
        </RefreshView>
    </ContentPage>
    

    And the ViewModel looks like this:

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using LommeKonstruktoren.Models;
    using Xamarin.Forms;
    using LommeKonstruktoren.Services;
    using System.Diagnostics;
    using System.Threading.Tasks;
    using System.ComponentModel;
    
        namespace LommeKonstruktoren.ViewModels
        {
    
            public class UValueViewModel : BaseViewModel
            {
                private Item _selectedItem;
    
                public ObservableCollection<Item> Items { get; }
                public Command LoadItemsCommand { get; }
                public Command AddItemCommand { get; }
                public Command<Item> ItemTapped { get; }
    
                public UValueViewModel()
                {
                    Title = "U-værdi";
                    Items = new ObservableCollection<Item>();
                    LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
    
                }
    
                async Task ExecuteLoadItemsCommand()
                {
    
    
                    try
                    {
    
                        Items.Clear();
                        var items = await DataStore.GetItemsAsync(true);
    
                        foreach (var item in items)
                        {
                            Items.Add(item);
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex);
                    }
                    finally
                    {
                        IsBusy = false;
                    }
    
    
                }
    
                public void OnAppearing()
                {
                    IsBusy = true;
                }
    
                //private async void OnAddItem(object obj)
                //{
                //    await Shell.Current.GoToAsync(nameof(NewItemPage));
    
    
                //}
    
            }
        }
    

Answers

  • ShekherShekher Member
    edited October 17

    Hey @mndevelopments

    Maybe you need to update the reference of the ObservableCollection

        Items.Clear();
                var items = await DataStore.GetItemsAsync(true);
                 Items = new ObservableCollection(items.ToList());
    
  • mndevelopmentsmndevelopments Member ✭✭

    Hey @Shekher
    I've been trying to do what you are suggesting, unfortunately without luck.

    I tried playing around with the binding context of the page, and when I write in Items now, it seems like the page is trying to read "Items" from the Item model, and not the observable collection, Items.

    Shouldn't I just be able to retrieve the list from the database? Would it be better to create the app without viewmodels, and just grab the list of items from an SQLite table instead?

  • mndevelopmentsmndevelopments Member ✭✭
    Accepted Answer

    I solved it :)! I wasn't calling the LoadItemsCommand, which I solved using a RefreshView.

    The XAML looks like this:

    <?xml version="1.0" encoding="utf-8"?>
    <ContentPage Title="{Binding Title}" xmlns="http://xamarin.com/schemas/2014/forms" xmlns:local="clr-namespace:LommeKonstruktoren.ViewModels" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="LommeKonstruktoren.Views.UValuePage"  xmlns:model="clr-namespace:LommeKonstruktoren.Models" x:Name="BrowseUValuePage">
        <ContentPage.ToolbarItems>
            <ToolbarItem Text="Add" Command="{Binding AddItemCommand}" />
        </ContentPage.ToolbarItems>
        <!--
          x:DataType enables compiled bindings for better performance and compile time validation of binding expressions.
          https://docs.microsoft.com/xamarin/xamarin-forms/app-fundamentals/data-binding/compiled-bindings
        -->
        <RefreshView x:DataType="local:UValueViewModel" Command="{Binding LoadItemsCommand}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
            <StackLayout>
            <Label Text="Indtast bygningsdele" FontSize="Medium" FontAttributes="Bold" />
            <Label Text="Der startes indefra" FontSize="Small" FontAttributes="Italic" /> <Button Text="Ny picker" />
            <Picker x:Name="picker1" Title="VÆLG" ItemsSource="{Binding Items}" ItemDisplayBinding="{Binding Name}"/>
    
            <Picker Title="Vælg bygningsdel" ItemsSource="{Binding Items}" ItemDisplayBinding="{Binding Name}"  />
            <Picker Title="Vælg bygningsdel" ItemsSource="{Binding Items}"  ItemDisplayBinding="{Binding Name}"/>
            <Picker Title="Vælg bygningsdel" ItemsSource="{Binding Items}"  ItemDisplayBinding="{Binding Name}"/>
            <Picker Title="Vælg bygningsdel" ItemsSource="{Binding Items}"  ItemDisplayBinding="{Binding Name}"/>
            <Picker Title="Vælg bygningsdel" ItemsSource="{Binding Items}"  ItemDisplayBinding="{Binding Name}"/>
            </StackLayout>
        </RefreshView>
    </ContentPage>
    

    And the ViewModel looks like this:

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using LommeKonstruktoren.Models;
    using Xamarin.Forms;
    using LommeKonstruktoren.Services;
    using System.Diagnostics;
    using System.Threading.Tasks;
    using System.ComponentModel;
    
        namespace LommeKonstruktoren.ViewModels
        {
    
            public class UValueViewModel : BaseViewModel
            {
                private Item _selectedItem;
    
                public ObservableCollection<Item> Items { get; }
                public Command LoadItemsCommand { get; }
                public Command AddItemCommand { get; }
                public Command<Item> ItemTapped { get; }
    
                public UValueViewModel()
                {
                    Title = "U-værdi";
                    Items = new ObservableCollection<Item>();
                    LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
    
                }
    
                async Task ExecuteLoadItemsCommand()
                {
    
    
                    try
                    {
    
                        Items.Clear();
                        var items = await DataStore.GetItemsAsync(true);
    
                        foreach (var item in items)
                        {
                            Items.Add(item);
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex);
                    }
                    finally
                    {
                        IsBusy = false;
                    }
    
    
                }
    
                public void OnAppearing()
                {
                    IsBusy = true;
                }
    
                //private async void OnAddItem(object obj)
                //{
                //    await Shell.Current.GoToAsync(nameof(NewItemPage));
    
    
                //}
    
            }
        }
    
Sign In or Register to comment.