Icon Color Replacement Fun with PowerShell

I like to keep interfaces simple and tend to use icons in my GUI designs. Recently, when making a PowerShell GUI to alert myself to alarm status changes in vCenter, I decided to reuse the same icon over and over for different statuses, but I made them meaningful by dynamically changing the color.

The Goal

My ultimate goal was to have 3 icons that were colored white, yellow and red.

white yellow red

How I did it

The first thing I did was search iconfinder for a free icon, and decided on this one:


Using paint.net, I determined that the base color of iconfinder’s server icon was #444444. Now what’s really cool about iconfinder, is they also offer the base64 code. As discussed in my earlier post, base64 can easily be used as icons and images in WPF forms.

So let’s take a look at what the code below executes to accomplish this task

  1. Loads WPF assemblies and sets base64 icon variable
  2. Creates a bitmapimage object to enable streaming of the base64 image
  3. Creates a colormap. Colormap is simple and just contains .OldColor and .NewColor.
  4. Passes colormap to an imageattribute which performs a SetRemapTable()
  5. Graphics.DrawImage draws an image based on these new imageattributes
  6. Saves the icon to variable for later use within the script ($iconwhite, $bmpwhite)
  7. Saves and opens the new icon so that you can confirm it worked.
  8. Do this for all colors within the newcolor array
# Add assemblies
Add-Type -AssemblyName PresentationFramework, System.Drawing

# set base64 image

# Create bitmapimage to enable streaming	
$script:bitmap = New-Object System.Windows.Media.Imaging.BitmapImage
$bitmap.StreamSource = [System.IO.MemoryStream][System.Convert]::FromBase64String($base64)

# Setup reusable objects
$colormap = New-Object System.Drawing.Imaging.ColorMap	
$attributes = New-Object System.Drawing.Imaging.ImageAttributes
$rectangle = New-Object System.Drawing.Rectangle(0, 0, $bitmap.Width, $bitmap.Height)

$newcolors = "white","yellow","red"

foreach ($color in $newcolors) {
	$bmp = [System.Drawing.Bitmap][System.Drawing.Image]::FromStream($bitmap.StreamSource)
	$colormap.OldColor = "#444444"
	$colormap.NewColor = $color
	$graphics = [System.Drawing.Graphics]::FromImage($bmp)
	$graphics.DrawImage($bmp, $rectangle, 0, 0, $rectangle.Width, $rectangle.Height, "Pixel", $attributes)
	$newicon = [System.Drawing.Icon]::FromHandle($bmp.GetHicon())
	# Set new variable to use icons and bmps within your script ($iconwhite,$bmpwhite)
	Set-Variable -Name "bmp$color" -value $bmp
	Set-Variable -Name "icon$color" -value $newicon
	$bmp.save("$env:temp\$color.png", "png")
	Invoke-Item "$env:temp\$color.png"

# System.Drawing.Graphics likes to leak memory. Dispose it.

This is high performance, and the total conversion time is less than 5 ms, which makes this technique efficient enough to use in all of your PowerShell GUI apps, if you’re so inclined :)

Chrissy is a Cloud and Datacenter Management & Data Platform MVP who has worked in IT for over 20 years. She is the creator of the popular SQL PowerShell module dbatools, holds a master's degree in Systems Engineering and is coauthor of Learn dbatools in a Month of Lunches. Chrissy is certified in SQL Server, Linux, SharePoint and network security. You can follow her on Twitter at @cl.

Posted in PowerShell, WPF

Leave a Reply