private/output/Add-DataGridColumns.ps1
|
function Add-DataGridColumns { <# .SYNOPSIS Generates DataGrid columns from the first item's properties with array support. #> [CmdletBinding()] param( [Parameter(Mandatory)] [System.Windows.Controls.DataGrid]$DataGrid, [Parameter(Mandatory)] [object]$FirstItem, [Parameter(Mandatory)] [hashtable]$Colors, [switch]$IncludeActionStatus ) $allProps = [System.Collections.Generic.List[object]]::new() $defaultProps = [System.Collections.Generic.List[object]]::new() # Check for DefaultDisplayPropertySet $hasDefaultSet = $false try { $stdMembers = $FirstItem.PSStandardMembers if ($stdMembers -and $stdMembers.DefaultDisplayPropertySet) { $defaultSet = $stdMembers.DefaultDisplayPropertySet if ($defaultSet.ReferencedPropertyNames) { $defaultProps.AddRange(@($defaultSet.ReferencedPropertyNames)) $hasDefaultSet = $true } } } catch { Write-Debug "Suppressed DefaultDisplayPropertySet lookup: $_" } # Get type name for fallback logic $itemTypeName = $FirstItem.PSObject.TypeNames[0] if (!$itemTypeName) { $itemTypeName = $FirstItem.GetType().FullName } # Fallback defaults for known types if (!$hasDefaultSet -and $itemTypeName -match 'Process') { $defaultProps.AddRange(@('ProcessName', 'Id', 'CPU', 'Handles', 'WorkingSet64')) $hasDefaultSet = $true } elseif (!$hasDefaultSet -and $itemTypeName -match 'Service') { $defaultProps.AddRange(@('Status', 'Name', 'DisplayName')) $hasDefaultSet = $true } # Create array display converters $arrayConverter = [PsUi.ArrayDisplayConverter]::new() $tooltipConverter = [PsUi.ArrayTooltipConverter]::new() $arrayLinkBrush = ConvertTo-UiBrush $Colors.Link foreach ($prop in $FirstItem.PSObject.Properties) { $name = $prop.Name if ($name.StartsWith('_')) { continue } [void]$allProps.Add($name) # Aliases bind to the actual .NET property, not the PowerShell alias $bindPath = if ($prop -is [System.Management.Automation.PSAliasProperty]) { $prop.ReferencedMemberName } else { $name } # Arrays get click-to-expand template columns instead of plain text $typeName2 = $prop.TypeNameOfValue $isArrayType = $typeName2 -and ($typeName2.EndsWith('[]') -or ($typeName2 -match 'Collection|List|Array|IEnumerable' -and $typeName2 -notmatch 'String')) if ($isArrayType) { # Create template column for arrays with click-to-expand $col = [System.Windows.Controls.DataGridTemplateColumn]::new() $col.Header = $name # FrameworkElementFactory created - its deprecated but still afaik the best way to create # datatemplates programatically... $cellTemplate = [System.Windows.DataTemplate]::new() $textBlockFactory = [System.Windows.FrameworkElementFactory]::new([System.Windows.Controls.TextBlock]) $binding = [System.Windows.Data.Binding]::new($bindPath) $binding.Mode = 'OneWay' $binding.Converter = $arrayConverter $textBlockFactory.SetBinding([System.Windows.Controls.TextBlock]::TextProperty, $binding) $textBlockFactory.SetValue([System.Windows.Controls.TextBlock]::ForegroundProperty, $arrayLinkBrush) $textBlockFactory.SetValue([System.Windows.Controls.TextBlock]::CursorProperty, [System.Windows.Input.Cursors]::Hand) $textBlockFactory.SetValue([System.Windows.Controls.TextBlock]::FontStyleProperty, [System.Windows.FontStyles]::Italic) $tooltipBinding = [System.Windows.Data.Binding]::new($bindPath) $tooltipBinding.Mode = 'OneWay' $tooltipBinding.Converter = $tooltipConverter $textBlockFactory.SetBinding([System.Windows.FrameworkElement]::ToolTipProperty, $tooltipBinding) $tagBinding = [System.Windows.Data.Binding]::new($bindPath) $tagBinding.Mode = 'OneWay' $textBlockFactory.SetBinding([System.Windows.FrameworkElement]::TagProperty, $tagBinding) $cellTemplate.VisualTree = $textBlockFactory $col.CellTemplate = $cellTemplate $headerMinWidth = [Math]::Max(80, ($name.Length * 7) + 30) $col.MinWidth = $headerMinWidth } else { # Standard text column for non-array properties $col = [System.Windows.Controls.DataGridTextColumn]::new() $col.Header = $name $col.Binding = [System.Windows.Data.Binding]::new($bindPath) $col.Binding.Mode = 'OneWay' $headerMinWidth = [Math]::Max(60, ($name.Length * 7) + 30) $col.MinWidth = $headerMinWidth } $col.Width = [System.Windows.Controls.DataGridLength]::Auto $col.IsReadOnly = $true # Hide non-default columns if we have a default set if ($hasDefaultSet -and $defaultProps -notcontains $name) { $col.Visibility = [System.Windows.Visibility]::Collapsed } $DataGrid.Columns.Add($col) } # If no default set, all properties are default if (!$hasDefaultSet) { $defaultProps = $allProps.Clone() } # Add Action Status column if requested # This will bind to the _ActionStatus hidden property added by the executor, which # inturn is updated by actions attached to the items using -ResultAction if ($IncludeActionStatus) { $statusCol = [System.Windows.Controls.DataGridTextColumn]::new() $statusCol.Header = "Action Status" $statusCol.Binding = [System.Windows.Data.Binding]::new("_ActionStatus") $statusCol.Binding.Mode = 'OneWay' $statusCol.Width = [System.Windows.Controls.DataGridLength]::new(150) $statusCol.MinWidth = 100 $statusCol.IsReadOnly = $true $DataGrid.Columns.Add($statusCol) } return @{ AllProperties = $allProps DefaultProperties = $defaultProps } } |