public/controls/charts/Update-UiChart.ps1
|
function Update-UiChart { <# .SYNOPSIS Updates an existing chart with new data. .DESCRIPTION Pushes new data to a chart created with New-UiChart. Works from async button actions since the update runs on the UI thread on its own. This is the explicit update path. Charts also update automatically when you assign an ordered hashtable of new data to the chart variable inside a button action. .PARAMETER Variable The variable name of the chart to update (matches -Variable on New-UiChart). .PARAMETER Data New chart data in any supported format: - Ordered hashtable: [ordered]@{ "Label" = Value; ... } - Array of hashtables: @(@{ Label = "x"; Value = 1 }, ...) - Objects with Label/Value or Name/Count properties (Key also resolves for labels, Sum and Total for values) .PARAMETER LabelProperty Property name to use as labels when Data contains objects. It sticks to the chart, so a later update that omits it keeps using this name. .PARAMETER ValueProperty Property name to use as values when Data contains objects. Sticks the same way. .EXAMPLE New-UiChart -Type Bar -Variable 'diskChart' -Title 'Disk size (GB)' New-UiButton -Text 'Refresh' -NoOutput -Action { $diskData = [ordered]@{} foreach ($disk in Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3") { $diskData[$disk.DeviceID] = [math]::Round($disk.Size / 1GB) } Update-UiChart -Variable 'diskChart' -Data $diskData } .EXAMPLE # Pipeline objects with custom property names New-UiChart -Type Pie -Variable 'procChart' -Title 'Processes by vendor' New-UiButton -Text 'Scan' -NoOutput -Action { $procs = Get-Process | Where-Object Company | Group-Object Company | Sort-Object Count -Descending Update-UiChart -Variable 'procChart' -Data $procs[0..7] -LabelProperty Name -ValueProperty Count } #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$Variable, [Parameter(Mandatory)] $Data, [string]$LabelProperty, [string]$ValueProperty ) $session = Get-UiSession if (!$session) { Write-Warning "No active UI session." return } $proxy = $session.GetSafeVariable($Variable) if (!$proxy) { Write-Warning "Chart variable '$Variable' not found in session." return } # Normalize data to consistent [{Label, Value}] format $collected = [System.Collections.Generic.List[object]]::new() if ($Data -is [System.Collections.IDictionary]) { foreach ($key in $Data.Keys) { $collected.Add(@{ Label = $key; Value = $Data[$key] }) } } elseif ($Data -is [System.Collections.IList]) { foreach ($item in $Data) { $collected.Add($item) } } else { $collected.Add($Data) } $chart = $proxy.Control $builtWith = Invoke-OnUIThread -ArgumentList $chart -ScriptBlock { param($chart) $config = $chart.Tag if ($config -and $config.ControlType -eq 'Chart') { @{ Label = $config.LabelProperty; Value = $config.ValueProperty } } } $labelName = if ($LabelProperty) { $LabelProperty } else { $builtWith.Label } $valueName = if ($ValueProperty) { $ValueProperty } else { $builtWith.Value } # The rows get read in the runspace that built them since a ScriptProperty read from the UI thread waits 250ms a row. # Invoke-ChartRedraw converts again with the Tag names... only a hashtable carrying Label and Value keys survives that pass $points = [System.Collections.Generic.List[object]]::new() foreach ($point in (ConvertTo-ChartData -RawData $collected -LabelProperty $labelName -ValueProperty $valueName)) { $points.Add(@{ Label = $point.Label; Value = $point.Value }) } $redraw = @($chart, $points, $LabelProperty, $ValueProperty) Invoke-OnUIThread -ArgumentList $redraw -ScriptBlock { param($containerRef, $dataRef, $labelOverride, $valueOverride) # Names given here replace the ones the chart was built with, since the Tag is the only place Invoke-ChartRedraw reads them from. $config = $containerRef.Tag if ($config -and $config.ControlType -eq 'Chart') { if ($labelOverride) { $config.LabelProperty = $labelOverride } if ($valueOverride) { $config.ValueProperty = $valueOverride } } Invoke-ChartRedraw -Container $containerRef -NewData $dataRef } } |