Обход файловой системы WPF
Я пытаюсь реализовать аналог программы TreeSize на WPF. Основная проблема - не могу заставить свой код обновлять интерфейс по мере обхода дерева (файловой системы). В оригинале это выглядит вот так: https://ibb.co/Mg30Pk4
Мой код сначала обходит всю файловую систему и только потом отрисовывает дерево. Ниже какая-то по счету попытка добиться результата (неудачная).
Model
public class TreeNode : INotifyPropertyChanged
{
private string _name;
private long _size;
private ObservableCollection<TreeNode> _nodes;
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged("Size");
}
}
public long Size { get => _size;
set
{
_size = value;
OnPropertyChanged("Size");
}
}
public ObservableCollection<TreeNode> Nodes
{
get => _nodes;
set
{
_nodes = value;
OnPropertyChanged("Nodes");
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ViewModel + обход файловой системы
public class TreeNodeViewModel : INotifyPropertyChanged
{
private TreeNode _treeNode;
public TreeNode TreeNode
{
get => _treeNode;
set
{
_treeNode = value;
OnPropertyChanged(nameof(TreeNode));
}
}
public TreeNodeViewModel()
{
_treeNode = new TreeNode{Name = @"C:\"};
}
public string Name
{
get => _treeNode.Name;
set
{
_treeNode.Name = value;
OnPropertyChanged("Name");
}
}
public long Size
{
get => _treeNode.Nodes.Sum(x => x.Size);
set
{
_treeNode.Size = value;
OnPropertyChanged("Size");
}
}
public ObservableCollection<TreeNode> Nodes
{
get => _treeNode.Nodes;
set
{
_treeNode.Nodes = value;
OnPropertyChanged("Nodes");
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void EnumerateFs()
{
TreeNode.Nodes = new ObservableCollection<TreeNode>();
EnumerationOptions options = new EnumerationOptions { AttributesToSkip = FileAttributes.ReparsePoint, IgnoreInaccessible = true };
var entries = (new DirectoryInfo(TreeNode.Name)).EnumerateFileSystemInfos("*", options);
foreach (var nodeEntry in entries)
{
if (nodeEntry is FileInfo fi)
{
TreeNode.Nodes.Add(new TreeNode { Name = fi.Name, Size = fi.Length });
OnPropertyChanged("Nodes");
}
else
{
var newNode = new TreeNode { Name = nodeEntry.FullName, Nodes = new ObservableCollection<TreeNode>() };
TreeNode.Nodes.Add(newNode);
newNode.Nodes.Add(EnumerateFs(newNode));
OnPropertyChanged("Nodes");
}
}
}
public TreeNode EnumerateFs(TreeNode node)
{
node.Nodes = new ObservableCollection<TreeNode>();
EnumerationOptions options = new EnumerationOptions { AttributesToSkip = FileAttributes.ReparsePoint, IgnoreInaccessible = true };
var entries = (new DirectoryInfo(node.Name)).EnumerateFileSystemInfos("*", options);
foreach (var nodeEntry in entries)
{
if (nodeEntry is FileInfo fi)
{
node.Nodes.Add(new TreeNode { Name = fi.Name, Size = fi.Length });
}
else
{
var newNode = new TreeNode { Name = nodeEntry.FullName, Nodes = new ObservableCollection<TreeNode>() };
node.Nodes.Add(newNode);
newNode.Nodes.Add(EnumerateFs(newNode));
}
}
return node;
}
}
View
<TreeView Grid.Row="1" x:Name="TreeView">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:TreeNodeViewModel}" ItemsSource="{Binding IsAsync=True, Path=Nodes, UpdateSourceTrigger=PropertyChanged}">
<StackPanel Orientation="Horizontal">
<TextBlock Width="200" Text="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}"></TextBlock>
<TextBlock Width="100" Text="{Binding Size, Converter={StaticResource FileSizeConverter}, UpdateSourceTrigger=PropertyChanged}"></TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
MainWindow
public partial class MainWindow : Window
{
TreeNodeViewModel vm;
public MainWindow()
{
InitializeComponent();
vm = new TreeNodeViewModel();
TreeView.ItemsSource = new ObservableCollection<TreeNodeViewModel> {vm};
Task.Run(() => vm.EnumerateFs());
}
}
Реально ли вообще добиться перерисовки во время рекурсивного обхода? Если да, то как? Не осталось никаких идей уже.