private/output/New-ObjectSubTab.ps1
|
function New-ObjectSubTab { <# .SYNOPSIS Creates a DataGrid sub-tab for displaying object collections. #> [CmdletBinding()] param( [Parameter(Mandatory)] [System.Collections.Generic.List[object]]$GroupItems, [Parameter(Mandatory)] [string]$TypeName, [Parameter(Mandatory)] [hashtable]$Colors, [Parameter(Mandatory)] [System.Windows.Controls.TabControl]$SubTabControl, [switch]$SingleSelect, [switch]$IncludeActionStatus ) $subTab = [System.Windows.Controls.TabItem]::new() $subTab.Header = "$TypeName ($($GroupItems.Count))" Set-TabItemStyle -TabItem $subTab $selectParam = if ($SingleSelect) { @{ SingleSelect = $true } } else { @{} } $subGrid = New-StyledDataGrid @selectParam # Build items in a List first (avoids UI notifications during population) $itemList = [System.Collections.Generic.List[object]]::new($GroupItems.Count) foreach ($item in $GroupItems) { if ($IncludeActionStatus) { $props = $item.PSObject.Properties if (!$props['_ActionStatus']) { $props.Add([System.Management.Automation.PSNoteProperty]::new('_ActionStatus', '')) } } $itemList.Add($item) } # Create ObservableCollection (single notification vs per-item) $observable = [System.Collections.ObjectModel.ObservableCollection[object]]::new($itemList) $subGrid.ItemsSource = [System.Windows.Data.CollectionViewSource]::GetDefaultView($observable) $allProps = [System.Collections.Generic.List[object]]::new() $defaultProps = [System.Collections.Generic.List[object]]::new() if ($GroupItems.Count -gt 0) { $firstItem = $GroupItems[0] $columnResult = Add-DataGridColumns -DataGrid $subGrid -FirstItem $firstItem -Colors $Colors -IncludeActionStatus:$IncludeActionStatus $allProps = $columnResult.AllProperties $defaultProps = $columnResult.DefaultProperties } # Pre-compute populated properties for "Has Data" filtering $populatedProps = Get-PopulatedProperties -Items $GroupItems -PropertyNames $allProps $subGrid.Tag = @{ AllProperties = $allProps DefaultProperties = $defaultProps PopulatedProperties = $populatedProps UnfilteredItems = $itemList Observable = $observable } Add-ArrayCellPopupHandler -DataGrid $subGrid $subTab.Content = $subGrid # Mark tab as indexing, then start background search index $subTab.Tag = 'Indexing' $items = @($observable) $runspace = [runspacefactory]::CreateRunspace() $runspace.Open() $ps = [powershell]::Create() $ps.Runspace = $runspace # Build search text for each item in background [void]$ps.AddScript({ param($items) foreach ($item in $items) { $props = $item.PSObject.Properties if ($props['_SearchText']) { continue } $sb = [System.Text.StringBuilder]::new() foreach ($prop in $props) { if ($prop.Name.StartsWith('_')) { continue } $propVal = $prop.Value if ($propVal) { [void]$sb.Append($propVal.ToString()); [void]$sb.Append(' ') } } $props.Add([System.Management.Automation.PSNoteProperty]::new('_SearchText', $sb.ToString())) } }).AddArgument($items) $asyncResult = $ps.BeginInvoke() $pollTimer = [System.Windows.Threading.DispatcherTimer]::new() $pollTimer.Interval = [TimeSpan]::FromMilliseconds(50) $pollTimer.Tag = @{ AsyncResult = $asyncResult PowerShell = $ps Runspace = $runspace Tab = $subTab SubTabControl = $SubTabControl } $pollTimer.Add_Tick({ $pt = $this.Tag if ($pt.AsyncResult.IsCompleted) { $this.Stop() try { $pt.PowerShell.EndInvoke($pt.AsyncResult) } catch { Write-Debug "Suppressed async EndInvoke cleanup error: $_" } $pt.PowerShell.Dispose() $pt.Runspace.Close() $pt.Tab.Tag = 'Indexed' # Enable filter box if this tab is currently selected $filterBox = $pt.SubTabControl.Tag if ($filterBox -and $pt.SubTabControl.SelectedItem -eq $pt.Tab) { $filterBox.IsEnabled = $true $filterBox.ToolTip = 'Filter results' if ($filterBox.Tag.Watermark) { $filterBox.Tag.Watermark.Text = 'Filter...' } } } }) $pollTimer.Start() return @{ Tab = $subTab DataGrid = $subGrid } } |