private/Controls/Invoke-UiDataGridCopyToClipboard.ps1
|
function Invoke-UiDataGridCopyToClipboard { <# .SYNOPSIS Copies selected rows (or focused cell) from a DataGrid to the clipboard, honoring column visibility. #> [CmdletBinding()] param( [Parameter(Mandatory)] [System.Windows.Controls.DataGrid]$DataGrid, [switch]$Cell ) # DataGrid property reads (SelectedItems, CurrentCell, Tag, Columns) have UI thread affinity, and Clipboard.SetText is STA only. # A background runspace would otherwise see SelectedItems as an empty enumeration and the function would return early without copying anything. # Dispatch the whole body once instead of piecemeal so one marshal covers all reads + the write. $cellMode = [bool]$Cell $gridRef = $DataGrid # GetNewClosure drops module private function resolution (same trap as New-ProgressPanel), the row branch silently died in its catch with CommandNotFound. # Carry the functions as references. $getPaths = ${function:Get-UiDataGridVisibleColumnPaths} $formatRows = ${function:Format-UiDataGridExportRows} $work = { if ($cellMode) { # No "selected cell" in row selection mode - CurrentCell tracks the focused one. $cellInfo = $gridRef.CurrentCell if (!$cellInfo.IsValid) { return } $item = $cellInfo.Item $col = $cellInfo.Column if ($null -eq $item -or $null -eq $col) { return } $bindPath = if ($col.SortMemberPath) { [string]$col.SortMemberPath } elseif ($col -is [System.Windows.Controls.DataGridBoundColumn] -and $col.Binding -and $col.Binding.Path) { [string]$col.Binding.Path.Path } elseif ($col.ClipboardContentBinding -and $col.ClipboardContentBinding.Path) { [string]$col.ClipboardContentBinding.Path.Path } else { $null } $text = '' if ($bindPath -eq '.') { # Scalar Value column - the item IS the cell. Skipping it copied '' over the user's clipboard. $text = [string]$item } elseif ($bindPath) { try { $val = $item.$bindPath if ($null -ne $val) { $text = [string]$val } } catch { Write-Debug "Cell read failed for '$bindPath': $_" } } try { [System.Windows.Clipboard]::SetText($text) } catch { Write-Debug "Cell copy failed: $_" } return } # Selected rows as CSV, scoped to visible columns so the clipboard matches what's on screen. if ($gridRef.SelectedItems.Count -eq 0) { return } try { $visibleProps = & $getPaths -DataGrid $gridRef $sanitize = ($gridRef.Tag -is [hashtable]) -and ($gridRef.Tag['SanitizeFormulas'] -eq $true) $projectionArgs = @{ Items = $gridRef.SelectedItems; Sanitize = $sanitize } if ($visibleProps -and $visibleProps.Count -gt 0) { $projectionArgs.Properties = $visibleProps } # Out-String tacks on a trailing newline. Join drops it. $text = [string]::Join([Environment]::NewLine, (& $formatRows @projectionArgs | ConvertTo-Csv -NoTypeInformation)) [System.Windows.Clipboard]::SetText($text) } catch { Write-Debug "Row copy failed: $_" } }.GetNewClosure() if ($DataGrid.Dispatcher.CheckAccess()) { & $work } else { $DataGrid.Dispatcher.Invoke([Action]$work) } } |