Skip to content

Instantly share code, notes, and snippets.

@jhoneill
Created February 19, 2025 10:27
Show Gist options
  • Save jhoneill/d9ddfea5ba0b5b499973defaa18e613a to your computer and use it in GitHub Desktop.
Save jhoneill/d9ddfea5ba0b5b499973defaa18e613a to your computer and use it in GitHub Desktop.
using namespace System.Windows.Forms.DataVisualization.Charting
using namespace System.Windows.Forms
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PsUseShouldProcessForStateChangingFunctions','',Justification='False positive. New-object does not change system state ')]
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSPossibleIncorrectUsageOfAssignmentOperator','',Justification='False positive. New-object does not change system state ')]
param()
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms.DataVisualization
function New-FormWithSave {
[outputType([System.Windows.Forms.Form])]
param (
[Alias('Width')]
$FormWidth = 1280,
[alias('Height')]
$FormHeight = 720,
[parameter(ValueFromPipeline=$True)]
[System.Windows.Forms.Control]$Control
)
begin {
$form = New-Object -TypeName System.Windows.Forms.Form -Property @{Width = $FormWidth; Height = $FormHeight}
$SaveButton = New-Object -TypeName System.Windows.Forms.Button -Property @{Left = ($FormWidth - 140); Top = ($FormHeight - 70); Text = "Save"; Anchor = ([ System.Windows.Forms.AnchorStyles]::Bottom -bor [ System.Windows.Forms.AnchorStyles]::Right)}
$SaveButton.add_click({
$FileTypes = [enum]::GetNames('System.Windows.Forms.DataVisualization.Charting.ChartImageFormat').ForEach({"*.$_"})
$SaveFileDlg = New-Object -TypeName system.Windows.Forms.SaveFileDialog -property @{
DefaultExt='PNG'; Filter="Image Files ($($FileTypes))|$($FileTypes)|All Files (*.*)|*.*"}
if ($SaveFileDlg.ShowDialog() -eq 'OK') {
$chart = $this.Parent.Controls.where({$_ -is [chart]})
$Chart.SaveImage($SaveFileDlg.FileName, ($SaveFileDlg.FileName -replace 'tif$','Tiff' -replace 'jpg$','jpeg' -replace '.*\.(.*)','$1'))
}
})
$Form.Controls.Add($SaveButton)
$Form.Add_Shown({$this.Activate()})
}
process {
foreach ($c in $Control) {
if ($form.width -lt ($c.width + 2* $c.left)) {$form.width = $c.width + 2* $c.left}
if ($form.height -lt ($c.height + $c.top + 80)) {$form.height = $c.height + $c.top + 80}
$Form.Controls.Add($c)
}
}
end { return $form }
}
function New-FormJob {
param(
[parameter(ValueFromPipeline=$True)]
[system.windows.forms.form]$Form
)
process {
$newRunspace = [RunspaceFactory]::CreateRunspace()
$newRunspace.ApartmentState = 'STA' #Otherwise some things - like save dialog - hang the process.
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable('Form', $form)
$PSCmd = [PowerShell]::Create().AddScript({
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Windows.Forms.DataVisualization
[void]$Form.ShowDialog()
})
$PSCmd.runspace = $newRunspace
[void]$psCmd.BeginInvoke()
$pscmd
}
}
function New-Chart {
param (
[parameter(Mandatory=$True,ValueFromPipeline=$True)]
$InputObject,
[Alias('Title')]
[string]$ChartTitle,
[System.Windows.Forms.DataVisualization.Charting.SeriesChartType]
$ChartType = 'Line',
$width =1240,
$Height = 630,
[string]$XField,
[string]$XAxisTitle,
[string]$xAxisNumberFormat,
[int]$xAxisLabelAngle,
[System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType]
$XDateTimeIntervalType = "Days",
[parameter(Mandatory=$True)]
[string[]]$YFields,
[string]$YAxisTitle,
[string]$YAxisNumberFormat,
[string[]]$SeriesColors = @('Blue'),
[string[]]$YAxis2Fields,
[string]$YAxis2Title,
[string]$YAxis2NumberFormat,
[string[]]
$YAxis2SeriesColors = @('Red'),
[string]$TitleFontName = 'Calibri',
[float]$TitleFontSize = 12,
[System.Drawing.FontStyle]
$TitleFontStyle = 'Bold',
[switch]$Enable3D,
[switch]$Legend
)
begin {$chartData = @() }
process {$chartData += $InputObject}
end {
$titleFont = New-Object System.Drawing.Font -ArgumentList $TitleFontName, $TitleFontSize, $TitleFontStyle
$Chart = New-object Chart -Property @{ Width = $width; Height = $Height; Left = 10; Top = 10; BorderColor = 'Black';
BorderDashStyle = 'Solid'; Anchor = ([System.Windows.Forms.AnchorStyles]15) } #15 is top + bottom + left + right
$Chart.ChartAreas.Add((New-Object ChartArea ))
If ($Enable3D) {
$chart.ChartAreas[0].Area3DStyle.Enable3D = $true
$chart.ChartAreas[0].Area3DStyle.Inclination = 45
}
#region get data labels / x-values; handle datetime as x axis values. Set axis titles and number formats and legend/title
if ($XField) {
$xAxisLabels = $Chart.ChartAreas[0].axisx.LabelStyle
if ($xValues = $chartData.$XField -as [datetime[]]) {
$dtFormats = (Get-Culture).DateTimeFormat
$xAxisLabels.IntervalOffsetType = $XDateTimeIntervalType
$xAxisLabels.IntervalType = $XDateTimeIntervalType
$xAxisLabels.Angle = $xAxisLabelAngle
if ($xAxisNumberFormat) {
$xAxisLabels.Format = $xAxisNumberFormat
}
else {
$xAxisLabels.Format = switch ($XDateTimeIntervalType) {
Hours {$dtFormats.ShortTimePattern}
Minutes {$dtFormats.LongTimePattern}
Seconds {$dtFormats.LongtTimePattern}
Milliseconds {$dtFormats.LongtTimePattern +".fff"}
Days {$dtFormats.ShortDatePattern + " " + $dtFormats.ShortTimePattern }
Default {$dtFormats.ShortDatePattern }
}}
}
else {
$xValues = $chartData.$XField
# $Measured = $xValues | Measure-Object -Minimum -Maximum
# $Chart.ChartAreas[0].axisx.Maximum = $Measured.Maximum
# $Chart.ChartAreas[0].axisx.Minimum = $Measured.Minimum
$xAxisLabels.Format = $xAxisNumberFormat
$xAxisLabels.Angle = $xAxisLabelAngle
}
}
if ($YAxisNumberFormat) {
$chart.ChartAreas[0].axisy.LabelStyle.Format = $YAxisNumberFormat
}
if ($YAxis2NumberFormat) {
$chart.ChartAreas[0].AxisY2.LabelStyle.Format = $YAxis2NumberFormat
}
if ($XAxisTitle) {
$Chart.ChartAreas[0].axisx.Title = $XAxisTitle
$Chart.ChartAreas[0].axisx.TitleFont = $titleFont
}
if ($YAxisTitle) {
$Chart.ChartAreas[0].axisy.Title = $YAxisTitle
$Chart.ChartAreas[0].axisy.TitleFont = $titleFont
}
if ($YAxis2Title) {
$Chart.ChartAreas[0].axisy2.Title = $YAxis2Title
$Chart.ChartAreas[0].axisy2.TitleFont = $titleFont
# $chart.chartAreas[0].TitleForeColor = [System.Drawing.Color]::DarkBlue
# $chart.chartAreas[0].axisy2.LineColor = [System.Drawing.Color]::DarkBlue
# $chart.chartAreas[0].axisy2.MajorGrid.LineColor = [System.Drawing.Color]::DarkBlue
# $chart.chartAreas[0].axisy2.LabelStyle.ForeColor = "Darkblue"
}
if ($Legend) {$Chart.Legends.Add((New-Object Legend -Property @{IsEquallySpacedItems = $True; BorderColor = 'Black'}))}
if ($ChartTitle) {$Chart.Titles.Add( (New-Object Title -Property @{Text = $ChartTitle; Font=$titleFont}))}
#endregion
#region add series to chart split between primary and secondary y-axis
$i = 0
foreach ($f in $YFields) {
$Series = New-Object -TypeName Series -Property @{ChartType = $ChartType; Name=$f; BorderWidth = 2}
if ($SeriesColors[$i]) {$Series.Color = $SeriesColors[$i]}
if ($xValues) {$Series.Points.DataBindXY($xValues, $chartData.$f)}
else {$Series.Points.DataBindY( $chartData.$f)}
$chart.Series.Add($Series)
$i ++
}
$i = 0
foreach ($f in $YAxis2Fields) {
$Series = New-Object -TypeName Series -Property @{ChartType = $ChartType; Name=$f; BorderWidth = 2; YAxisType = 'Secondary'}
if ($YAxis2SeriesColors[$i]) {
$Series.Color = $YAxis2SeriesColors[$i]}
if ($xValues) {$Series.Points.DataBindXY($xValues, $chartData.$f)}
else {$Series.Points.DataBindY( $chartData.$f)}
$chart.Series.Add($Series)
$i ++
}
#endregion
return $Chart
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment