Powershell winforms form load должен срабатывать дважды, чтобы получить данные для загрузки
Я полностью застрял на этом в течение последних двух недель. Я должен создать winform, который показывает каталоги и файлы в treeview. Я получил большую часть его работы (помимо загрузки поддельных узлов, чтобы показать знаки плюс/минус, но пока это нормально), но проблема, с которой я сталкиваюсь, заключается в том, что мне нужно запустить скрипт дважды, чтобы событие загрузки формы начало загружать данные в treeview. Как правило, я бы просто сделал addrange для treenodecollection, однако мой клиент хочет, чтобы размер папки и файлов отображался в заголовке. Вот что у меня есть:
<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, чтобы ускорить его:
<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>
Однако это дало мне некоторые проблемы с возвратом пустых записей, и он все еще не загружал treeview с первого раза запуска кода. Я был натыкаясь моей голове в течение нескольких недель теперь, и я сдаюсь. Любая помощь будет оценена по достоинству!