WPF中Properties.Settings的StringCollection绑定ListView后删除项不刷新
你遇到的问题核心在于**StringCollection并没有实现INotifyCollectionChanged接口**,而WPF的ListView要自动刷新UI,需要绑定的集合能在元素增删时发出变更通知。另外你之前调用的OnPropertyChanged("Genres")不会起作用——因为你的ListView绑定的是Properties.Settings.Default的Genres属性,而不是ViewModel里的属性,ViewModel的属性变更通知无法触发Settings对象的属性更新。
当你删除元素后,虽然Settings.Default.Genres的数据已经更新并保存,但ListView没有收到任何“集合已变化”的信号,所以不会重新渲染;直到窗口重启时,ListView重新读取了Settings的数据,才会显示正确内容。
ObservableCollection包装集合 最优雅且通用的解决方式是用实现了INotifyCollectionChanged的ObservableCollection<string>来包装StringCollection,让UI能自动感知集合的变化。以下是针对你代码的具体修改步骤:
1. 修改ViewModel,添加ObservableCollection属性
更新SettingWindowViewModel.cs,添加一个用于绑定的ObservableCollection,并在构造函数中从Settings初始化它:
using System.Collections.ObjectModel; // 需要添加这个命名空间 using System.Linq; // 用于Cast转换 class SettingWindowViewModel : INotifyPropertyChanged { private string selectedGenre; public string SelectedGenre { get { return selectedGenre; } set { selectedGenre = value; OnPropertyChanged("SelectedGenre"); } } // 新增:用于绑定的ObservableCollection private ObservableCollection<string> _genreCollection; public ObservableCollection<string> GenreCollection { get => _genreCollection; set { _genreCollection = value; OnPropertyChanged(); } } public SettingWindowViewModel() { // 将Settings中的StringCollection转换为ObservableCollection _genreCollection = new ObservableCollection<string>(Properties.Settings.Default.Genres.Cast<string>()); } // ... 原有INotifyPropertyChanged实现代码保持不变 ... // 修改DeleteGenreCommand逻辑 private RelayCommand deleteGenreCommand; public RelayCommand DeleteGenreCommand { get { return deleteGenreCommand ?? (deleteGenreCommand = new RelayCommand(obj => { string genre = obj as string; // 1. 先从ObservableCollection删除,UI会自动刷新 GenreCollection.Remove(genre); // 2. 同步更新Settings中的StringCollection并保存 Properties.Settings.Default.Genres.Remove(genre); Properties.Settings.Default.Save(); }, (obj) => GenreCollection.Count > 0 && obj != null)); } } }
2. 更新XAML的绑定目标
修改SettingWindow.xaml中ListView的ItemsSource绑定,从原来的Settings.Default.Genres改为ViewModel的GenreCollection:
<ListView ItemsSource="{Binding GenreCollection}" SelectedItem="{Binding SelectedGenre}" VerticalAlignment="Top"/>
ObservableCollection<T>内置了INotifyCollectionChanged接口实现,当你调用Remove()方法时,它会自动触发CollectionChanged事件,ListView监听这个事件后会立即更新UI。- 我们同时同步更新
Properties.Settings.Default.Genres并保存,确保数据能持久化到本地,重启窗口后依然有效。
如果不想使用ObservableCollection,也可以在删除元素后手动触发Settings对象的属性变更通知,强制ListView重新读取数据:
修改DeleteGenreCommand的逻辑:
public RelayCommand DeleteGenreCommand { get { return deleteGenreCommand ?? (deleteGenreCommand = new RelayCommand(obj => { string genre = obj as string; Properties.Settings.Default.Genres.Remove(genre); Properties.Settings.Default.Save(); // 手动触发Settings.Default的Genres属性变更通知 ((INotifyPropertyChanged)Properties.Settings.Default).PropertyChanged?.Invoke( Properties.Settings.Default, new PropertyChangedEventArgs("Genres") ); }, (obj) => Properties.Settings.Default.Genres.Count > 0 && obj != null)); } }
这种方式不需要修改XAML绑定,但缺点是不够优雅——每次修改集合都需要手动触发通知,而且批量修改时会多次刷新UI,体验不如ObservableCollection。
内容的提问来源于stack exchange,提问作者Andrei R

