首先跟各位说一声抱歉,第一版给老师看过了之后老师觉得我的写法很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();
}
}
}
运行后的结果
这个部分才写到文件夹的展示,后面还会有复选框的多选及单选、选中文件之后的显示以及上传报告,如果感兴趣的话可以关注我一下后续持续更新~
今天的文章就写到这里,希望能对你有所帮助~
放一张我做的雪玫瑰吧,希望你们开心每一天~