Xamarin Forms Picker触发两次事件致SelectedId Setter执行两次问题
Picker绑定SelectedItem时setter执行两次并传入null的问题分析与解决
问题场景
你定义的Picker组件:
<Picker Title="Identifier" ItemsSource="{Binding IdentifyUsing}" SelectedItem="{Binding SelectedId}" />
ViewModel中SelectedId的实现:
public string SelectedId { get => _selectedId; set { SetProperty(ref _selectedId, GetAppropriateLabel(value)); // 此代码执行两次 IsEnabled = true; } }
GetAppropriateLabel方法:
private string GetAppropriateLabel(string value) { if (value.Contains("Member")) return "Member ID (ex: 999999999)"; else if (value.Contains("Social")) return "Social Security Number (ex: 999-99-9999)"; else if (value.Contains("StudentId")) return "Medicare Number (ex: ********-**)"; else return value; }
当选择属于if-else分支的选项时,SelectedId的setter会执行两次,第二次传入null导致崩溃;选择其他选项则正常。
问题原因
核心是双向绑定的匹配逻辑冲突:
- 第一次调用:Picker将选中的原始值(比如"Member")传入
SelectedId的setter。 - 你在setter里通过
GetAppropriateLabel把原始值转换成了一个全新的字符串(比如"Member ID (ex: 999999999)"),这个值并不存在于Picker的ItemsSource(IdentifyUsing集合)中。 - Picker的双向绑定机制会验证
SelectedItem的值是否在自己的选项列表里——因为转换后的标签不在集合中,Picker无法匹配到对应的选项,就会自动把SelectedItem设为null,从而触发第二次setter调用,传入null。 - 当选择不需要转换的选项时,
GetAppropriateLabel返回原始值,该值存在于ItemsSource中,Picker能正常匹配,所以不会触发第二次调用。
解决方案
方案1:拆分绑定字段,分离选中值与显示标签
在ViewModel中新增两个属性,一个存储原始选中值,一个存储转换后的显示标签:
private string _selectedOriginalId; public string SelectedOriginalId { get => _selectedOriginalId; set { if (SetProperty(ref _selectedOriginalId, value)) { SelectedIdLabel = GetAppropriateLabel(value); IsEnabled = true; } } } private string _selectedIdLabel; public string SelectedIdLabel { get => _selectedIdLabel; set => SetProperty(ref _selectedIdLabel, value); }
修改XAML绑定,Picker绑定原始值,用单独的控件显示标签:
<Picker Title="Identifier" ItemsSource="{Binding IdentifyUsing}" SelectedItem="{Binding SelectedOriginalId}" /> <TextBlock Text="{Binding SelectedIdLabel}" Margin="0,8,0,0" />
方案2:使用实体类作为ItemsSource的元素类型
定义包含原始值和显示标签的实体类:
public class IdentifierOption { public string Value { get; set; } public string Label { get; set; } }
构建IdentifyUsing集合时预先处理好每个选项的标签:
public ObservableCollection<IdentifierOption> IdentifyUsing { get; } = new ObservableCollection<IdentifierOption> { new IdentifierOption { Value = "Member", Label = "Member ID (ex: 999999999)" }, new IdentifierOption { Value = "Social", Label = "Social Security Number (ex: 999-99-9999)" }, new IdentifierOption { Value = "StudentId", Label = "Medicare Number (ex: ********-**)" }, // 其他选项... };
修改Picker的绑定,指定显示的属性:
<Picker Title="Identifier" ItemsSource="{Binding IdentifyUsing}" DisplayMemberName="Label" SelectedItem="{Binding SelectedIdentifier}" />
ViewModel中的选中属性改为实体类类型:
private IdentifierOption _selectedIdentifier; public IdentifierOption SelectedIdentifier { get => _selectedIdentifier; set { if (SetProperty(ref _selectedIdentifier, value)) { IsEnabled = true; // 需要原始值时直接访问 SelectedIdentifier.Value } } }
内容的提问来源于stack exchange,提问作者Harsh
相关产品推荐
相关产品推荐

