.NET Maui中如何使用OnPropertyChanged实现视图自动更新?
.NET Maui 集合绑定自动更新的正确实现方式
问题核心
你当前使用List<Category>作为绑定源,List<T>未实现INotifyCollectionChanged接口,仅当整个集合被替换时,OnPropertyChanged(nameof(Categories))才能触发视图更新。但如果是在原有集合中添加/删除元素,视图无法感知到变化。规范的做法是使用ObservableCollection<T>,它专门为数据绑定设计,会自动通知集合的增删改操作。
分步解决方案
1. 替换集合类型为ObservableCollection
首先修改ViewModel中的集合类型,同时确保Category类中的PDFs也换成ObservableCollection<PDF>:
public class MainViewModel : INotifyPropertyChanged { private string _categoriesPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Categories"); // 替换List为ObservableCollection private ObservableCollection<Category> _categories; public ObservableCollection<Category> Categories { get { return _categories; } set { _categories = value; OnPropertyChanged(nameof(Categories)); } } // 构造函数初始化ObservableCollection public MainViewModel() { LoadCategoriesAndPdfs(); } // ... 其他方法后续修改 } // 补充Category类定义并实现INotifyPropertyChanged public class Category : INotifyPropertyChanged { private string _name; public string Name { get => _name; set { _name = value; OnPropertyChanged(nameof(Name)); } } // 替换List为ObservableCollection private ObservableCollection<PDF> _pdfs; public ObservableCollection<PDF> PDFs { get => _pdfs; set { _pdfs = value; OnPropertyChanged(nameof(PDFs)); } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } // PDF类同样实现INotifyPropertyChanged(规范做法) public class PDF : INotifyPropertyChanged { private string _name; public string Name { get => _name; set { _name = value; OnPropertyChanged(nameof(Name)); } } private string _category; public string Category { get => _category; set { _category = value; OnPropertyChanged(nameof(Category)); } } private string _path; public string Path { get => _path; set { _path = value; OnPropertyChanged(nameof(Path)); } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. 更新加载方法初始化ObservableCollection
修改LoadCategoriesAndPdfs,将List转换为ObservableCollection:
public void LoadCategoriesAndPdfs() { if (!Directory.Exists(_categoriesPath)) Directory.CreateDirectory(_categoriesPath); var categoriesList = Directory.EnumerateDirectories(_categoriesPath) .Select(directory => new Category { Name = Path.GetFileName(directory), PDFs = new ObservableCollection<PDF>( Directory.GetFiles(directory, "*.pdf") .Select(file => new PDF { Name = Path.GetFileNameWithoutExtension(file), Category = Path.GetFileName(directory), Path = file }) ) }) .ToList(); Categories = new ObservableCollection<Category>(categoriesList); }
3. 优化AddPdf和AddCategory方法,直接更新集合
无需重新加载整个列表,直接修改现有集合即可触发视图自动更新:
修改AddCategory方法
public async Task AddCategory() { try { string result = await App.Current.MainPage.DisplayPromptAsync("Choose a name", "Category Name"); if (string.IsNullOrEmpty(result)) return; var categoryPath = Path.Combine(_categoriesPath, result); if (Directory.Exists(categoryPath)) { await App.Current.MainPage.DisplayAlert(null, "Category already exists", "Ok"); return; } Directory.CreateDirectory(categoryPath); // 直接添加新分类,自动触发视图更新 Categories.Add(new Category { Name = result, PDFs = new ObservableCollection<PDF>() }); } catch (Exception ex) { await App.Current.MainPage.DisplayAlert(null, ex.Message, "OK"); } }
修改AddPdf方法
public async Task AddPdf() { try { var pdfFiles = await FilePicker.PickMultipleAsync(new PickOptions { FileTypes = FilePickerFileType.Pdf }); if (pdfFiles.Count == 0) return; var othersPath = Path.Combine(_categoriesPath, "Others"); if (!Directory.Exists(othersPath)) Directory.CreateDirectory(othersPath); // 找到或创建"Others"分类 var othersCategory = Categories.FirstOrDefault(c => c.Name == "Others"); if (othersCategory == null) { othersCategory = new Category { Name = "Others", PDFs = new ObservableCollection<PDF>() }; Categories.Add(othersCategory); } List<string> pdfAlreadyExists = new List<string>(); foreach (var pdf in pdfFiles) { var destPath = Path.Combine(othersPath, pdf.FileName); if (File.Exists(destPath)) { pdfAlreadyExists.Add(pdf.FileName); } else { File.Copy(pdf.FullPath, destPath); // 直接添加新PDF,自动触发视图更新 othersCategory.PDFs.Add(new PDF { Name = Path.GetFileNameWithoutExtension(pdf.FileName), Category = "Others", Path = destPath }); } } if (pdfAlreadyExists.Count != 0) { string message = $"The following PDFs already exist: \n\n{string.Join("\n\n", pdfAlreadyExists)}"; await App.Current.MainPage.DisplayAlert(null, message, "OK"); } } catch (Exception ex) { await App.Current.MainPage.DisplayAlert(null, ex.Message, "OK"); } }
关键说明
ObservableCollection<T>会在集合元素增删、替换时自动触发CollectionChanged事件,绑定视图会自动更新,无需手动调用OnPropertyChanged。- 子集合(如
Category.PDFs)也必须使用ObservableCollection,否则添加PDF时对应的Expander内容不会自动更新。 - 如果需要处理外部修改的文件,仍可保留
LoadCategoriesAndPdfs方法,重新赋值Categories即可触发更新,但直接修改集合的方式更高效。
内容的提问来源于stack exchange,提问作者Diego Araujo
相关产品推荐
相关产品推荐

