问题描述
过去两周我一直坚持不懈。我必须创建一个winform,在树视图中显示目录和文件。我得到了大部分工作(除了加载假节点以显示加号/减号,但现在还可以)但我遇到的问题是我必须运行脚本两次才能将表单加载事件发送到开始将数据加载到树视图中。通常,我只是为treenodecollection做一个addrange,但是我的客户希望文件夹和文件的大小显示在标题中。这就是我所拥有的:
I have been completely stuck for the past two weeks on this. I have to create a winform that shows directories and files in a treeview. I got the majority of it working (aside from loading fake nodes in to show plus/minus signs but that's okay for now) but the issue that I am having is that I have to run the script twice in order for the form load event to start loading data into the treeview. Typically, I would just do an addrange for the treenodecollection, however my client wants the the size of the folder and files to be displayed in the title. This is what I have:
<blockquote class="quote"><div class="op">Quote:</div>
[cmdletbinding()]
param(
[string]$TargetPath = (get-item .).fullname
)
#region Load Assemblies
[Void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[Void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[Void][System.Reflection.Assembly]::LoadWithPartialName("System.ServiceProcess")
[Void][System.Reflection.Assembly]::LoadWithPartialName("System.Design")
[void][System.Reflection.Assembly]::LoadWithPartialName('System.Collections.Generic')
[void][System.Reflection.Assembly]::LoadWithPartialName('System.Collections')
[void][System.Reflection.Assembly]::LoadWithPartialName('System.ComponentModel')
[void][System.Reflection.Assembly]::LoadWithPartialName('System.Text')
[void][System.Reflection.Assembly]::LoadWithPartialName('System.Data')
#endregion Load Assemblies
$ErrorActionPreference = 'continue' #'silentlycontinue'
#region Generated Form Objects
function InitializeComponent {
[System.Windows.Forms.Application]::EnableVisualStyles()
$MainForm = [System.Windows.Forms.Form]::new()
$TreeView = [System.Windows.Forms.Treeview]::new()
$RootNode = [System.Windows.Forms.TreeNode]::new()
$ContextMenu = [System.Windows.Forms.ContextMenuStrip]::new()
$PathMenuItem = [System.Windows.Forms.ToolStripMenuItem]::new()
$imagelist = [System.Windows.Forms.ImageList]::new()
$MainForm.SuspendLayout()
#Treeview
$TreeView.Location = '0, 0'
$TreeView.Name = 'TreeView'
$TreeView.ImageIndex = 1
$TreeView.Size = '500, 900'#'296, 640'
$TreeView.Dock = 'Fill'
$TreeView.TabIndex = 3
$TreeView.SelectedImageIndex = 1
#$TreeView.Sorted = $true
$TreeView.AutoSize = $true
$TreeView.Font = 'Century Gothic, 12pt' #8.25pt'
#$TreeView.BorderStyle = 'None'
$TreeView.add_BeforeExpand($TreeView_BeforeExpand)
$TreeView.add_NodeMouseClick($TreeView_NodeMouseClick)
$TreeView.add_NodeMouseDoubleClick($TreeView_NodeMouseDoubleClick)
#RootNode
$RootNode.Name = 'Root'
$RootNode.Text = "Selected Path: $((Get-location).Path)"
$RootNode.Tag = "Directory"
[void]$TreeView.Nodes.Add($RootNode)
#ContextMenu
$ContextMenu.Name = "ContextMenu"
$ContextMenu.Size = '170, 26'
$ContextMenu.add_ItemClicked($ContextMenu_ItemClicked)
#[Void]$ContextMenu.Items.Add($PathMenuItem)
#PathMenuItem
$PathMenuItem.Name = "PathMenuItem"
$PathMenuItem.Size = '169, 22'
$PathMenuItem.Text = "Change Path"
$PathMenuItem.Font = 'Century Gothic, 8.25pt'
$PathMenuItem.add_Click($PathMenuItem_Click)
#MainForm
$MainForm.ClientSize = '1440, 1000' #'1000, 800'
$MainForm.StartPosition = 'CenterScreen'
$MainForm.Text = "TreeView Disk Beta"
$MainForm.Padding = "0,0,0,0"
$MainForm.Font = [System.Drawing.Font]::new("Segoe UI",9,[System.Drawing.FontStyle]::Regular,[System.Drawing.GraphicsUnit]::Pixel) #'Segoe UI, 9pt'
$MainForm.FormBorderStyle = 'Sizable'
$MainForm.AutoSize = $true
$MainForm.AutoScaleDimensions = '6, 13'
#$MainForm.AutoScaleBaseSize = '6, 13'
$MainForm.AutoScaleMode = 'Font' #'Dpi'
$MainForm.Controls.Add($TreeView)
$MainForm.add_Load($MainForm_Load)
$MainForm.add_Load($Form_StateCorrection_Load)
#$MainForm.Add_Shown($MainForm_Shown)
$MainForm.add_FormClosing($Form_Cleanup_FormClosing)
#Save the initial state of the form
$InitialFormWindowState = $MainForm.WindowState
#$MainForm.ResumeLayout($false)
$MainForm.ResumeLayout()
}
. InitializeComponent
#endregion Generated Form Objects
#region Functions
# Gets Size in human readable format for File and Folder Objects
function Get-FriendlySize {
param($Bytes)
$sizes='Bytes,KB,MB,GB,TB,PB,EB,ZB' -split ','
for($i=0; ($Bytes -ge 1kb) -and
($i -lt $sizes.Count); $i++) {$Bytes/=1kb}
$N=2; if($i -eq 0) {$N=0}
"{0:N$($N)} {1}" -f $Bytes, $sizes[$i]
} #Ref: https://martin77s.wordpress.com/2017/05/20/display-friendly-file-sizes-in-powershell
# Calls to change the path when right-click on Treeview
Function Change-TreePath {
$FolderBrowserDialog = [System.Windows.Forms.FolderBrowserDialog]::new()
$FolderBrowserDialog.ShowNewFolderButton = $false
$FolderBrowserDialog.Description = 'Select a Target Path'
if ($FolderBrowserDialog.ShowDialog() -eq 'OK') {
$TargetPath = $FolderBrowserDialog.SelectedPath
New-Variable -Name TargetPath -Value $TargetPath -Scope 1 -Force
$TreeView.Nodes.Clear()
$PathNode = [System.Windows.Forms.TreeNode]::new()
$PathNode.Text = "Selected Path: $((Get-Item -Path $TargetPath).FullName)"
[void]$TreeView.Nodes.Add($PathNode)
& $MainForm_Load
}
#[void]$FolderBrowserDialog.ShowDialog()
}
# Scriptblock for coloring nodes for Hidden Files and Folders
$ColorNode = {
Foreach ($node in $TreeView.SelectedNode.Nodes) {
if ($node.Tag -contains "hidden") {
$node.ForeColor = [System.Drawing.Color]::OrangeRed
}
}
}
Function InvokeAddNodes {
param($selectedNode, $IOPath)
gci -Path $IOPath -Force -ErrorAction SilentlyContinue | Foreach {
if ($_ -is [System.IO.FileInfo]) {
$ImageFiles = switch -Regex ($_.Extension) {
"^\.ini|\.dll|\.sys" {5}
"^\.exe|\.msi" {2}
"^.jpg|\.png|\.bmp|\.ico|\.jpeg|\.fav|\.svg" {3}
"^\.zip|\.rar|\.7z" {7}
"\.txt|\.rtf|\.log|\.msg" {6}
default {0}
}
$subnode = [System.Windows.Forms.TreeNode]::new()
$subnode.Name = $_.FullName
$subnode.Text = & { ($_.Name + " " + (Get-FriendlySize -Bytes $_.Length) -as [string]) }
$subnode.Tag = @(($_.Attributes).ToString().Split(",").Replace(" ", ""))
$subnode.ImageIndex = $ImageFiles
$subnode.SelectedImageIndex = $ImageFiles
}
if ($_ -is [System.IO.DirectoryInfo]) {
$DirInfo = $_ | gci -Force -ErrorAction SilentlyContinue | Measure-Object -Sum length -ErrorAction SilentlyContinue
$subnode = [System.Windows.Forms.TreeNode]::new()
$subnode.Name = $_.FullName
$subnode.Tag = @(($_.Attributes).ToString().Split(",").Replace(" ", ""))
$subnode.ImageIndex = 1
$subnode.SelectedImageIndex = 1
if ($DirInfo.Count -gt 1) {
$subnode.Text = & { ($_.Name + " " + (Get-FriendlySize -Bytes $DirInfo.Sum)) -as [String] }
}
else {
$subnode.Text = & { ($_.Name + " " + (Get-FriendlySize -Bytes $_.Length)) -as [String] }
}
}
[void]$SelectedNode.Nodes.Add($subnode)
}
}
#endregion Functions
#region Event Handles
#region MainForm Events
$Form_StateCorrection_Load = {
#Correct the initial state of the form to prevent the .Net maximized form issue
$MainForm.WindowState = $InitialFormWindowState
}
$MainForm_Load = {
$TreeView.SelectedNode = $TreeView.Nodes[0]
Try {
#$MainForm.Cursor = 'WaitCursor'
$TreeView.BeginUpdate()
gci $TargetPath -Force | Foreach {
if ($_ -is [System.IO.FileInfo]) {
$ImageFiles = switch -Regex ($_.Extension) {
"^\.ini|\.dll|\.sys" {5}
"^\.exe|\.msi" {2}
"^.jpg|\.png|\.bmp|\.ico|\.jpeg|\.fav|\.svg" {3}
"^\.zip|\.rar|\.7z" {7}
"^\.txt|\.rtf|\.log|\.msg" {6}
default {0}
}
$subnode = [System.Windows.Forms.TreeNode]::new()
$subnode.Name = $_.FullName
$subnode.Text = & { ($_.Name + " " + (Get-FriendlySize -Bytes $_.Length) -as [string]) }
$subnode.Tag = @(($_.Attributes).ToString().Split(",").Replace(" ", ""))
$subnode.ImageIndex = $ImageFiles
$subnode.SelectedImageIndex = $ImageFiles
}
if ($_ -is [System.IO.DirectoryInfo]) {
$DirInfo = $_ | gci -recurse -Force -ErrorAction SilentlyContinue | Measure-Object -Sum length -ErrorAction SilentlyContinue
$subnode = [System.Windows.Forms.TreeNode]::new()
$subnode.Name = $_.FullName
$subnode.Tag = @(($_.Attributes).ToString().Split(",").Replace(" ", ""))
$subnode.ImageIndex = 1
$subnode.SelectedImageIndex = 1
if ($DirInfo.Count -gt 1) {
$subnode.Text = & { ($_.Name + " " + (Get-FriendlySize -Bytes $DirInfo.Sum)) -as [String] }
}
else {
$subnode.Text = & { ($_.Name + " " + (Get-FriendlySize -Bytes $_.Length)) -as [String] }
}
}
[void]$TreeView.Nodes[0].Nodes.Add($subnode)
}
& $ColorNode
$TreeView.EndUpdate()
$TreeView.Refresh()
$TreeView.SelectedNode.Expand()
}
Catch [Exception]{}
# Finally {
# $mainForm.Cursor = 'Default'
# }
}
#endregion MainForm Events
#region TreeView & TreeNodes Events
$treeview_BeforeExpand = [System.Windows.Forms.TreeViewCancelEventHandler] {
if((-not $_.Node.IsExpanded) -and ($_.Node.Tag -contains "Directory") -and ($_.Node.Clicks -eq 2)) {
$_.Node.Expand()
}
else { $_.Node.Collapse() }
}
$TreeView_NodeMouseClick=[System.Windows.Forms.TreeNodeMouseClickEventHandler] {
$TreeView.SelectedNode = $_.Node
if ($_.Button -eq 'Right') {
$point = [System.Drawing.Point]::new(($RootNode.Bounds.X + 60), $RootNode.Bounds.Y)
$ContextMenu.Items.Clear()
[Void]$ContextMenu.Items.Add($PathMenuItem)
$ContextMenu.Show($TreeView, $point.X, $point.Y)
}
if ((-not $_.Node.IsExpanded) -and (-not $_.Node.Nodes) -and ($_.Node.Tag -contains "Directory") ) {
$TreeView.SelectedNode.Nodes.Clear()
$TreeView.BeginUpdate()
InvokeAddNodes -SelectedNode $TreeView.SelectedNode -IOPath $TreeView.SelectedNode.Name
& $ColorNode
$TreeView.EndUpdate()
$TreeView.Refresh()
}
}
$TreeView_NodeMouseDoubleClick = [System.Windows.Forms.TreeNodeMouseClickEventHandler] {
if (($_.Button -eq 'Left') -and (-not $_.Node.IsExpanded) -and ($_.Node.Tag -contains "Directory")) {
Try {
$msg = [bool][System.IO.DirectoryInfo]::new($_.Node.Name).GetFileSystemInfos()
if (!$msg) {
[System.Windows.Forms.MessageBox]::Show("Directory: $($_.Node.Name) is empty.", " Empty Folder")
}
}
Catch [Exception] {
$errmsg = $PSItem.Exception.Message
[System.Windows.Forms.MessageBox]::Show($errmsg , " Access Denied")
}
}
}
#endregion TreeView & TreeNodes Events
#region FolderBrowserDialog for Changing Paths
$PathMenuItem_Click = {
Change-TreePath
}
#endregion FolderBrowserDialog for Changing Paths
#region Closing and Cleaning Form & Controls
$Form_Cleanup_FormClosing = [System.Windows.Forms.FormClosingEventHandler] {
#Remove all event handlers from the controls and releases garbage collection
try
{
$TreeView.remove_BeforeCheck($TreeView_BeforeCheck)
$TreeView.remove_AfterCheck($TreeView_AfterCheck)
$TreeView.remove_BeforeExpand($TreeView_BeforeExpand)
$TreeView.remove_NodeMouseClick($TreeView_NodeMouseClick)
$TreeView.remove_DoubleClick($TreeView_DoubleClick)
$MainForm.remove_Load($MainForm_Load)
$MainForm.remove_Shown($MainForm_Shown)
$MainForm.remove_Load($Form_StateCorrection_Load)
$MainForm.remove_FormClosing($Form_Cleanup_FormClosing)
$PathMenuItem.remove_Click($PathMenuItem_Click)
$TreeView.Nodes.Clear()
$TreeView.Dispose()
#$thisIO.Dispose(); rv thisIO
$MainForm.Dispose()
rv TreeView
rv RootNode
rv TreeView_NodeMouseClick
rv TreeView_NodeMouseDoubleClick
rv MainForm
rv ContextMenu
rv iconImage
rv treeview_BeforeExpand
rv Form_StateCorrection_Load
rv imagelist
rv PathMenuItem
[System.GC]::Collect()
$Error.Clear()
}
catch [Exception]
{ }
}
#endregion Closing and Cleaning Form & Controls
#endregion Event Handles
#Show Disk Tree Form
$null = $MainForm.ShowDialog()
</blockquote>
我的尝试:
我尝试在我的MainForm_Load和InvokeAddNodes函数中使用直接.net而不是get-childitem来加速它:
What I have tried:
I have tried using in both my MainForm_Load and InvokeAddNodes Function using straight .net instead of get-childitem to speed it up:
<blockquote class="quote"><div class="op">Quote:</div>
$nodecollection =[System.Collections.ArrayList]::new()
Foreach ($node in ([System.IO.DirectoryInfo]::new($TargetPath).GetFileSystemInfos())) {
if ($node -is [System.IO.FileInfo]) {
$subnode = [System.Windows.Forms.TreeNode]::new()
$subnode.Name = $node.FullName
$subnode.Text = & { ($node.Name + " " + (Get-FriendlySize -Bytes $node.Length) -as [string]) }
$subnode.Tag = @(($node.Attributes).ToString().Split(",").Replace(" ", ""))
$nodecollection.Add($subnode)
}
if ($node -is [System.IO.DirectoryInfo]) {
$subnode = [System.Windows.Forms.TreeNode]::new()
$DirInfo = [System.IO.DirectoryInfo]::new($node.FullName).GetFileSystemInfos() | Measure-Object -Sum length -ErrorAction SilentlyContinue
$subnode.Name = $node.FullName
$subnode.Tag = @(($node.Attributes).ToString().Split(",").Replace(" ", ""))
if ($DirInfo.Count -gt 1) {
$subnode.Text = & { (($node.Name).ToString() + " " + (Get-FriendlySize -Bytes $DirInfo.Sum)) -as [String] }
$nodecollection.Add($subnode)
}
else {
$subnode.Text = & { (($_.Name).ToString() + " " + (Get-FriendlySize -Bytes $_.Length)) -as [String] }
$nodecollection.Add($subnode)
}
}
}</blockquote>
然而,这给了我一些关于返回空白的问题条目,它仍然没有从第一次运行代码加载树视图。我已经撞了几个星期了,我放弃了。任何帮助将不胜感激!
However, that has given me some issues with returning blank entries and it still didn't load the treeview from first time of running the code. I have been bumping my head for a few weeks now and I give up. Any help would be appreciated!
推荐答案
这篇关于Powershell winforms表单加载必须触发两次才能加载数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!