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。我以前的回答现在与新问题无关,对任何人都没有好处。
对于你的新问题,你的 Quantity
是 int
类型,你的选择器项目是 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));
}
}
让我知道这是否能让你前进
我的产品页面只显示'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。我以前的回答现在与新问题无关,对任何人都没有好处。
对于你的新问题,你的 Quantity
是 int
类型,你的选择器项目是 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));
}
}
让我知道这是否能让你前进