简体   繁体   中英

xamarin forms - two way binding with a picker - why cant I update a picker from code behind?

My products page only shows 'Product Name' and 'Quantity', quantity isdisplayed/ binded to the picker.

For test purposes to get this working there is only 2 products loading from the VM. Wine 1 and wine 2.

When the application loads, why is the picker empty with no value selected. When quantity for each item is set to 1, when loading from the VM

Quantity is set to 1, the picker is just not updating when it initially loads, I know this because if I click on the empty picker and select 1. nothing happens because the code hits

if (quantity != value)

// where quantity 1 being selected is already 1 in code behind, so wont call propertyChanged from setter in quantity,

also... if I select the picker and choose another number, say 4 for example. the setter in quantity is hit and OnPropertyChanged is hit, and 4 is displayed on the picker. (I even tested changing the productName if 3 was picked) and this worked.

I know this all works as I have stepped through the code. However another issue that is now happening is that for some reason the code is hitting the quantity get/set AGAIN and setting the value to 0, after everytime it is clicked.

So for example if 4 is clicked, the picker will be updated to 4 on screen, then if I follow it through stepping in the code, the setter in quantity is called again, setting the value to 0, and thus selecting nothing in the picker. Leaving it blank, as it is when it initially loads.

any help to help resolve this would be appreciated thank Y

thank Y for any help received

public class ProductModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private int quantity;
        private string productName;

        public ProductModel()
        { }

        [PrimaryKey, AutoIncrement]
        public int ProductId { get; set; }

        [MaxLength(50)]
        public string ProductName
        {
            get
            {
                return productName;
            }
            set
            {
                productName = value;
                OnPropertyChanged();
            }
        }

        public int Quantity
        {
            get
            {
                return quantity;
            }
            set
            {
                if (quantity != value)
                {
                    quantity = value;
                    OnPropertyChanged();

                    //test to check if binding works, successfully changed ProductName to "test" if 3 is picked from picker
                    if (quantity == 3)
                        ProductName = "test;";
                }

            }
        }
        protected void OnPropertyChanged([CallerMemberName] string name = null)
        {
            var changed = PropertyChanged;
            if (changed == null)
                return;

            changed.Invoke(this, new PropertyChangedEventArgs(name));
        }

    }
    public class ProductPageViewModel : BindableObject
        {
           public ObservableCollection<ProductModel> WineList { get; set; }
            public ProductPageViewModel ()
            {
                WineList = new ObservableCollection<ProductModel>();
                WineList.Add(new ProductModel { ProductId = 1, ProductName = "Wine 1", Quantity = 1});
                WineList.Add(new ProductModel { ProductId = 2, ProductName = "Wine 2", Quantity = 1});
            }
   
        
    
    
 <?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ScrollApp2.Views.ProductPage">
    <ContentPage.Content>
        <StackLayout>
         
            <ListView  x:Name="producttablelist" IsVisible="True" VerticalOptions="FillAndExpand" HasUnevenRows="True" ItemsSource="{Binding WineList}" HeightRequest="1500">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <StackLayout HeightRequest="120" BackgroundColor="Green" HorizontalOptions="StartAndExpand">
                                <Grid>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto"/>
                                        <RowDefinition Height="Auto"/>
                                    </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <Label Grid.Row="0" Grid.Column="0" Text="{Binding ProductName}" TextColor="Black" VerticalOptions="Start"></Label>

                                    <Picker Grid.Column="1" Grid.Row="0" SelectedItem="{Binding Quantity,Mode=TwoWay}">
                                        <Picker.Items>
                                            <x:String>0</x:String>
                                            <x:String>1</x:String>
                                            <x:String>2</x:String>
                                            <x:String>3</x:String>
                                            <x:String>4</x:String>
                                            <x:String>5</x:String>
                                            <x:String>6</x:String>
                                        </Picker.Items>
                                    </Picker>
                                </Grid>
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

            
        </StackLayout>
       
    </ContentPage.Content>
</ContentPage>

    
   namespace ScrollApp2.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class ProductPage : ContentPage
    {
        public ProductPage()
        {
            InitializeComponent();
            BindingContext = new ProductPageViewModel();
        }
    }
}

Your ProductModel class does not inherit from INotifyPropertyChanged interface, you'll need to add the interface to your class and implement it, also making sure to raise the INotifyPropertyChanged.PropertyChanged event in Quantity setter.

\n

You might want to create a ProductViewModel at this point, as I'm not sure you want to add INotifyPropertyChanged to a POCO class like ProductModel .


Please don't edit your question with a different one, open a new question. Your previous question might have helped someone else facing the same problem of UI not being updated when changing a property of a class that does not inherit from IPropertyChanged . My previous answer is now irrelevant to the new question, and will never benefit anyone else.

For your new question, your Quantity is of type int and your picker items are of type string , changing Quantity type to string should help solving your issue, you can also use SelectedIndex instead of SelectedItem if the index of the items are matching.

<Picker SelectedIndex="{Binding Quantity, Mode=TwoWay}">
    <Picker.Items>
        <x:String>0</x:String>
        <x:String>1</x:String>
        <x:String>2</x:String>
        <x:String>3</x:String>
        <x:String>4</x:String>
        <x:String>5</x:String>
        <x:String>6</x:String>
    </Picker.Items>
</Picker>

It looks like you have properly implemented INotifyPropertyChanged on your ProductModel , but now you need to subscribe to it in the constructor of the ProductModel class.

I have stubbed up a simple implementation of what it is that you are looking for (I excluded the rest of your code so that it would be easier to digest)

public class ProductModel : INotifyPropertyChanged {
        
        //Event
        public event PropertyChangedEventHandler PropertyChanged;

        //Fields
        private string _ProductName;
        private int _Quantity;

        //Properties
        public int Quantity {
            get { return _Quantity; }
            set {
                _Quantity = value;
                OnPropertyChanged();
            }
        }

        public string ProductName {
            get { return _ProductName; }
            set {
                _ProductName = value;
                OnPropertyChanged();
            }
        }


        //Constructor
        public ProductModel() {
            //Subscription
            this.PropertyChanged += OnPropertyChanged;
        }

        //OnPropertyChanged
        private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) {
            if (e.PropertyName == nameof(Quantity)) {
                //Do anything that needs doing when the Quantity changes here...
            }
        }

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

Let me know if this gets you going

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM