Menu

Virtual Geek

Tales from real IT system administrators world and non-production environment

Powershell WPF Charts dashboard demo

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

Go Back

Nice script...

I would add little thing to avoid the Form from disappearing behind others...

Add this iine$Form.TopMost = $True, before the showdialog

Reply


Comment

Blog Search

Page Views

1889213

Follow me on Blogarama