桌面应用开发的日子(二):实现文件资源管理列表树加载

已矣乎!寓形宇内复几时?曷不委心任去留?胡为乎遑遑欲何之?富贵非吾愿,帝乡不可期。怀良辰以孤往,或植杖而耘耔。登东皋以舒啸,临清流而赋诗。聊乘化以归尽,乐夫天命复奚疑!————《归去来兮辞并序》·陶渊明

实现效果

获取本地逻辑驱动器、文件夹、文件

Directory.GetLogicalDrives();
Directory.GetDirectories(fullPath); // 返回指定目录中的子目录
Directory.GetFiles(fullPath); // 返回指定目录中的文件

// 截取文件或文件夹名称
public static string GetFileFolderName(string path)
{
    if (string.IsNullOrEmpty(path))
        return string.Empty;
    path = path.Replace("/", "\");

    var lastIndex = path.LastIndexOf("\");
    if (lastIndex <= 0)
        return path;

    return path.Substring(lastIndex + 1);
}

通过mvvm模式,实现后台数据与ui进行绑定,下面是树UI结构定义和实体,视图模型的定义

<TreeView x:Name="FolderTreeView" Grid.Row="1" ItemsSource="{Binding Items}">
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded" Value="{Binding IsExpanded,Mode=TwoWay}"/>
        </Style>
    </TreeView.ItemContainerStyle>
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding Children}">
            <StackPanel Orientation="Horizontal">
                <TextBlock Margin="3" FontFamily="{StaticResource IconFont}" FontSize="26" Foreground="{Binding IconColor}" Text="{Binding Icon}" />
                <TextBlock  VerticalAlignment="Center" FontSize="18" Text="{Binding Name}"/>
            </StackPanel>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>
public class DirectoryItem
{
    public string FullPath { get; set; }
    public DirectoryItemType Type { get; set; }
    public string Name
    {
        get
        {
            return this.Type == DirectoryItemType.Drive ?
              this.FullPath :
              DirectoryStructure.GetFileFolderName(this.FullPath);
        }
    }
    public string Icon { get; set; }
    public Brush IconColor { get; set; }
}
public class DirectoryItemViewModel : BaseViewModel
{
    /// <summary>
    /// 全路径
    /// </summary>
    public string FullPath { get; set; }

    /// <summary>
    /// 成员类型
    /// </summary>
    public DirectoryItemType Type { get; set; }

    /// <summary>
    /// 成员名称
    /// </summary>
    public string Name
    {
        get
        {
            return this.Type == DirectoryItemType.Drive ?
              this.FullPath :
              DirectoryStructure.GetFileFolderName(this.FullPath);
        }
    }

    /// <summary>
    /// 成员图标
    /// </summary>
    public string Icon { get; set; }

    /// <summary>
    /// 图标颜色
    /// </summary>
    public Brush IconColor { get; set; }

    /// <summary>
    /// 子成员
    /// </summary>
    public ObservableCollection<DirectoryItemViewModel> Children { get; set; }

    /// <summary>
    /// 表示成员是否可以展开
    /// </summary>
    public bool CanExpend { get { return this.Type != DirectoryItemType.File; } }

    /// <summary>
    /// 展开/收缩的处理
    /// </summary>
    public bool IsExpanded
    {
        get
        {
            return this.Children?.Count(x => x != null) > 0;
        }
        set
        {
            if (value)
                Expand();
            else
                ClearChildren();
        }
    }


    public ICommand ExpandCommand { get; set; }

    public DirectoryItemViewModel()
    {
        this.ExpandCommand = new RelayCommand(Expand);
        this.ClearChildren();
    }

    private void ClearChildren()
    {
        this.Children = new ObservableCollection<DirectoryItemViewModel>();
        if (this.Type != DirectoryItemType.File)
            this.Children.Add(null);
    }

    /// <summary>
    /// 展开目录并找到所有子成员
    /// </summary>
    private void Expand()
    {
        if (this.Type == DirectoryItemType.File)
            return;
        var children = DirectoryStructure.GetDirectContents(this.FullPath);
        var selector = children.Select(x => new DirectoryItemViewModel()
        {
            FullPath = x.FullPath,
            Type = x.Type,
            Icon = x.Icon,
            IconColor = x.IconColor
        });
        this.Children = new ObservableCollection<DirectoryItemViewModel>(selector);
    }
}
public class DirectoryStructureViewModel : BaseViewModel
{
    public ObservableCollection<DirectoryItemViewModel> Items { get; set; }

    public DirectoryStructureViewModel()
    {
        var children = DirectoryStructure.GetLogicalDrives();
        var selector = children.Select(x =>
        new DirectoryItemViewModel()
        {
            FullPath = x.FullPath,
            Type = x.Type,
            Icon = x.Icon,
            IconColor = x.IconColor
        });
        this.Items = new ObservableCollection<DirectoryItemViewModel>(selector);
    }
}

构建基类视图模型

[AddINotifyPropertyChangedInterface]
public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged=(sender, e)=>{}; //数据变化时通知前端
}

通过命令,点击逻辑器或文件夹时,加载子元素

    public class RelayCommand : ICommand
    {
        private Action action;

        public event EventHandler CanExecuteChanged = (sender, e) => { };

        public RelayCommand(Action action)
        {
            this.action = action;
        }

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            action();
        }
    }

相关参考:
https://github.com/Fody/PropertyChanged
https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.controls.treeview?view=net-5.0
https://docs.microsoft.com/zh-cn/dotnet/api/system.componentmodel.inotifypropertychanged?view=net-5.0
https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.input.icommand?view=net-5.0

原文地址:https://www.cnblogs.com/zhuanghamiao/p/wpf-treeview.html