xamarin 表单 - 与选择器的双向绑定 - 为什么我不能从后面的代码更新选择器?

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

我的产品页面只显示'Product Name'和'Quantity',数量显示/绑定到选择器。

出于测试目的以使其正常工作,仅从 VM 加载了 2 个产品。酒 1 和酒 2.

应用程序加载时,为什么选取器为空且没有值 selected。当每个项目的数量设置为 1 时,从 VM 加载时

数量设置为 1,选择器在最初加载时不会更新,我知道这一点是因为如果我单击空选择器并且 select 1. 没有任何反应,因为代码命中

if (quantity != value)

// selected 中的数量 1 在后面的代码中已经是 1,因此不会从数量 setter 中调用 propertyChanged,

还有... 如果我 select 选择器并选择另一个数字,例如 4。数量 setter 命中,OnPropertyChanged 命中,选择器上显示 4。 (如果选择了 3,我什至测试过更改 productName) 这有效。

我知道这一切都有效,因为我已经逐步完成了代码。 然而,现在发生的另一个问题是,出于某种原因,代码再次点击数量 get/set 并将值设置为 0,每次点击后。

因此,例如,如果单击 4,选择器将在屏幕上更新为 4,然后如果我在代码中逐步执行,则会再次调用数量中的 setter,将值设置为0,因此 select 在选择器中什么也没有。将其留空,就像它最初加载时一样。

任何有助于解决此问题的帮助都将不胜感激谢谢 Y

感谢 Y 提供的任何帮助

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();
        }
    }
}

您的 ProductModel class 没有继承自 INotifyPropertyChanged 接口,您需要将接口添加到 class 并实现它, 还要确保在 Quantity setter.

中引发 INotifyPropertyChanged.PropertyChanged 事件

此时您可能想要创建一个 ProductViewModel,因为我不确定您是否想要将 INotifyPropertyChanged 添加到 POCO class,例如 ProductModel


请不要用不同的问题编辑您的问题,打开一个新问题。您之前的问题可能帮助其他人面临同样的问题,即在更改不继承自 IPropertyChanged 的 class 的 属性 时未更新 UI。我以前的回答现在与新问题无关,对任何人都没有好处。

对于你的新问题,你的 Quantityint 类型,你的选择器项目是 string 类型,将 Quantity 类型更改为 string应该有助于解决您的问题,如果项目的索引匹配,您也可以使用 SelectedIndex 而不是 SelectedItem

<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>

看起来您已经在 ProductModel 上正确实现了 INotifyPropertyChanged,但现在您需要在 ProductModel class 的构造函数中订阅它。

我已经对您正在寻找的内容进行了简单的实现(我排除了您的其余代码,以便更容易理解)

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));
        }
    }

让我知道这是否能让你前进