如何更新Picker的ItemsSource列表并避免无限循环?
问题分析
当你调用OnPropertyChanged(nameof(PickerItemList))更新列表时,Picker会重新加载整个ItemsSource。此时原SelectedLanguage对应的KeyValuePair实例已不在新列表中(新列表是从更新后的字典生成的全新实例),导致Picker重新设置SelectedItem,再次触发SelectedLanguage的setter,形成无限循环。
解决方案
方案1:使用ObservableCollection更新单个元素(推荐)
将PickerItemList改为ObservableCollection<KeyValuePair<string, string>>,直接更新集合内的元素而非替换整个集合,避免触发ItemsSource的完全重新绑定,从根源上防止循环。
修改后的ViewModel代码:
public partial class SettingsViewModel : ObservableObject { private static ILocalizationResourceManager LocalizationResourceManager; // 替换为ObservableCollection,利用CommunityToolkit的ObservableProperty简化绑定 [ObservableProperty] private ObservableCollection<KeyValuePair<string, string>> _pickerItemList; private KeyValuePair<string, string> _selectedLanguage; public KeyValuePair<string, string> SelectedLanguage { get => _selectedLanguage; set { if (_selectedLanguage.Equals(value)) return; _selectedLanguage = value; if (!string.IsNullOrEmpty(value.Key)) { LocalizationResourceManager.CurrentCulture = new CultureInfo(value.Key); // 直接更新集合中的对应元素,而非替换整个集合 UpdatePickerItems(); // 更新SelectedLanguage为集合中的新实例,保证显示匹配 _selectedLanguage = PickerItemList.First(x => x.Key == value.Key); OnPropertyChanged(nameof(SelectedLanguage)); } } } public SettingsViewModel(ILocalizationResourceManager localizationResourceManager) { LocalizationResourceManager = localizationResourceManager; // 初始化ObservableCollection PickerItemList = new ObservableCollection<KeyValuePair<string, string>> { new("de", LocalizationResourceManager["German"]), new("en", LocalizationResourceManager["English"]) }; if (Preferences.ContainsKey(Constants.ApplicationLanguage)) { var langSetting = Preferences.Default.Get(Constants.ApplicationLanguage, "en"); SelectedLanguage = PickerItemList.First(x => x.Key == langSetting); } } private void UpdatePickerItems() { // 更新德语项 var germanIndex = PickerItemList.IndexOf(PickerItemList.First(x => x.Key == "de")); PickerItemList[germanIndex] = new KeyValuePair<string, string>("de", LocalizationResourceManager["German"]); // 更新英语项 var englishIndex = PickerItemList.IndexOf(PickerItemList.First(x => x.Key == "en")); PickerItemList[englishIndex] = new KeyValuePair<string, string>("en", LocalizationResourceManager["English"]); } }
方案2:使用标志位防止递归触发setter
如果坚持使用List,可通过添加更新标志位,在更新集合时跳过setter逻辑,避免无限循环。
修改后的ViewModel代码:
public partial class SettingsViewModel : ObservableObject { private static ILocalizationResourceManager LocalizationResourceManager; private Dictionary<string, string> LanguageDictionary { get; set; } // 防止递归的标志位 private bool _isUpdating; public List<KeyValuePair<string, string>> PickerItemList { get => LanguageDictionary.ToList(); } private KeyValuePair<string, string> _selectedLanguage; public KeyValuePair<string, string> SelectedLanguage { get => _selectedLanguage; set { // 如果正在更新,直接返回,避免递归 if (_isUpdating) return; _selectedLanguage = value; if (!string.IsNullOrEmpty(value.Key)) { LocalizationResourceManager.CurrentCulture = new CultureInfo(value.Key); // 更新字典 LanguageDictionary.Clear(); LanguageDictionary.Add("de", LocalizationResourceManager["German"]); LanguageDictionary.Add("en", LocalizationResourceManager["English"]); _isUpdating = true; // 通知集合更新 OnPropertyChanged(nameof(PickerItemList)); // 重新设置SelectedLanguage为新集合中的对应项 _selectedLanguage = PickerItemList.First(x => x.Key == value.Key); OnPropertyChanged(nameof(SelectedLanguage)); _isUpdating = false; } } } public SettingsViewModel(ILocalizationResourceManager localizationResourceManager) { LocalizationResourceManager = localizationResourceManager; LanguageDictionary = new Dictionary<string, string>() { {"de", $"{LocalizationResourceManager["German"]}"}, {"en", $"{LocalizationResourceManager["English"]}"}, }; if (Preferences.ContainsKey(Constants.ApplicationLanguage)) { var langSetting = Preferences.Default.Get(Constants.ApplicationLanguage, "en"); SelectedLanguage = PickerItemList.First(x => x.Key == langSetting); } } }
关键说明
- 方案1通过
ObservableCollection的元素更新机制,避免了整个集合的替换,不会触发Picker的ItemsSource完全重新绑定,是更优雅的解决方案。 - 方案2通过标志位阻断递归,适合不想修改集合类型的场景,但需要手动维护
SelectedLanguage与新集合的匹配关系。
内容的提问来源于stack exchange,提问作者OXO
相关产品推荐
相关产品推荐

