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

WPF MVVM文件管理器:集合变更后界面未重绘问题求助

WPF MVVM文件资源管理器粘贴操作后界面不自动更新问题

使用WPF和MVVM复刻文件资源管理器,TreeView和ListBox在仅浏览文件夹时运行正常,但执行文件复制粘贴操作后,界面不会自动重绘,必须取消并重新选择文件夹才能看到更新后的项目列表。

说明:附上额外代码,因为怀疑问题并非直接关联ObservableCollection,而是与整体实现方式有关。

项目概述:
使用FileViewModel和FolderViewModel分别表示文件和文件夹。其中FolderViewModel包含完整路径、文件名两个字符串,以及一个在获取内容时懒加载的ObservableCollection,用于存储文件夹内容。

FolderViewModel代码(已省略无关内容、Files集合及与Move类似的方法)

public class FolderViewModel : FileFolderBaseViewModel
{
    public TrulyObservableCollection<FileFolderBaseViewModel> _folders;

    private ICommand _paste;
    public ICommand PasteClicked
    {
        get => _paste;
        set
        {
            _paste = value;
            RaisePropertyChanged();
        }
    }
    public TrulyObservableCollection<FileFolderBaseViewModel> Folders
    {
        get
        {
            if (_folderModel.HasDummy)
            {
                _folders.Clear();
                PopulateFoldersOnDemand();
            }
            return _folders;
        }
        set { _folders = value; RaisePropertyChanged(); }
    }
    public override bool IsSelected
    {
        get => _folderModel.IsSelected;
        set
        {
            if (value != _folderModel.IsSelected)
            {
                _folderModel.IsSelected = value;
                RaisePropertyChanged();
                //Remove contents only if not expanded and not selected
                if (!_folderModel.IsSelected && !_folderModel._isExpanded)
                {
                    if (_folders.Count() > 0)
                    {
                        _folderModel.HasDummy = true;
                        Folders.Clear();
                        Files.Clear();
                        Folders.Add(new FileViewModel(new FileModel("dummy", "dummy")));
                    }
                }
            }
        }
    }
    public bool IsExpanded
    {
        get => _folderModel._isExpanded;
        set
        {
            if (value != _folderModel._isExpanded)
            {
                _folderModel._isExpanded = value;
                RaisePropertyChanged();

                //Remove contents only if not expanded and not selected
                if (!_folderModel._isExpanded && !_folderModel.IsSelected)
                {
                    if (_folders.Count() > 0)
                    {
                        _folders.Clear();
                        _files.Clear();
                        _folderModel.HasDummy = true;
                        Folders.Add(new FileViewModel(new FileModel("dummy", "nopath")));
                    }
                }
            }
        }
    }

    public FolderViewModel(FolderModel folderModel, TrulyObservableCollection<FileFolderBaseViewModel> contents) : base(folderModel)
    {
        _files = new TrulyObservableCollection<FileFolderBaseViewModel>(contents.OfType<FileViewModel>());
        _folders = new TrulyObservableCollection<FileFolderBaseViewModel>(contents.OfType<FolderViewModel>());

        if (Directory.GetDirectories(_folderModel.FilePath).Any())
        {
            _folderModel.HasDummy = true;
            _folders.Add(new FileViewModel(new FileModel("dummy", "nopath")));
        }

        _paste = new RelayCommand(new Action<object>(HandlePaste));
    }

    private void Move(FileFolderBaseViewModel file)
    {
        try
        {
            if (file is FileViewModel f)
            {
                File.Move(file.FilePath, FilePath + "\\" + file.FileName, true);
            }
            else
            {
                Directory.Move(file.FilePath, FilePath + "\\" + file.FileName);
            }
        }
        catch (Exception e)
        {
            MessageBox.Show(e.ToString(), "Error", MessageBoxButton.OK);
            return;
        }
    }

    private void HandlePaste(object obj)
    {
        if (ClipBoardItem is not null)
        {
            if (PreviousRightClickAction is RightClickAction.Move)
            {
                if (File.Exists(FilePath + "\\" + ClipBoardItem.FileName))
                {
                    MessageBoxResult retVal = MessageBox.Show("File already exists. Overwrite?", "Warning", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning);

                    if (retVal != MessageBoxResult.Yes)
                    {
                        return;
                    }
                }
                Files.Add(ClipBoardItem);
                Move(ClipBoardItem);
            }
            ...
        }
    }

    private void HandleClickListBoxFolder(object obj) => IsSelected = true;

    private void PopulateFoldersOnDemand()
    {
        string[] dirs = Directory.GetDirectories(_folderModel.FilePath);
        FileFolderBaseModel model;

        foreach (string dir in dirs)
        {
            DirectoryInfo inf = new DirectoryInfo(dir);
            model = new FolderModel(inf.Name, inf.FullName);
            _folders.Add(new FolderViewModel((FolderModel)model, new TrulyObservableCollection<FileFolderBaseViewModel>()));
        }
    }
}

FileExplorerViewModel代码

public class FileExplorerViewModel : BaseViewModel
{
    private ICommand _selectedItemChanged;
    public TrulyObservableCollection<FileFolderBaseViewModel> ListViewItems { get; set; }
    public TrulyObservableCollection<FolderViewModel> Folders { get; set; }

    public ICommand ChangeRoot
    {
        get => _changeRoot;
        set {_changeRoot = value;}
    }

    public ICommand SelectedItemChanged
    {
        get => _selectedItemChanged;
        set { _selectedItemChanged = value;}
    }

    public FileFolderBaseViewModel? SelectedItem 
    {
        get => FileFolderBaseModel.SelectedItem;
        set
        {
            if(FileFolderBaseModel.SelectedItem != value)
            {
                FileFolderBaseModel.SelectedItem = value;
                RaisePropertyChanged();

                if (FileFolderBaseModel.SelectedItem is FolderViewModel selectedFolder)
                {
                    ListViewItems.Clear();
                    foreach (var item in selectedFolder.Folders)
                    {
                        ListViewItems.Add(item);
                    }
                    foreach (var item in selectedFolder.Files)
                    {
                        ListViewItems.Add(item);
                    }
                }
            }
        }
    }

    public FileExplorerViewModel()
    {
        _rootDirectory = "C:";
        ListViewItems = new TrulyObservableCollection<FileFolderBaseViewModel>();
        Folders = new TrulyObservableCollection<FolderViewModel>();

        PopulateFolders();
        _selectedItemChanged = new RelayCommand(new Action<object>(HandleListViewItemChanged));
    }

    private void HandleListViewItemChanged(object obj) => SelectedItem = (FileFolderBaseViewModel)obj;

    private void PopulateFolders()
    {
        string[] directoryPaths = Directory.GetDirectories(RootDirectory);

        foreach (string f in directoryPaths)
        {
            FileAttributes attributes = File.GetAttributes(@f);
            if (attributes.HasFlag(FileAttributes.Hidden) || attributes.HasFlag(FileAttributes.System))
            {
                continue;
            }
            Folders.Add((FolderViewModel)CreateFolderItem(f));
        }
    }

    private FileFolderBaseViewModel CreateFolderItem(string path)
    {
        DirectoryInfo dirInfo = new DirectoryInfo(path);

        return 
            new FolderViewModel(
                new FolderModel(dirInfo.Name, path), new TrulyObservableCollection<FileFolderBaseViewModel>());
    }
}

已尝试实现TrulyObservableCollection,让集合中项修改时触发CollectionChanged事件来重绘界面;也试过用FileSystemWatcher触发CollectionChanged事件,但两种方案都未能解决问题。

内容的提问来源于stack exchange,提问作者magmyr01

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 18:05:41