I was preparing for some dashboards using Powershell WPF GUI and was looking for some charts and graph to show on my GUI, I tried utilizing data visualization charts but I was looking for more cooler ways to show them and was looking for interactive ones which can complement my dashboards. To achieve this I have created html files using chart.js and bootstrap html codes and used WPF webbrowser controls to project them.

There are multiple script files and folders involved in this project. Once the main script with name WPF-WebBrowser.ps1 is executed, it shows the GUI. Here for blue circle information I used eclipse control and Textblock on top of it to show my data.

<Ellipse Name='Ellipse4' HorizontalAlignment="Left" Height="111" Margin="323,195,0,0" VerticalAlignment="Top" Width="116" Fill="DeepSkyBlue"/>

<Label Content="Memory" HorizontalAlignment="Left" Margin="323,8,0,0" VerticalAlignment="Top" Width="116" HorizontalContentAlignment="Center" FontSize="16" Foreground="DeepSkyBlue"/>

To show example here I am only taking Bart chart here and how it is created, recipe for other charts are similar. I have added webbrowser controls and whatever default browser you have set, it will use the same here to show your html file, for example google chrome is my default browser, Web browser will use Chrome to show the html chart data. Below is one of the webbrowser control example I am using for bar chart.

<WebBrowser x:Name='WebBrowser2' HorizontalAlignment="Left" Height="270" Margin="602,356,0,0" VerticalAlignment="Top" Width="396"/>

Data is collected in below format to show on html file.

#This will collect the all processes information form remote computer and get the first 5 top processes by number.

$AllProcesses = Get-Process -ComputerName $ComputerName.Text

$ProcessByCount = $AllProcesses| Group-Object -Property Name | Sort-Object -Property Count -Descending | Select-Object -First 5

#These are the bar chart labels info and data collected to feed up in chart.js format, I need this information in example format of 'label1','label2','label3'

$Top5ProcName = ($ProcessByCount.Name | ForEach-Object {"'{0}'" -f $_}) -join ', '

#This is the actual data from where bars are generated on chart. Data output looks like for example 90,80,30.

$Top5ProcCount = ($ProcessByCount | Select-Object -ExpandProperty Count) -join ', '

#All the gathered data is used as parameter value in another script file and it generates html file, webbrowser control use this file to show on the gui screen.

& "$ScriptPath\Psscripts\New-BarChart.ps1" -Top5ProcName $Top5ProcName -Top5ProcCount $Top5ProcCount -LegendLabel Count

$WebBrowser2.Navigate("file:///$HtmlCharts\Bar.html")

1: ########################################## 2: # Created by http://vcloud-lab.com 3: # Created using chart.js html 4: # Tested on Windows 10 5: ########################################## 6: #Load required libraries 7: Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase, System.Windows.Forms, System.Drawing 8: $ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path 9: #$AssemblyLocation = Join-Path -Path $ScriptPath -ChildPath Charts 10: 11: $Global:AllChartsPath = $ScriptPath 12: [xml]$xaml = @" 13: <Window 14: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 15: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 16: xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 17: xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 18: xmlns:local="clr-namespace:WpfApp2" 19: 20: Title="ChartDemo" Height="300" Width="300"> 21: <Grid> 22: <Image Name='CornerImaage' HorizontalAlignment="Left" Height="228" VerticalAlignment="Top" Width="256"/> 23: <WebBrowser Name='WebBrowser1' HorizontalAlignment="Left" Height="250" Margin="602,49,0,0" VerticalAlignment="Top" Width="396"/> 24: <TextBox Name='ComputerName' HorizontalAlignment="Left" Margin="10,23,0,0" TextWrapping="Wrap" Text="$env:COMPUTERNAME" VerticalAlignment="Top" Height="20" Width="167"/> 25: <Button Name='Button' Content="Get-Details" HorizontalAlignment="Left" Margin="182,23,0,0" VerticalAlignment="Top" Width="76"/> 26: <Label Content="Services by status" HorizontalAlignment="Left" Margin="602,10,0,0" VerticalAlignment="Top" Width="396" HorizontalContentAlignment="Center" FontSize="18" Foreground="DeepSkyBlue"/> 27: <Label Content="Top 5 processes by Count" HorizontalAlignment="Left" Margin="602,311,0,0" VerticalAlignment="Top" Width="396" HorizontalContentAlignment="Center" FontSize="18" Foreground="DeepSkyBlue"/> 28: <WebBrowser x:Name='WebBrowser2' HorizontalAlignment="Left" Height="270" Margin="602,356,0,0" VerticalAlignment="Top" Width="396"/> 29: <Label Content="Top 5 Processes consuming CPU" HorizontalAlignment="Left" Margin="193,311,0,0" VerticalAlignment="Top" Width="396" HorizontalContentAlignment="Center" FontSize="18" Foreground="DeepSkyBlue"/> 30: <WebBrowser x:Name='WebBrowser3' HorizontalAlignment="Left" Height="250" Margin="193,356,0,0" VerticalAlignment="Top" Width="396"/> 31: <Ellipse Name='Ellipse1' HorizontalAlignment="Left" Height="111" Margin="473,44,0,0" VerticalAlignment="Top" Width="116" Fill="DeepSkyBlue"/> 32: <Ellipse Name='Ellipse2' HorizontalAlignment="Left" Height="111" Margin="473,195,0,0" VerticalAlignment="Top" Width="116" Fill="DeepSkyBlue"/> 33: <Ellipse Name='Ellipse3' HorizontalAlignment="Left" Height="111" Margin="323,44,0,0" VerticalAlignment="Top" Width="116" Fill="DeepSkyBlue"/> 34: <Ellipse Name='Ellipse4' HorizontalAlignment="Left" Height="111" Margin="323,195,0,0" VerticalAlignment="Top" Width="116" Fill="DeepSkyBlue"/> 35: <Label Content="Memory" HorizontalAlignment="Left" Margin="323,8,0,0" VerticalAlignment="Top" Width="116" HorizontalContentAlignment="Center" FontSize="16" Foreground="DeepSkyBlue"/> 36: <Label Content="All Cores" HorizontalAlignment="Left" Margin="473,8,0,0" VerticalAlignment="Top" Width="116" HorizontalContentAlignment="Center" FontSize="16" Foreground="DeepSkyBlue"/> 37: <Label Content="Total Disk Size" HorizontalAlignment="Left" Margin="323,160,0,0" VerticalAlignment="Top" Width="116" HorizontalContentAlignment="Center" FontSize="16" Foreground="DeepSkyBlue"/> 38: <Label Content="Total Nics" HorizontalAlignment="Left" Margin="473,160,0,0" VerticalAlignment="Top" Width="116" HorizontalContentAlignment="Center" FontSize="16" Foreground="DeepSkyBlue"/> 39: <Label Name='Label1' HorizontalAlignment="Left" Margin="323,70,0,0" VerticalAlignment="Top" Width="116" HorizontalContentAlignment="Center" FontSize="32" Foreground="White"/> 40: <Label Name='Label2' HorizontalAlignment="Left" Margin="473,70,0,0" VerticalAlignment="Top" Width="116" HorizontalContentAlignment="Center" FontSize="32" Foreground="White"/> 41: <Label Name='Label3' HorizontalAlignment="Left" Margin="323,224,0,0" VerticalAlignment="Top" Width="116" HorizontalContentAlignment="Center" FontSize="32" Foreground="White"/> 42: <Label Name='Label4' HorizontalAlignment="Left" Margin="473,224,0,0" VerticalAlignment="Top" Width="116" HorizontalContentAlignment="Center" FontSize="32" Foreground="White"/> 43: </Grid> 44: </Window> 45: "@ 46: 47: #Read the form 48: $Reader = (New-Object System.Xml.XmlNodeReader $xaml) 49: $Form = [Windows.Markup.XamlReader]::Load($reader) 50: 51: #AutoFind all controls 52: $xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach-Object { 53: New-Variable -Name $_.Name -Value $Form.FindName($_.Name) -Force 54: } 55: 56: $CornerImaage.Source = "$ScriptPath\PsScripts\corner.png" 57: 58: $HtmlCharts = "$ScriptPath\htmlCharts" 59: $Button.Add_Click({ 60: $Label1.Content = "$((Get-WmiObject Win32_PhysicalMemory -ComputerName $ComputerName.Text | Select-Object -ExpandProperty Capacity | Measure-Object -Sum).Sum / 1GB)" + " GB" 61: $Label2.Content = $(Get-WmiObject Win32_Processor -ComputerName $ComputerName.Text).NumberOfLogicalProcessors 62: $Label3.Content = "$([System.Math]::Round((get-wmiobject Win32_DiskDrive -ComputerName $ComputerName.Text).size / 1gb))" + " GB" 63: $Label4.Content = (Get-WmiObject win32_NetworkAdapter -ComputerName $ComputerName.Text | Where-Object {$_.PhysicalAdapter}).Count 64: 65: $AllProcesses = Get-Process -ComputerName $ComputerName.Text 66: $ProcessByCpu = $AllProcesses | Group-object -Property Name 67: $Top5ProcessesByCPU = $ProcessByCpu | Select-Object Name, @{N='CpuUsage';E={$_.Group.cpu | Measure-Object -Sum | Select-Object -ExpandProperty Sum}} | Sort-Object -Property CpuUsage -Descending | Select-Object -First 5 68: $TopCpuNames = ($Top5ProcessesByCPU.Name | ForEach-Object {"'{0}'" -f $_}) -join ', ' 69: $TopCpuUsage = ($Top5ProcessesByCPU | Select-Object -ExpandProperty CpuUsage) -join ', ' 70: & "$ScriptPath\Psscripts\New-LineChart.ps1" -TopCpuNames $TopCpuNames -TopCpuUsage $TopCpuUsage -LegendLabel CpuUsage 71: $WebBrowser3.Navigate("file:///$HtmlCharts\Line.html") 72: 73: $Form.Height="620" 74: $Form.Width="1016" 75: 76: $ProcessByCount = $AllProcesses| Group-Object -Property Name | Sort-Object -Property Count -Descending | Select-Object -First 5 77: $Top5ProcName = ($ProcessByCount.Name | ForEach-Object {"'{0}'" -f $_}) -join ', ' 78: $Top5ProcCount = ($ProcessByCount | Select-Object -ExpandProperty Count) -join ', ' 79: & "$ScriptPath\Psscripts\New-BarChart.ps1" -Top5ProcName $Top5ProcName -Top5ProcCount $Top5ProcCount -LegendLabel Count 80: $WebBrowser2.Navigate("file:///$HtmlCharts\Bar.html") 81: 82: $AllServices = Get-Service -ComputerName $ComputerName.Text 83: $ServicesByCount = $AllServices | Group-Object -Property Status 84: $ServicesNames = ($ServicesByCount.Name | ForEach-Object {"'{0}'" -f $_}) -join ', ' 85: $ServicesStatusCount = ($ServicesByCount | Select-Object -ExpandProperty Count) -join ', ' 86: & "$ScriptPath\Psscripts\New-DoughnutChart.ps1" -ServicesNames $ServicesNames -ServicesCount $ServicesStatusCount -LegendLabel Status 87: $WebBrowser1.Navigate("file:///$HtmlCharts\Doughnut.html") 88: 89: }) 90: 91: #Mandetory last line of every script to load form 92: [void]$Form.ShowDialog() 93:

