private/charts/Invoke-ChartRedraw.ps1
|
function Invoke-ChartRedraw { <# .SYNOPSIS Redraws a chart container with new data. Must run on the UI thread. #> param( [System.Windows.Controls.DockPanel]$Container, $NewData ) # Read chart config from container Tag $config = $Container.Tag if (!$config -or $config.ControlType -ne 'Chart') { return } # Walk the DockPanel to find the Viewbox containing our canvas $canvas = $null foreach ($child in $Container.Children) { if ($child -is [System.Windows.Controls.Viewbox]) { $canvas = $child.Child break } } if (!$canvas) { return } # Normalize raw data into consistent [{Label, Value}] format. # Handles OrderedDictionary from hydration, arrays, and already-normalized data. $collected = [System.Collections.Generic.List[object]]::new() if ($NewData -is [System.Collections.IList]) { foreach ($item in $NewData) { $collected.Add($item) } } elseif ($NewData -is [System.Collections.IDictionary]) { foreach ($key in $NewData.Keys) { $collected.Add(@{ Label = $key; Value = $NewData[$key] }) } } elseif ($null -ne $NewData) { $collected.Add($NewData) } $chartData = ConvertTo-ChartData -RawData $collected -LabelProperty $config.LabelProperty -ValueProperty $config.ValueProperty # Clear existing chart content $canvas.Children.Clear() # Empty data shows a placeholder message if (!$chartData -or $chartData.Count -eq 0) { $placeholder = [System.Windows.Controls.TextBlock]@{ Text = 'No data' FontSize = 18 FontStyle = 'Italic' Opacity = 0.4 } $placeholder.SetResourceReference( [System.Windows.Controls.TextBlock]::ForegroundProperty, 'ControlForegroundBrush' ) [System.Windows.Controls.Canvas]::SetLeft($placeholder, ($canvas.Width / 2) - 30) [System.Windows.Controls.Canvas]::SetTop($placeholder, ($canvas.Height / 2) - 12) [void]$canvas.Children.Add($placeholder) # Null data for hydration - chart variable reads as $null when empty [PsUi.UiHydration]::SetData($Container, $null) return } # Grab current theme palette $palette = Get-ChartPalette # Dispatch to the appropriate chart renderer $renderParams = @{ Canvas = $canvas Data = $chartData Palette = $palette ShowValues = [bool]$config.ShowValues XAxisLabel = $config.XAxisLabel YAxisLabel = $config.YAxisLabel } switch ($config.ChartType) { 'Bar' { Add-BarChartElements @renderParams } 'Line' { Add-LineChartElements @renderParams } 'Pie' { Add-PieChartElements @renderParams } } # Pie charts need their legend rebuilt with the new data if ($config.ChartType -eq 'Pie' -and $config.ShowLegend) { # Remove stale legend $oldLegend = $null foreach ($child in $Container.Children) { if ($child -is [System.Windows.Controls.WrapPanel]) { $oldLegend = $child break } } if ($oldLegend) { [void]$Container.Children.Remove($oldLegend) } # Insert new legend just before the Viewbox (last child fills remaining space) $legend = New-ChartLegend -Data $chartData -Palette $palette [System.Windows.Controls.DockPanel]::SetDock($legend, [System.Windows.Controls.Dock]::Bottom) $viewboxIndex = $Container.Children.Count - 1 $Container.Children.Insert($viewboxIndex, $legend) } # Store as hashtables for hydration (survives cross-runspace injection) $storableData = [System.Collections.Generic.List[hashtable]]::new() foreach ($item in $chartData) { $storableData.Add(@{ Label = $item.Label; Value = $item.Value }) } [PsUi.UiHydration]::SetData($Container, $storableData) } |