我有一个 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;
}
}
}
另一方面,如果您有两个 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();
}
此外,如果pathLorem
和ipsumPath
都可以在F:
或Z:
中输出是可能的code> 或其他地方,那么您需要结合此处提到的两个想法并添加两个驱动器选择器(例如 ComboBox
)以从中为每个选择目标驱动器,而不是对它们进行硬编码。或者,如果一次处理单个 TreeView
是可以接受的,那么您只需要一个 cmbDrives
组合框和两个 RadioButton
控件来相应地选择 pathLorem
或 ipsumPath
控件进行处理。
关于c# - 两个 Drives 中的两个 TreeView 结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73649197/