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
相关产品推荐
相关产品推荐

