Picker SelectedItem property setter not being hit for custom class (works for strings)

NixerNixer Member ✭✭

Hi
I'm trying to get a picker working for a custom class. My ItemsSource binding works fine but when I select an item the breakpoint I have in the property setter is not hit.

I looked at the MonkeyApp sample and copied some code from that (I can't run the sample directly as it needs Windows 10 and I'm on W7 using VS Community 2017) but I'm having the same problem with the sample code.

I implemented another picker to select from a list of strings where SelectedItem is bound to a public string property, for this picker the property setter breakpoint is hit.

I read something that suggested older versions of the Picker/Xamarin forms might not work so I used Nuget to install Xamarin.Forms 3.1.0.583944. Still doesn't work.

xaml:

        <Picker Title="string list" ItemsSource="{Binding StringList}" SelectedItem="{Binding SelectedString}"/>
        <Label Text="{Binding SelectedString}"/>

        <Picker Title="Select a monkey" ItemsSource="{Binding Monkeys}" ItemDisplayBinding="{Binding Name}" 
                SelectedItem="{Binding SelectedMonkey, Mode=TwoWay}" />
        <Label Text="{Binding SelectedMonkey.Location}" FontAttributes="Italic" HorizontalOptions="Center" />

viewmodel:

    string selectedString;
    public string SelectedString
    {
        get
        {
            return selectedString;
        }
        set
        {
            selectedString = value;
            OnPropertyChanged();
        }
    }

    Monkey selectedMonkey;
    public Monkey SelectedMonkey
    {
        get { return selectedMonkey; }
        set
        {
            if (selectedMonkey != value)
            {
                selectedMonkey = value;
                OnPropertyChanged();
            }
        }
    }

ViewModelBase class:

public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

Can anyone tell me where I am going wrong?
Thanks in advance.

