c# - 两个 Drives 中的两个 TreeView 结构

标签 c# visual-studio winforms treeview

我有一个 WinForm 应用程序,它采用两个 TreeView 的结构并将它们作为文件夹实现到用户在下拉列表中选择的路径中。

Drop Down 当前从 Z 获取所有可选择的文件夹:

现在我的 TreeView loremPath有正确的 Drive with Z: 但是 ipsumPath应该进入R: 但使用相同的下拉菜单 - 因为第二个驱动器具有与 Z: 相同的精确 文件夹结构,所以我不需要构建一个全新的下拉菜单,只需更改 ipsumPath 中的路径即可。到 R: 并且可以对两个 TreeView 使用一个下拉菜单。

所以我之前有一个关于 StackOverflow 的问题,有人建议我对两个 TreeView 使用硬编码路径,但我不知道如何实现它。

我试过类似的方法:

        var testPath= new DirectoryInfo("R:\\").GetDirectories();
        var treeSeperator = ipsumPath.PathSeparator;
        var dirSep = Path.DirectorySeparatorChar.ToString();

        foreach (var node in GetCheckedNodes(ipsumPath.Nodes))
        {
            var sPath = Path.Combine(testPath.ToString(), node.FullPath.Replace(treeSeperator, dirSep));
            Directory.CreateDirectory(sPath);
        }

但这根本不起作用。

我的整个代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
using IWshRuntimeLibrary;
using System.Reflection;

namespace Form1
{
    public partial class Form1 : Form
    {
        [DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
        private static extern IntPtr CreateRoundRectRgn
         (
             int nLeftRect,
             int nTopRect,
             int nRightRect,
             int nBottomRect,
             int nWidthEllipse,
             int nHeightEllipse
         );

        public Form1()
        {
            InitializeComponent();
            this.FormBorderStyle = FormBorderStyle.None;
            Region = System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, Width, Height, 20, 20));
            foreach (TreeNode tn in loremPath.Nodes)
            {
                tn.Expand();
            }
            foreach (TreeNode tn in ipsumPath.Nodes)
            {
                tn.Expand();
            }
            ipsumDropDown.Items.AddRange(new[] { "R:\\", "Z:\\" });
            loremDropDown.DataSource = new DirectoryInfo($"{ipsumDropDown.SelectedItem}").GetDirectories();
        }

        private void CreateShortcutToCurrentAssembly(string saveDir)
        {
            var testPath = loremDropDown.SelectedValue.ToString();
            WshShell wshShell = new WshShell();
            string fileName = testPath + "\\" + Application.ProductName + ".lnk";
            IWshShortcut shortcut = (IWshShortcut)wshShell.CreateShortcut(fileName);
            shortcut.TargetPath = Application.ExecutablePath;
            shortcut.Save();
        }

        private void close_Click(object sender, EventArgs e)
        {
            System.Windows.Forms.Application.Exit();
        }

        private void loremPath_AfterCheck(object sender, TreeViewEventArgs e)
        {
            if (e.Action == TreeViewAction.Unknown) return;

            foreach (TreeNode n in e.Node.Children())
                n.Checked = e.Node.Checked;

            foreach (TreeNode p in e.Node.Parents())
                p.Checked = p.Nodes.OfType<TreeNode>().Any(n => n.Checked);
        }

        private IEnumerable<TreeNode> GetCheckedNodes(TreeNodeCollection nodeCol)
        {
            foreach (TreeNode node in nodeCol)
            {
                if (node.Checked ||
                    node.Nodes.Cast<TreeNode>().Any(n => n.Checked))
                {
                    yield return node;
                }

                foreach (TreeNode childNode in GetCheckedNodes(node.Nodes))
                {
                    if (childNode.Checked)
                        yield return childNode;
                }
            }
        }
        private void projektordnerGenerieren_Click(object sender, EventArgs e)
        {
            var destPath = loremDropDown.SelectedValue.ToString();
            var treeSep = loremPath.PathSeparator;
            var dirSep = Path.DirectorySeparatorChar.ToString();

            foreach (var node in GetCheckedNodes(loremPath.Nodes))
            {
                var sPath = Path.Combine(destPath, node.FullPath.Replace(treeSep, dirSep));
                Directory.CreateDirectory(sPath);
            }
            foreach (var node in GetCheckedNodes(ipsumPath.Nodes))
            {
                var sPath = Path.Combine(destPath, node.FullPath.Replace(treeSep, dirSep));
                Directory.CreateDirectory(sPath);
            }
            string folder = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            CreateShortcutToCurrentAssembly(folder);
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            this.loremPath.SelectedNode = this.loremPath.Nodes[0];
            this.ipsumPath.SelectedNode = this.ipsumPath.Nodes[0];
            loremPath.SelectedNode.Text = textBox1.Text;
            ipsumPath.SelectedNode.Text = textBox1.Text;
        }

