You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何更新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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 16:43:13