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

.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 06:17:02