使用TreeView实现文件目录树的展示(基于C#MVVM)第二版!

       首先跟各位说一声抱歉,第一版给老师看过了之后老师觉得我的写法很LOW,然后决定带着我边写边学。

       目前也才学习到如何给展示目录树,但是使用的不一样的方法,接下来展示给大家。

      首先按照固定的模式创建好文件夹,MVVM的特点是用来解耦,将指定的类型放在指定的文件夹下面方便我们对问题拆分理解。

代码部分我按照自己学习到的层面给大家做了注解  希望能够对你们有帮助^ _ ^

ViewModels

FileSystemInfoNodeViewModel类

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Media.Imaging;
using CommunityToolkit.Mvvm.ComponentModel;

namespace ReportInput.ViewModels
{
    //一个枚举类,表示了文件的类型 是目录还是文件
    public enum FileSystemInfoNodeType
    {
        File,
        Directory
    }
    //创建一个ViewModel类作为View和Model的中介,将Model中的数据和方法转换为View中可用的形式,同时继承Observable类方便通知
    public abstract class FileSystemInfoNodeViewModel : ObservableObject
    {
        public FileSystemInfoNodeViewModel(FileSystemInfo fsi)
        {
            _wrapper = fsi;
        }
        //定义了一个类型为FileSystemInfo的属性,这个属性是受保护的,它只能在当前类和派生类里面可以使用
        protected FileSystemInfo _wrapper;
        //定义了一个字符串属性(名字)把对象的名字赋值给它
        public string Name => _wrapper.Name;
        //定义一个字符串属性(图标资源)
        private string _IconSource;
        //图标资源的获取与设置
        public string IconSource
        {
            get { return _IconSource; }
            set
            {
                
                SetProperty(ref _IconSource, value);
            }
            
        }
        //定义一个抽象的属性,必须在抽象类或者重写一下才行
        public abstract FileSystemInfoNodeType NodeType { get; }
        //定义一个属性表示是否需要扩展
        [ObservableProperty]
        bool _isExpanded = false;
        //定义一个属性表示是否选择了
        [ObservableProperty]
        bool _isSelected = false;
        
    }

    //定义了一个FileNodeViewModel类继承FileSystemInfoNodeViewModel类,这个类是跟文件有关的,跟文件有关的操作就写在这里面
    public class FileNodeViewModel : FileSystemInfoNodeViewModel
    {
        public FileNodeViewModel(FileInfo fi) : base(fi)
        {
            
        }
        //因为使用到了抽象类,所以要重写一下 
        public override FileSystemInfoNodeType NodeType => FileSystemInfoNodeType.File;
        
        
    }


    //定义了一个DirectoryNodeViewModel类继承FileSystemInfoNodeViewModel类,这个类是跟文件目录有关的,跟文件目录有关的操作就写在这里面
    public class DirectoryNodeViewModel : FileSystemInfoNodeViewModel
    {
        public DirectoryNodeViewModel(DirectoryInfo di) : base(di)
        {
            
        }
        //因为使用到了抽象类,所以要重写一下 
        public override FileSystemInfoNodeType NodeType => FileSystemInfoNodeType.Directory;


        //集合类的基础接口,它定义了一个用于遍历集合的枚举器,一般在foreach循环中要访问的集合和数组都实现了IEnumerable这个接口
        //这个接口内实现了对文件目录子节点的操作,获取子节点的信息,设置子节点的图片
        public IEnumerable<FileSystemInfoNodeViewModel> Children
        {
            get
            {
                //定义一个变量并将获取到的文件目录信息赋值给这个变量
                var di = _wrapper as DirectoryInfo;
                //使用GetFileSystemInfos()方法返回文件系统信息集合,然后从集合中选择符合的数据,同时将这些数据转换成FileSystemInfoNodeViewModel类型
                return di.GetFileSystemInfos().Select<FileSystemInfo, FileSystemInfoNodeViewModel>(fsi =>
                {
                    //对查询到的数据进行判断,如果查询到的结果是文件信息
                    if (fsi is FileInfo fi)
                    {
                        //就返回一个实例化对象,同时给对象的IconSource属性设置一个值
                        return new FileNodeViewModel(fi) { IconSource = "/Images/file_word.png" };
                    }
                    //如果查询到的结果是文件目录信息
                    else if (fsi is DirectoryInfo dii)
                    {
                        //就返回一个实例化对象,同时给对象的IconSource属性设置一个值
                        return new DirectoryNodeViewModel(dii) { IconSource = "/Images/folder.png" };
                    }
                    //如果查询的是其他的类型
                    else
                    {
                        //就抛出异常(不知道文件系统信息的类型)
                        throw new Exception("Unknown FileSystemInfo type");
                    }
                });
            }
        }
    }
}

MainWindowViewModel

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace ReportInput.ViewModels
{
    //继承ObservableObject类可以在数据改变的时候进行通知
    internal class MainWinowViewModel : ObservableObject
    {

        MainWinowViewModel()
        {

        }


        public static MainWinowViewModel Instance { get; } = new MainWinowViewModel();

        //窗口的标题
        string _title = "批量录入报告";
        //定义标题属性
        public string Title => _title;
        //根目录
        DirectoryNodeViewModel _root;
        //集合类的基础接口,它定义了一个用于遍历集合的枚举器,一般在foreach循环中要访问的集合和数组都实现了IEnumerable这个接口
        public IEnumerable<DirectoryNodeViewModel> Roots => new DirectoryNodeViewModel[] { _root };

        #region commands
        //定义一个文件夹路径属性并给它赋值
        string _folderPath = null;
        //选择文件的命令
        RelayCommand _selectFolderCommand;
        public RelayCommand SelectFolderCommand => _selectFolderCommand ?? new RelayCommand(() =>
        {
            //实例化一个用于选择文件夹的类
            var dialog = new FolderBrowserDialog();
            //如果选择了文件夹 DialogResult.OK就等于TRUE 则进行到下一步
            if (dialog.ShowDialog() == DialogResult.OK)
            {
                //把选择文件夹的路径赋值给定义好的文件夹路径
                _folderPath = dialog.SelectedPath;
                //根目录等于实例化一个ViewModel,所有的操作都在ViewModel里别人是看不见的。传入一个文件目录信息(文件夹路径){以及根目录的IconSource绑定的图片}
                _root = new DirectoryNodeViewModel(new DirectoryInfo(_folderPath)) { IconSource = "/Images/folder.png"};
                //触发事件通知UI层
                OnPropertyChanged(nameof(Roots));
            }
        });

        #endregion commands

    }
}

MainWindow.xaml

<Window x:Class="ReportInput.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ReportInput"
        xmlns:lvm="clr-namespace:ReportInput.ViewModels"
        mc:Ignorable="d"
        Title="{Binding Title}" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="2*"/>
        </Grid.ColumnDefinitions>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition Height="40"/>
            </Grid.RowDefinitions>

            <GroupBox Grid.Row="0" Header="目录结构">
                <TreeView ItemsSource="{Binding Roots}">
                    <TreeView.Resources>
                        <!--通过lvm引用FileNodeViewModel中的类型的数据-->
                        <DataTemplate DataType="{x:Type lvm:FileNodeViewModel}">
                            <StackPanel Orientation="Horizontal">
                                <CheckBox>
                                    <StackPanel Orientation="Horizontal">
                                        <Image Width="16" Height="16" Source="{Binding IconSource}" />
                                        <TextBlock Text="{Binding Name}" Foreground="Red"/>
                                    </StackPanel>
                                </CheckBox>
                            </StackPanel>
                        </DataTemplate>

                        <!--通过lvm引用DirectoryNodeViewModel中的类型的数据-->
                        <HierarchicalDataTemplate ItemsSource="{Binding Children}" DataType="{x:Type lvm:DirectoryNodeViewModel}">
                            <StackPanel Orientation="Horizontal">
                                <CheckBox>
                                    <StackPanel Orientation="Horizontal">
                                        <!--图片绑定到IconSource-->
                                <Image Width="16" Height="16" Source="{Binding IconSource}" />
                                <TextBlock Text="{Binding Name}" Foreground="Red"/>
                                    </StackPanel>
                                </CheckBox>
                            </StackPanel>
                        </HierarchicalDataTemplate>
                    </TreeView.Resources>
                </TreeView>
            </GroupBox>

            <StackPanel Grid.Row="1" Orientation="Horizontal">
                <StackPanel.Resources>
                    <Style TargetType="Button">
                        <Setter Property="Margin" Value="10,5"/>
                        <Setter Property="Width" Value="100"/>
                    </Style>
                </StackPanel.Resources>
                <!--按钮绑定到SelectFolderCommand这个命令-->
                <Button Content="选择文件夹" Command="{Binding SelectFolderCommand}"/>
                <Button Content="上传选中的报告"/>
            </StackPanel>
        </Grid>

        <GroupBox Grid.Column="2" Header="筛选列表">

        </GroupBox>

        <GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="LightGray"/>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using ReportInput.ViewModels;

namespace ReportInput
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            DataContext = MainWinowViewModel.Instance;
            InitializeComponent();
           
        }
    }
}

运行后的结果

这个部分才写到文件夹的展示,后面还会有复选框的多选及单选、选中文件之后的显示以及上传报告,如果感兴趣的话可以关注我一下后续持续更新~ 

今天的文章就写到这里,希望能对你有所帮助~

放一张我做的雪玫瑰吧,希望你们开心每一天~

  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单的C# MVVM侧边导航栏TreeView的示例代码: View: ```xaml <TreeView ItemsSource="{Binding MenuItems}"> <TreeView.ItemTemplate> <HierarchicalDataTemplate ItemsSource="{Binding SubMenuItems}"> <TextBlock Text="{Binding Title}" /> </HierarchicalDataTemplate> </TreeView.ItemTemplate> <TreeView.ItemContainerStyle> <Style TargetType="{x:Type TreeViewItem}"> <Setter Property="IsExpanded" Value="{Binding IsExpanded}" /> <Setter Property="IsSelected" Value="{Binding IsSelected}" /> </Style> </TreeView.ItemContainerStyle> <i:Interaction.Triggers> <i:EventTrigger EventName="SelectedItemChanged"> <i:InvokeCommandAction Command="{Binding NavigationCommand}" /> </i:EventTrigger> </i:Interaction.Triggers> </TreeView> ``` ViewModel: ```csharp public class MainViewModel : ViewModelBase { public ObservableCollection<MenuItemViewModel> MenuItems { get; set; } public ICommand NavigationCommand { get; set; } public MainViewModel() { MenuItems = new ObservableCollection<MenuItemViewModel> { new MenuItemViewModel { Title = "Menu Item 1", SubMenuItems = new ObservableCollection<MenuItemViewModel> { new MenuItemViewModel { Title = "Submenu Item 1" }, new MenuItemViewModel { Title = "Submenu Item 2" } } }, new MenuItemViewModel { Title = "Menu Item 2" } }; NavigationCommand = new RelayCommand(Navigate); } private void Navigate() { // Update main content based on selected menu item } } public class MenuItemViewModel : ViewModelBase { public string Title { get; set; } public ObservableCollection<MenuItemViewModel> SubMenuItems { get; set; } private bool _isExpanded; public bool IsExpanded { get { return _isExpanded; } set { SetProperty(ref _isExpanded, value); } } private bool _isSelected; public bool IsSelected { get { return _isSelected; } set { SetProperty(ref _isSelected, value); } } } ``` 在这个示例中,MenuItemViewModel表示一个菜单项,它包含一个标题和一个子菜单项的集合。MainViewModel包含一个ObservableCollection,其中包含所有菜单项,并且具有一个NavigationCommand来处理选定的菜单项。在View中,使用HierarchicalDataTemplate嵌套TreeViewItem,以便可以显示子菜单项。使用Interaction.Triggers将SelectedItemChanged事件绑定到NavigationCommand。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值