C# - 创建平面列表的树结构(按日期)

C# - Create a tree structure of a flat list (by dates)

我有一个包含很多文件的列表。我想以树结构而不是平面列表的形式显示此列表。 像这样:

2001
- 2001-01
    -2001-01-01
        -File 1
        -File 2
        -File 3
        -File 4
    -2001-01-02
        -File 5
        -File 6
        -File 7
        -File 8
    -2001-01-03
        - etc
    -2001-01-03
etc...

我发现了类似的东西,但不知道如何将它应用到我的设置中。 How to efficiently build a tree from a flat structure?

这些文件有一个 DateTime 字段,我可以在其中获取日期。

我可以将我的所有文件归为同一日期吗?

编辑: 现在列表显示如下

2001-01-01 - File 1
2001-01-01 - File 2
2001-01-01 - File 3
2001-01-01 - File 4
2001-01-02 - File 5
2001-01-02 - File 6
2001-01-02 - File 7
etc

好吧,这只是一个非常简单的实现,但它确实起到了作用:)

List<string> files = new List<string>()
{
    {"2001-01-01 - File 1"},
    {"2001-01-01 - File 2"},
    {"2001-01-02 - File 1"},
    {"2001-01-03 - File 1"}
};

TreeView tv = new TreeView();

foreach(string file in files)
{
    string y = file.Substring(0, 4);
    string ym = file.Substring(0, 7);
    string ymd = file.Substring(0, 10);
    string fn = file.Substring(13);

    TreeNode Node = tv.Nodes[y] ?? tv.Nodes.Add(y,y);
    Node = Node.Nodes[ym] ?? Node.Nodes.Add(ym,ym);
    Node = Node.Nodes[ymd] ?? Node.Nodes.Add(ymd,ymd);
    Node.Nodes.Add(fn);
}

编辑: 所以,我并没有真正注意到使用日期时间的信息。为此,我现在使用一个包含 类 的列表,其中包含日期时间和字符串文件名:

List<fi> files = new List<fi>()
{
    new fi(new DateTime(2001, 1, 1), "File 1"),
    new fi(new DateTime(2001, 1, 1), "File 2"),
    new fi(new DateTime(2001, 1, 3), "File 3"),
    new fi(new DateTime(2001, 1, 1), "File 4"),
    new fi(new DateTime(2001, 1, 2), "File 5"),
    new fi(new DateTime(2001, 1, 2), "File 6"),
    new fi(new DateTime(2001, 1, 2), "File 7")
};

TreeView tv = new TreeView();

var orderedfiles = from file in files orderby file.date ascending select file;
foreach(fi file in orderedfiles)
{
    TreeNode Node = tv.Nodes[file.date.Year.ToString()] ?? tv.Nodes.Add(file.date.Year.ToString(), file.date.Year.ToString());
    Node = Node.Nodes[file.date.ToString("yyyy-mm")] ?? Node.Nodes.Add(file.date.ToString("yyyy-mm"), file.date.ToString("yyyy-mm"));
    Node = Node.Nodes[file.date.ToString("yyyy-mm-dd")] ?? Node.Nodes.Add(file.date.ToString("yyyy-mm-dd"), file.date.ToString("yyyy-mm-dd"));
    Node.Nodes.Add(file.fn);
}

我假设您有一个名为 Files of FileInfo 对象的排序数组。然后你可以像这样遍历它:

foreach (FileInfo fi in Files)
{
    AddNode(fi);
}

AddNode 成员将节点添加到树中:

private void AddNode(FileInfo F)
{
    // Create the node labels.
    string y = String.Format("{0:yyyy}", F.LastWriteTime);
    string ym = String.Format("{0:yyyy-MM}", F.LastWriteTime);
    string ymd = String.Format("{0:yyyy-MM-dd}", F.LastWriteTime);

    TreeNode[] tnY = null;
    TreeNode[] tnYM = null;
    TreeNode[] tnYMD = null;

    // Find existing tree nodes for the file we are working on.
    tnY = treeView1.Nodes.Find(y, false);
    if (tnY.Length == 1)
    {
        tnYM = tnY[0].Nodes.Find(ym, false);
        if (tnYM.Length == 1)
        {
            tnYMD = tnYM[0].Nodes.Find(ymd, false);
        }
    }

    // Create the missing nodes.
    if (tnY.Length == 0)
    {
        tnY = new TreeNode[1];
        tnY[0] = treeView1.Nodes.Add(y, y);
    }
    if (tnYM == null || tnYM.Length == 0)
    {
        tnYM = new TreeNode[1];
        tnYM[0] = tnY[0].Nodes.Add(ym, ym);
    }
    if (tnYMD == null || tnYMD.Length == 0)
    {
        tnYMD = new TreeNode[1];
        tnYMD[0] = tnYM[0].Nodes.Add(ymd, ymd);
    }

    // And finally, add the node with the file name.
    tnYMD[0].Nodes.Add(F.Name);
}

基本思想是跟踪前一年、前一个月和前一天,以便您知道是否应该创建新的父节点。你没有说你想要它显示在树视图中,所以我假设你想要它在标准文本或 HTML 中。使其成为树视图控件在实现上稍微复杂一些,但基本思想没有任何不同。

为了避免陷入 C# 实现的细节,我将展示伪代码。这将使我更容易理解这些想法,并且让您更容易移植到您需要的任何形式以适合您的数据和输出方法。

这假定文件按日期排序,最早的日期(即最旧的文件)排在最前面。

首先将前一天、前一个月和前一年设置为-1:

previousYear = -1
previousMonth = -1
previousDay = -1
for each file in list
{
    if (file.date.year != previousYear)
    {
        output file.date.year
        previousYear = file.date.year
        // set previousMonth to default to force a new month folder
        previousMonth = -1
    }
    if (file.date.Month != previousMonth)
    {
        output file.date.month
        previousMonth = file.date.month
        // set previousDay to default to force a new day folder
        previousDay = -1
    }
    if (file.date.day != previousDay)
    {
        output file.date.day
        previousDay = file.date.day
    }
    // and then output the file name
    output file.name
}