Below is example of Bar charts script and it creates a html file, I highly recommend you read chart.js documentation, I have intermediate level of knowledge on it but everything is written and build reading docs only, and nothing else, I found them very easy, Chart scripts folder location is .\PSscripts. Below data fields are used to fill up data from earlier script as shown above.

labels:[$Top5ProcName],

datasets:[{

label: `"$LegendLabel`" ,

data:[$Top5ProcCount],

For each different chart I have a separate script, And I wrote it like that way so each chart I can modify easily disturbing other file. The given charts are very basic charts and there few more different charts available as per your need, you just have to add few information. Another hint is for setting html page chart correctly on webbrowser control is take webbrowser size (height and width) use same as canvas height and width on html pages.

1: [CmdletBinding(SupportsShouldProcess=$True, 2: ConfirmImpact='Medium', 3: HelpURI='http://vcloud-lab.com')] 4: Param 5: ( 6: [parameter(Position=0, Mandatory=$true, ValueFromPipelineByPropertyName=$true)] 7: [string]$Top5ProcName, 8: [parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true)] 9: [string]$Top5ProcCount, 10: [parameter(Position=2, Mandatory=$true, ValueFromPipeline=$true)] 11: [string]$LegendLabel 12: ) 13: 14: $Chart = @" 15: <!DOCTYPE html> 16: <html lang="en"> 17: <head> 18: <meta charset="UTF-8"> 19: <meta name="viewport" content="width=device-width, initial-scale=1.0"> 20: <meta http-equiv="X-UA-Compatible" content="ie=edge"> 21: <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script> 22: <script src="JSScripts/Chart.min.js"></script> 23: <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> 24: <link rel="stylesheet" href="JSScripts/bootstrap.min.css"> 25: <title>Bar Chart</title> 26: </head> 27: <body> 28: <div class="container"> 29: <canvas id="myChart"></canvas> 30: </div> 31: 32: <script> 33: let myChart = document.getElementById('myChart').getContext('2d'); 34: Chart.defaults.global.defaultFontSize = 12; 35: Chart.defaults.global.defaultFontColor = '#777'; 36: 37: let massPopChart = new Chart(myChart, { 38: type:'bar', 39: data:{ 40: labels:[$Top5ProcName], 41: datasets:[{ 42: label: `"$LegendLabel`" , 43: data:[$Top5ProcCount], 44: backgroundColor:'rgba(0,191,255, 0.6)', 45: hoverBackgroundColor: 'rgba(255,165,0, 0.6)', 46: borderWidth:1, 47: borderColor:'#777', 48: hoverBorderWidth:3, 49: hoverBorderColor:'#000', 50: fontsize:10, 51: }] 52: }, 53: options:{ 54: title:{ 55: display:false, 56: fontSize:8 57: }, 58: legend:{ 59: display:false, 60: position:'bottom', 61: labels:{ 62: fontColor:'Gray', 63: fontSize:12, 64: } 65: }, 66: layout:{ 67: padding:{ 68: left:0, 69: right:0, 70: bottom:0, 71: top:0 72: } 73: }, 74: tooltips:{ 75: enabled:true, 76: titleFontSize:10 77: }, 78: 79: scales: { 80: yAxes: [{ 81: ticks: { 82: beginAtZero: false, 83: display: true, //label show\hide 84: fontSize:10, 85: }, 86: gridLines: { 87: display:true, 88: drawBorder: true, 89: } 90: }], 91: xAxes: [{ 92: // Change here 93: barPercentage: 1, 94: ticks:{ 95: fontSize: 12, 96: display: true, //label show\hide 97: }, 98: gridLines: { 99: //color: "rgba(0, 0, 0, 0)", 100: //lineWidth: 0 101: display:true, 102: drawBorder: true, 103: } 104: }] 105: } 106: } 107: }); 108: </script> 109: </body> 110: </html> 111: "@ 112: 113: $Chart | Out-File -FilePath $Global:AllChartsPath\HtmlCharts\Bar.html 114: #Invoke-Expression C:\temp\test.html

Download these complete script here, This is also available on github.com.

Useful Articles

Part 1: Create WPF XAML powershell GUI form with Visual studio

Part 2: Powershell and WPF: Build GUI applications tutorial

Part 3: Create shorter Microsoft Powershell WPF automated clean script

Powershell PoshGUI: Convert user to SID and vice versa using

Microsoft Powershell GUI: Change Internet Options connections Lan settings proxy server grayed out