        private void ipsumPath_AfterCheck(object sender, TreeViewEventArgs e)
        {
            if (e.Action == TreeViewAction.Unknown) return;

            foreach (TreeNode n in e.Node.Children())
                n.Checked = e.Node.Checked;

            foreach (TreeNode p in e.Node.Parents())
                p.Checked = p.Nodes.OfType<TreeNode>().Any(n => n.Checked);
        }

        public const int WM_NCLBUTTONDOWN = 0xA1;
        public const int HT_CAPTION = 0x2;

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern bool ReleaseCapture();

        private void Form1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                ReleaseCapture();
                SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
            }
        }

        private void alleErweitern_Click(object sender, EventArgs e)
        {
            foreach (TreeNode tn in loremPath.Nodes)
            {
                tn.Expand();
            }
            foreach (TreeNode tn in ipsumPath.Nodes)
            {
                tn.Expand();
            }
        }

        private void alleReduzieren_Click(object sender, EventArgs e)
        {
            foreach (TreeNode tn in loremPath.Nodes)
            {
                tn.Collapse();
            }
            foreach (TreeNode tn in ipsumPath.Nodes)
            {
                tn.Collapse();
            }
        }

        private void minimize_Click(object sender, EventArgs e)
        {
            this.WindowState = FormWindowState.Minimized;
        }
    }
    static class TreeViewExtensions
    {
        public static IEnumerable<TreeNode> Children(this TreeNode node)
        {
            foreach (TreeNode n in node.Nodes)
            {
                yield return n;

                foreach (TreeNode child in Children(n))
                    yield return child;
            }
        }

        public static IEnumerable<TreeNode> Parents(this TreeNode node)
        {
            var p = node.Parent;

            while (p != null)
            {
                yield return p;

                p = p.Parent;
            }
        }
    }
}

最佳答案

您在这里试图解决的问题是在文件系统中镜像检查的 TreeNode 分支,并在目标目录中创建类似的目录结构。目标目录可以位于特定驱动器之一,例如 R:Z:。那么,让我们分解一下。

目标驱动器

首先,您需要在列表控件中列出目标驱动器,例如 ComboBox 以选择一个。您可以通过 DriveInfo.GetDrives 在系统中获取固定和就绪的驱动器。方法或者如果不想全部列出它们,可以手动添加它们。我将在这里使用后一种方法。从这里开始,在设计器中,放置一个 ComboBox 并将其命名为 cmbDrives,然后添加到表单的构造函数中:

public Form1()
{
    InitializeComponent();
    // ...

    cmbDrives.Items.AddRange(new[] { "R:\\", "Z:\\" });
    cmbDrives.SelectedIndex = 0;  
}

目标目录

你已经have列出目标目录/文件夹的 loremDropDown 组合框。处理 cmbDrives.SelectedIndexChanged 事件以填充所选驱动器中的目标目录。重新访问构造函数以添加:

public Form1()
{
    InitializeComponent();
    // ...

    cmbDrives.Items.AddRange(new[] { "R:\\", "Z:\\" });
    cmbDrives.SelectedIndex = 0; 

    loremDropDown.DisplayMember = "Name";
    loremDropDown.ValueMember = "FullName"; 
}

并添加事件处理程序:

private void cmbDrives_SelectedIndexChanged(object sender, EventArgs e)
{
    var selDrive = cmbDrives.SelectedItem.ToString();
    loremDropDown.DataSource = new DirectoryInfo(selDrive).GetDirectories();
}

现在,当您选择 R: 驱动器时,loremDropDown.SelectedValue 返回所选目标目录的完整路径,例如 R:\\SomeSelectedDirectory\。当您选择另一个驱动器如 Z: 时,同一目录的路径将为 Z:\\SomeSelectedDirectory\。因此您无需执行任何其他操作。

创建目录结构

来自之前的回答:

private void SomeButton_Click(object sender, EventArgs e)
{
    var destPath = loremDropDown.SelectedValue.ToString();
    var treeSep = pathLorem.PathSeparator;
    var dirSep = Path.DirectorySeparatorChar.ToString();

    foreach (var node in GetCheckedNodes(pathLorem.Nodes))
    {
        var sPath = Path.Combine(destPath, node.FullPath.Replace(treeSep, dirSep));
        Directory.CreateDirectory(sPath);
    }

    // The same for `ipsumPath` TreeView if the target destination is the same.
}

private IEnumerable<TreeNode> GetCheckedNodes(TreeNodeCollection nodeCol)
{
    foreach (TreeNode node in nodeCol)
    {
        if (node.Checked ||
            node.Nodes.Cast<TreeNode>().Any(n => n.Checked))
        {
            yield return node;
        }

        foreach (TreeNode childNode in GetCheckedNodes(node.Nodes))
        {
            if (childNode.Checked)
                yield return childNode;
        }
    }
}

SO73649197


另一方面,如果您有两个 TreeView 控件并且您需要同时在两个不同的驱动器中创建目录结构,其中两个驱动器具有相同的目标目录结构,那么您只需要更改驱动器...(在这种情况下忽略 cmbDrives)...

public Form1()
{
    InitializeComponent();
    // ...

    loremDropDown.DisplayMember = "Name";
    loremDropDown.ValueMember = "FullName";
    loremDropDown.DataSource = new DirectoryInfo("F:\\").GetDirectories();
}

private void SomeButton_Click(object sender, EventArgs e)
{
    var driveF = "C:\\";
    var driveZ = "D:\\";
    var selDir = loremDropDown.SelectedValue.ToString();
    var destPathF = selDir.Replace(Path.GetPathRoot(selDir), driveF);
    var destPathZ = selDir.Replace(Path.GetPathRoot(selDir), driveZ);
    var treeSep = pathLorem.PathSeparator;
    var dirSep = Path.DirectorySeparatorChar.ToString();
    var shortcuts = new HashSet<string>();

    foreach (var node in GetCheckedNodes(pathLorem.Nodes))
    {
        var sPath = Path.Combine(destPathF, node.FullPath.Replace(treeSep, dirSep));
        Directory.CreateDirectory(sPath);

        if (node.Level == 0) shortcuts.Add(sPath.TrimStart(driveF.ToArray()));
    }

    foreach (var node in GetCheckedNodes(ipsumPath.Nodes))
    {
        var sPath = Path.Combine(destPathZ, node.FullPath.Replace(treeSep, dirSep));
        Directory.CreateDirectory(sPath);

        if (node.Level == 0) shortcuts.Add(sPath.TrimStart(driveZ.ToArray()));
    }

    foreach (var shortcut in shortcuts)
    {
        var dirF = $"{driveF}{shortcut}";
        var dirZ = $"{driveZ}{shortcut}";

        if (Directory.Exists(dirF) && Directory.Exists(dirZ))
        {
            CreateShortcut(dirF, dirZ);
            CreateShortcut(dirZ, dirF);
        }
    }

    // Optional...
    // CreateShortcut(destPathF, destPathZ);
    // CreateShortcut(destPathZ, destPathF);
}

private void CreateShortcut(string shortcutPath, string targetPath)
{
    WshShell wshShell = new WshShell();
    string fileName = Path.Combine(shortcutPath, $"{Application.ProductName}.lnk");
    IWshShortcut shortcut = (IWshShortcut)wshShell.CreateShortcut(fileName);
    shortcut.TargetPath = targetPath;
    shortcut.Save();
}

此外,如果pathLoremipsumPath 都可以在F:Z: 中输出是可能的code> 或其他地方,那么您需要结合此处提到的两个想法并添加两个驱动器选择器(例如 ComboBox)以从中为每个选择目标驱动器,而不是对它们进行硬编码。或者,如果一次处理单个 TreeView 是可以接受的,那么您只需要一个 cmbDrives 组合框和两个 RadioButton 控件来相应地选择 pathLoremipsumPath 控件进行处理。

关于c# - 两个 Drives 中的两个 TreeView 结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73649197/

相关文章:

c# - 从扩展方法中获取原始变量名

c# - SQL 查询在 SSMS 中有效,但在 C# : SELECT denied 中无效

javascript - Ajax 完全没有被调用

asp.net - 无法安装软件包 'Owin 1.0.0.'您正在尝试将此软件包安装到针对 '.NETFramework,Version=v4.5.2'的项目中

c# - BeginInvoke 方法的全部内容,以便它不会关闭我的应用程序?

C#:DateTimePicker 在使用自定义格式的低宽度程序启动时有下拉显示错误。是什么原因造成的?

c# - 使用 WCF 的原始 MQ 消息

C# 相当于 Java 的 Arrays.fill() 方法

.net - 考虑在没有 app.config 映射的情况下对程序集进行 app.config 重新映射

c# - 使用退出按钮关闭 winform 程序