Answers

  • LandLuLandLu Member, Xamarin Team Xamurai

    How do you construct your viewmodel and set the StringList, Monkeys property? Can you share your sample to help us reproduce your issue? MonkeyApp works properly on my side.

  • NixerNixer Member ✭✭

    Hi LandLu. Thanks for replying and sorry for the delay in responding - this is a hobby project and I haven't had any free time to spend on it for a while.

    I've managed to get the MonkeyApp demo that I downloaded running in my environment now (I got rid of the IOS and UWP projects and just kept the Droid one) and it has exactly the same problem, the SelectedMonkey property setter is not hit and therefore the labels bound to it don't update. I've modified it slightly to add a second Picker of type string with a corresponding label for its SelectedString and that one is working fine.

    Here's a cut and paste of relevant bits of code. I will try to upload a .zip of the whole solution as well.

    MonkeysPage.xaml

        <?xml version="1.0" encoding="UTF-8"?>
    





    code behind of MonkeysPage that constructs the view model:

    public partial class MonkeysPage : ContentPage
    {
        public MonkeysPage()
        {
            InitializeComponent();
            BindingContext = new MonkeysPageViewModel();
        }
    }
    

    MonkeysPageViewModel:

    public class MonkeysPageViewModel : ViewModelBase
    {
        public IList<string> StringList =>  new List<string>(new string[] { "a", "b" });
        string  selectedString;
        public string SelectedString
        {
            get { return selectedString; }
            set
            {
                if (selectedString != value)
                {
                    selectedString = value;
                    OnPropertyChanged();
                }
            }
        }
    
        public IList<Monkey> Monkeys { get { return MonkeyData.Monkeys; } }
    
        Monkey selectedMonkey;
        public Monkey SelectedMonkey
        {
            get { return selectedMonkey; }
            set
            {
                if (selectedMonkey != value)
                {
                    selectedMonkey = value;
                    OnPropertyChanged();
                }
            }
        }
    }
    

    MonkeyData class (not posted all of it - you get the idea and I haven't changed it so should be same as the demo)

    public static class MonkeyData
        {
            public static IList<Monkey> Monkeys { get; private set; }
    
            static MonkeyData()
            {
                Monkeys = new List<Monkey>();
    
                Monkeys.Add(new Monkey
                {
                    Name = "Baboon",
                    Location = "Africa & Asia",
                    Details = "Baboons are African and Arabian Old World monkeys belonging to the genus Papio, part of the subfamily Cercopithecinae.",
                    ImageUrl = ""
                });
    
                Monkeys.Add(new Monkey
                {
                    Name = "Capuchin Monkey",
                    Location = "Central & South America",
                    Details = "The capuchin monkeys are New World monkeys of the subfamily Cebinae. Prior to 2011, the subfamily contained only a single genus, Cebus.",
                    ImageUrl = ""
                });
    

    Monkey class:

    public class Monkey
    {
        public string Name { get; set; }
        public string Location { get; set; }
        public string Details { get; set; }
        public string ImageUrl { get; set; }
    }
    

    ViewModelBase class:

    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    I think the problem is something in my environment so I've also changed the Droid project to compile using Android 8.1 (it was 7.1 previously) and updated all Nuget packages which hasn't made any difference. I'm now using Xamarin.Forms 3.1.0.697729

  • NixerNixer Member ✭✭
    edited August 2018

    Hi LandLu, sorry for the delay in replying. This is a hobby project and I haven't had any free time lately.

    I've got the MonkeyApp demo running in my environment now (got rid of the UWC and IOS projects and just kept the Droid one) and it's got exactly the same problem. I've modified it to add a string property and label and this one works fine.

    Here's the MonkeysPage. xaml (I've had to chop out the xml namespaces because this forum won't let me post links:

        <?xml version="1.0" encoding="UTF-8"?>
    <ContentPage xmlns="/schemas/2014/forms" xmlns:x="schemas.microsoft.com/winfx/2009/xaml" x:Class="MonkeyApp.MonkeysPage">
        <ScrollView>
            <StackLayout Margin="20">
                <Picker Title="Strings" ItemsSource="{Binding StringList}" SelectedItem="{Binding SelectedString}"/>
                <Label Text="{Binding SelectedString}"/>            
                <Label Text="Monkeys" FontAttributes="Bold" HorizontalOptions="Center" />
                <Picker Title="Select a monkey" ItemsSource="{Binding Monkeys}" ItemDisplayBinding="{Binding Name}" SelectedItem="{Binding SelectedMonkey}" />
                <Label Text="{Binding SelectedMonkey.Name}" HorizontalOptions="Center" Style="{DynamicResource TitleStyle}" />
                <Label Text="{Binding SelectedMonkey.Location}" FontAttributes="Italic" HorizontalOptions="Center" />
                <Image Source="{Binding SelectedMonkey.ImageUrl}" HeightRequest="200" WidthRequest="200" HorizontalOptions="CenterAndExpand" />
                <Label Text="{Binding SelectedMonkey.Details}" Style="{DynamicResource BodyStyle}" />
            </StackLayout>
        </ScrollView>
    </ContentPage>
    

    and its code behind:

    public partial class MonkeysPage : ContentPage
    {
        public MonkeysPage()
        {
            InitializeComponent();
            BindingContext = new MonkeysPageViewModel();
        }
    }
    

    and the MonkeysPageViewModel:

    public class MonkeysPageViewModel : ViewModelBase
    {
            public IList<string> StringList =>  new List<string>(new string[] { "a", "b" });
            string  selectedString;
            public string SelectedString
            {
                get { return selectedString; }
                set
                {
                    if (selectedString != value)
                    {
                        selectedString = value;
                        OnPropertyChanged();
                    }
                }
            }
    
            public IList<Monkey> Monkeys { get { return MonkeyData.Monkeys; } }
    
            Monkey selectedMonkey;
            public Monkey SelectedMonkey
            {
                get { return selectedMonkey; }
                set
                {
                    if (selectedMonkey != value)
                    {
                        selectedMonkey = value;
                        OnPropertyChanged();
                    }
                }
            }
        }
    
  • NixerNixer Member ✭✭

    I updated the Droid project to use Android 8.1 (was previously 7.1) which allowed me to update the Nuget packages. So I am now using Xamarin.Forms 3.1.0.697729.

Sign In or Register to comment.