PowerShell In GUI Blog

PowerShell GUI Box Just In A Few Clicks

Archive for the ‘System.Windows.Forms.DockStyle’ Category

PowerShell Wizard. Round 2. It works!

with one comment

After posting the wizard sample, I thought – why don’t finish it today? I added

  • the Finish and the Cancel buttons in a way that the Finish button asks the user whether one wishes to exit now or not. The Cancel is available on the first step and by clicking this the user quits immediately
  • scriptblocks, used as a code that is run on clicking the Next button
  • fixed bugs, added comments here and there and a bit of polishing

At first, I’d like to show what the sample does.

It starts the wizard, sets the second step as the first the user sees (just for example) and provides a label saying that the wizard is awaiting for a path to any directory.

After that the user presses the next button (it stays here if the text box field is empty. The wrong path won’t be accepted too) and goes to the next step called, just for example, the Progress step:

The step called in the sample as Output contains a list box filled with the files list:

After all, at the last step can be written something encouradging, or an error log to be shown, or something else. There comes the Finish button, as well as the Cancel button comes at the first step of the wizard:

Now, after we know what this does, I can post the code running the sample:

		# Step 1: Welcome
		New-WizardStep 'Welcome' `
			'This is the first step' `
			'Welcome to the PowerShell Wizard, the our dear customer!';
		# Add a label
		# Please note that we can use the enumeration $steps which is being created runtime
		# on a call of the New-WizardStep function
		Add-ControlToStep $steps.Welcome `
			System.Windows.Forms.Label `
			'lblWelcome' 20 10 50 300 `
			'This Wizard carries you through the steps you need to collect the files from a given path';

		# Step 2
		New-WizardStep 'Input' `
			'Step Two' `
			'Here you type some in controls, plz';
		# Add a label
		Add-ControlToStep $steps.Input `
			System.Windows.Forms.Label `
			'lblInput' 20 10 20 300 `
			'Please type the path to a catalog';
		# Add a text box
		Add-ControlToStep $steps.Input `
			System.Windows.Forms.TextBox `
			'txtInput' 40 10 20 300
		# Add the code which requires that text box was not empty
		Add-CodeToStep $steps.Input `
			-StepCode {
					[string]$private:path = `
						$wzdSteps[$steps.Input].Controls['txtInput'].Text;
					if ($private:path.Length -eq 0)
					{
						# stop the step
						throw;
					}
					if (-not [System.IO.Directory]::Exists($private:path))
					{
						# stop the step
						throw;
					}
				}

		# Step 3
		New-WizardStep 'Progress' `
			'The third one' `
			'Wait, please. Sip a coffee' ;
		# Add a progress bar
		Add-ControlToStep $steps.Progress `
			'System.Windows.Forms.ProgressBar' `
			'pbDir' 200 50 100 400
		Add-CodeToStep $steps.Progress `
			-StepCode {
					# set progress bar maximum
					$wzdSteps[$steps.Progress].Controls['pbDir'].Minimum = 0;
					$wzdSteps[$steps.Progress].Controls['pbDir'].Value = 0;
					$wzdSteps[$steps.Progress].Controls['pbDir'].Maximum = `
						(Get-ChildItem $wzdSteps[$steps.Input].Controls['txtInput'].Text).Length;
					# clear the list box (from the next step)
					$wzdSteps[$steps.Output].Controls['lbxFiles'].Items.Clear();
					# add file names to the list box
					Get-ChildItem $wzdSteps[$steps.Input].Controls['txtInput'].Text | %{
							$wzdSteps[$steps.Progress].Controls['pbDir'].Value++;
							$wzdSteps[$steps.Output].Controls['lbxFiles'].Items.Add($_.Name);
						}
				}

		# Step 4
		New-WizardStep 'Output' 'Fourth' `
			'Now awake and read the output';
		# Add a list box
		Add-ControlToStep $steps.Output `
			System.Windows.Forms.ListBox `
			lbxFiles 50 50 300 400

		# Step 5: Finish
        New-WizardStep 'Finish' 'Finish!' 'Bye!';

		Initialize-Wizard;
		# Set the second step as active
		Invoke-WizardStep -Forward $true;

		$script:frmWizard.ShowDialog() | Out-Null;

The full version is posted in the box at rigth and published on the PoshCode. Ask for a doc or more comments in the code if you need to.

Hey, Scripting GUI! PowerShell wizard in a couple of minutes

with one comment

At the same time when many PowerShell glitteriti entertained the public at TechEd with some PowerShell tricks or new applications, we didn’t spend our time too. Today’s module is intended to help all who creates PowerShell scripts with conversion of these scripts to GUI tools.

Accordingly to Ravikanth’s survey results, the vast majority of scripts delivered to consumers or colleagues are wrapped into some GUI (the first chart). The second chart clearly shows that most respondents do it just because GUI tools are warmly accepted by consumers in opposite to bare commandline scripts, even with the richest system of parameters.

Discussing this topic further, we should conclude that the scriptwriting itself is not an easy work. Scripts need writing, debugging, testing, being more or less universal, its bugs should be found and fixed, in an ideal case. Traditionally, scripts are harder for us to debug than applications of compiled frameworks. The stranger seems the state of affairs that scripters are obliged to create forms in addition to their hard work.

At that point, we turn to the third chart to see that scripters prefer tools belonged to a big class called Rapid Application Development instruments, which includes Primal Forms, Visual Studio and SharpDevelop. Need to admit that a few chose WPK, a framework combining simplicity of its object model and complexity of commandline tools.

However, these RAD tools are not what does all the work instead of us. You need not only paint controls and write the behind code, what’s about doing something intricate like a wizard? Such non-standard in the PowerShell world tasks (which are common for a decade or more in the world of RAD) are laborious. RAD tools are often shipped with out-of-the-box templates allowing you to add some code without thinking how to make wizard’s panels rotating on the Next button clicking.

Templates! What is the answer to the problem! PowerShell scripters need templates.

The code below demonstrates how can the user modify a template aiming to construct a wizard. Please note, that

  • for now, this is only a concept having no actions. In other words, it’s only easily to play around with the template, but adding the real code is not easy yet
  • there is no Finish and Cancel buttons
  • there is no way to disable the Next or Back buttons
  • and other restrictions persist (Do I sugar the pills if I say that the graphical tool allowing you to paint a wizard in a few clicks is coming,? I’m planning to publish the module at the beginning of the next week or slightly later)
cls
Set-StrictMode -Version 2
#region host preparations
if ($Host.Name -eq 'ConsoleHost')
{
	Add-Type -AssemblyName System.Windows.Forms;
	Add-Type -AssemblyName System.Drawing;
}
#endregion host preparations
#region the resulting wizard
	#region adjustable settings
		#region controls settings
		# Form size and caption
		[string]$script:constWizardInitialCaption = `
			'This is a sample wizard';
		[int]$script:constWizardWidth = `
			[System.Windows.Forms.SystemInformation]::VirtualScreen.Width / 2;
		[int]$script:constWizardHeight = `
			[System.Windows.Forms.SystemInformation]::VirtualScreen.Height / 2;
		# Buttons Next, Back
		[int]$script:constButtonHeight = 23;
		[int]$script:constButtonWidth = 75;
		[int]$script:constButtonAreaHeight = `
			$script:constButtonHeight * 2;
		[string]$script:constButtonNextName = '&Next';
		[string]$script:constButtonBackName = '&Back';
		# Step display name and description
		[int]$script:constLabelStepDisplayNameLeft = 5;
		[int]$script:constLabelStepDisplayNameTop = 0;
		[float]$script:constLabelStepDisplayNameFontSize = 16;
		[int]$script:constLabelStepDescriptionLeft = 5;
		[int]$script:constLabelStepDescriptionTop = 30;
		# Form properties
		[bool]$script:constWizardRigthToLeft = $false;
		# Initial step number
		[int]$script:currentStep = 0;
		#endregion controls settings
	#endregion adjustable settings
	#region mandatory settings
		#region Initialization of the SplitContainer controls
		# The outer split container
		[System.Windows.Forms.SplitContainer]$script:splitOuter = `
			New-Object System.Windows.Forms.SplitContainer;
			$script:splitOuter.Dock = [System.Windows.Forms.DockStyle]::Fill;
			$script:splitOuter.IsSplitterFixed = $true;
			$script:splitOuter.Orientation = `
				[System.Windows.Forms.Orientation]::Horizontal;
			$script:splitOuter.SplitterWidth = 5;
		# The inner split container
		[System.Windows.Forms.SplitContainer]$script:splitInner = `
			New-Object System.Windows.Forms.SplitContainer;
			$script:splitInner.Dock = [System.Windows.Forms.DockStyle]::Fill;
			$script:splitInner.IsSplitterFixed = $true;
			$script:splitInner.Orientation = `
				[System.Windows.Forms.Orientation]::Horizontal;
			$script:splitInner.SplitterWidth = 5;
			$script:splitOuter.Panel1.Controls.Add($script:splitInner);
		# The labels for the curent step name and description
		[System.Windows.Forms.Label]$script:lblStepDisplayName = `
			New-Object System.Windows.Forms.Label;
			$script:lblStepDisplayName.Left = `
				$script:constLabelStepDisplayNameLeft;
			$script:lblStepDisplayName.Top = `
				$script:constLabelStepDisplayNameTop;
			[System.Drawing.Font]$private:font = `
				$script:lblStepDisplayName.Font;
			$private:font = `
				New-Object System.Drawing.Font($private:font.Name, `
						$script:constLabelStepDisplayNameFontsize, `
			            $private:font.Style, $private:font.Unit, `
			            $private:font.GdiCharSet, $private:font.GdiVerticalFont );
			$script:lblStepDisplayName.Font = $private:font;
		[System.Windows.Forms.Label]$script:lblStepDescription = `
			New-Object System.Windows.Forms.Label;
			$script:lblStepDescription.Left = `
				$script:constLabelStepDescriptionLeft;
			$script:lblStepDescription.Top = `
				$script:constLabelStepDescriptionTop;
		$script:splitInner.Panel1.Controls.AddRange(($script:lblStepDisplayName, `
			$script:lblStepDescription));
		#endregion Initialization of the SplitContainer controls
		#region the Next and Back buttons
		[System.Windows.Forms.Button]$script:btnNext = `
			New-Object System.Windows.Forms.Button;
		$script:btnNext.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor `
			[System.Windows.Forms.AnchorStyles]::Right -bor `
			[System.Windows.Forms.AnchorStyles]::Top;
		$script:btnNext.Text = $script:constButtonNextName;
		[System.Windows.Forms.Button]$script:btnBack = `
			New-Object System.Windows.Forms.Button;
		$script:btnBack.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor `
			[System.Windows.Forms.AnchorStyles]::Right -bor `
			[System.Windows.Forms.AnchorStyles]::Top;
		$script:btnBack.Text = $script:constButtonBackName;
	    $script:btnBack.Enabled = $false;
		$script:splitOuter.Panel2.Controls.AddRange(($script:btnBack, $script:btnNext));
		#endregion the Next and Back buttons
		#region Initialization of the main form
		$script:frmWizard = $null;
		[System.Windows.Forms.Form]$script:frmWizard = `
			New-Object System.Windows.Forms.Form;
		$script:frmWizard.Controls.Add($script:splitOuter);

		if ($script:constWizardRigthToLeft)
		{
			$script:frmWizard.RightToLeft = `
				[System.Windows.Forms.RightToLeft]::Yes;
			$script:frmWizard.RightToLeftLayout = $true;
		}
		else
		{
			$script:frmWizard.RightToLeft = `
				[System.Windows.Forms.RightToLeft]::No;
			$script:frmWizard.RightToLeftLayout = $false;
		}
		$script:frmWizard.Text = $script:constWizardInitialCaption;
		#endregion Initialization of the main form
	#endregion mandatory settings
	#region the Wizard steps
	[System.Collections.ArrayList]$script:wzdSteps = `
		New-Object System.Collections.ArrayList;
		# Here we create an 'enumeration' (PSObject)
		# and begin filling ArrayList $script:wzdSteps with Panel controls
		[System.EventHandler]$script:hndlRunControlsAdd = `
			{try{$script:splitInner.Panel2.Controls.Add($script:wzdSteps[$script:currentStep]);}catch{Write-Debug $Error[0]; Write-Debug $global:Error[0];}};
			#region function New-WizardStep
		function New-WizardStep
		{
			param(
				  [string]$StepName,
				  [string]$StepDisplayName,
				  [string]$StepDescription = ''
				  )
			# Storing parameters in step arrays
			Add-Member -InputObject $script:steps -MemberType NoteProperty `
				-Name $StepName -Value $script:wzdSteps.Count;
			$null = $script:stepDisplayNames.Add($StepDisplayName);
			$null = $script:stepDescriptions.Add($StepDescription);
			# Create and add the new step's panel to the array
			[System.Windows.Forms.Panel]$private:panel = `
				New-Object System.Windows.Forms.Panel;
			$null = $script:wzdSteps.Add($private:panel);
			$script:currentStep = $script:wzdSteps.Count - 1;

			$script:splitInner.Panel2.Controls.Add($script:wzdSteps[$script:currentStep]);

			$script:wzdSteps[$script:currentStep].Dock = `
				[System.Windows.Forms.DockStyle]::Fill;
			# To restore initial state for this code running before the user accesses the wizard.
			$script:currentStep = 0;
		}
			#endregion function New-WizardStep
			#region function Add-ControlToStep
		function Add-ControlToStep
		{
			param(
				  [string]$StepNumber,
				  [string]$ControlType,
				  [string]$ControlName,
				  [int]$ControlTop,
				  [int]$ControlLeft,
				  [int]$ControlHeight,
				  [int]$ControlWidth,
				  [string]$ControlData
				 )
			$private:ctrl = $null;
			try{
				$private:ctrl = New-Object $ControlType;
			}catch{Write-Error "Unable to create a control of $($ControlType) type";}
			try{
				$private:ctrl.Name = $ControlName;
			}catch{Write-Error "Unable to set the Name property with value $($ControlName) to a control of the $($ControlType) type";}
			try{
				$private:ctrl.Top = $ControlTop;
			}catch{Write-Error "Unable to set the Top property with value $($ControlTop) to a control of the $($ControlType) type";}
			try{
				$private:ctrl.Left = $ControlLeft;
			}catch{Write-Error "Unable to set the Left property with value $($ControlLeft) to a control of the $($ControlType) type";}
			try{
				$private:ctrl.Height = $ControlHeight;
			}catch{Write-Error "Unable to set the Height property with value $($ControlHeight) to a control of the $($ControlType) type";}
			try{
				$private:ctrl.Width = $ControlWidth;
			}catch{Write-Error "Unable to set the Width property with value $($ControlWidth) to a control of the $($ControlType) type";}
			try{
				$private:ctrl.Text = $ControlData;
			}catch{Write-Error "Unable to set the Text property with value $($ControlData) to a control of the $($ControlType) type";}
			try{
				$wzdSteps[$StepNumber].Controls.Add($private:ctrl);
			}catch{Write-Error "Unable to add a control of $($ControlType) type to the step $($StepNumber)";}
		}
			#endregion function Add-ControlToStep
		# Step data arrays
		[psobject]$script:steps = New-Object psobject;
		[System.Collections.ArrayList]$script:stepDisplayNames = `
			New-Object System.Collections.ArrayList;
		[System.Collections.ArrayList]$script:stepDescriptions = `
			New-Object System.Collections.ArrayList;
	#endregion the Wizard steps
	#region events of the wizard controls
		#region resizing
			#region function Initialize-WizardControls
	function Initialize-WizardControls
	<#
		.SYNOPSIS
			The Initialize-WizardControls function sets the wizard common controls to predefined positions.

		.DESCRIPTION
			The Initialize-WizardControls function does the following:
			- sets Top, Left, Width and Height properties of the wizard buttons
			- positions of the step labels
			- sets the SplitterDistance property of both Splitcontainer controls

		.EXAMPLE
			PS C:\> Initialize-WizardControls
			This example shows how to step the wizard forward.

		.INPUTS
			No input

		.OUTPUTS
			No output
	#>
	{
		# Set sizes of buttons
		$script:btnNext.Height = $script:constButtonHeight;
		$script:btnNext.Width = $script:constButtonWidth;
		$script:btnBack.Height = $script:constButtonHeight;
		$script:btnBack.Width = $script:constButtonWidth;
		# SplitterDistance of the outer split container
		# in other words, the area where Next and Back buttons are placed
		$script:splitOuter.SplitterDistance = `
			$script:splitOuter.Height - `
			$script:constButtonAreaHeight;
		#if ($script:splitOuter.SplitterDistance -lt 0)
		#{$script:splitOuter.SplitterDistance = 10;}
		#$script:splitOuter.SplitterDistance = `
		#	$script:splitOuter.Height - `
		#	$script:constButtonAreaHeight;

		# Placements of the buttons
		if ($script:constWizardRigthToLeft)
		{
			$script:btnNext.Left = 10;
			$script:btnBack.Left = $script:constButtonWidth + 20;
		}
		else
		{
			$script:btnNext.Left = $script:splitOuter.Width - `
				$script:constButtonWidth - 10;
			$script:btnBack.Left = $script:splitOuter.Width - `
				$script:constButtonWidth - `
				$script:constButtonWidth - 20;
		}
		$script:btnNext.Top = `
			($script:constButtonAreaHeight - $script:constButtonHeight) / 2;
		$script:btnBack.Top = `
				($script:constButtonAreaHeight - $script:constButtonHeight) / 2;

		# SplitterDistance of the inner split container
		# this is the place where step name is placed
		$script:splitInner.SplitterDistance = `
			$script:constButtonAreaHeight * 1.5;
			#$script:splitOuter.Panel2.Height * 1.5;
		#if ($script:splitInner.SplitterDistance -lt 0)
		#{$script:splitInner.SplitterDistance = 10;}

		# Step Display Name and Description labels
		$script:lblStepDisplayName.Width = `
			$script:splitInner.Panel1.Width - `
			$script:constLabelStepDisplayNameLeft * 2;
		$script:lblStepDescription.Width = `
			$script:splitInner.Panel1.Width - `
			$script:constLabelStepDescriptionLeft * 2;

		# Refresh after we have changed placements of the controls
		[System.Windows.Forms.Application]::DoEvents();
	}
			#endregion function Initialize-WizardControls
	[System.EventHandler]$script:hndlFormResize = {Initialize-WizardControls;}
	[System.EventHandler]$script:hndlFormLoad = {Initialize-WizardControls;}
	#[System.Windows.Forms.MouseEventHandler]$script:hndlSplitMouseMove = `
	#	{Initialize-WizardControls;}
	# Initial arrange on Load form.
	$script:frmWizard.add_Load($script:hndlFormLoad);
	#$script:frmWizard.add_Resize($script:hndlFormResize);
	$script:splitOuter.add_Resize($script:hndlFormResize);
		#endregion resizing
		#region steps
			#region function Invoke-WizardStep
	function Invoke-WizardStep
	<#
		.SYNOPSIS
			The Invoke-WizardStep function sets active panel on the wizard form.

		.DESCRIPTION
			The Invoke-WizardStep function does the following:
			- changes internal variable $script:currentStep
			- sets/resets .Enabled property of btnNext and btnBack
			- changes .Dock and .Left properties of every panel

		.PARAMETER  Forward
			The optional parameter Forward is used to point out the direction the wizard goes.

		.EXAMPLE
			PS C:\> Invoke-WizardStep -Forward $true
			This example shows how to step the wizard forward.

		.INPUTS
			System.Boolean

		.OUTPUTS
			No output
	#>
	{
		[CmdletBinding()]
		param(
			  [Parameter(Mandatory=$false)]
			  [bool]$Forward = $true
			  )
		Begin{}
		Process{
		if ($Forward)
		{
			$script:btnBack.Enabled = $true;
			if ($script:currentStep -lt ($script:wzdSteps.Count - 1))
			{$script:currentStep++;}
			if ($script:currentStep -lt ($script:wzdSteps.Count - 1))
			{$script:btnNext.Enabled = $true;}
			else
			{$script:btnNext.Enabled = $false;}
		}
		else
		{
			$script:btnNext.Enabled = $true;
			if ($script:currentStep -gt 0)
			{$script:currentStep--;}
			if ($script:currentStep -gt 0)
			{$script:btnBack.Enabled = $true;}
			else
			{$script:btnBack.Enabled = $false;}
		}
		for($private:i = 0; $private:i -lt $script:wzdSteps.Count;
			$private:i++)
		{
			if ($private:i -ne $script:currentStep)
			{
				$script:wzdSteps[$private:i].Dock = `
					[System.Windows.Forms.DockStyle]::None;
				$script:wzdSteps[$private:i].Left = 10000;
			}
			else
			{
				$script:wzdSteps[$private:i].Dock = `
					[System.Windows.Forms.DockStyle]::Fill;
				$script:wzdSteps[$private:i].Left = 0;
			}
		}
		$script:lblStepDisplayName.Text = `
			$script:stepDisplayNames[$script:currentStep];
		$script:lblStepDescription.Text = `
			$script:stepDescriptions[$script:currentStep];
		}
		End{}
	}
			#endregion function Invoke-WizardStep
			#region function Initialize-WizardStep
	function Initialize-WizardStep
	# This is the selfsufficient function doing all the necessary
	# calculations for controls on each panel.
	# Also from the code can be seen how to address the panel you are interesting in
	# using the 'enumeration' created earlier
	# for example, $script:wzdSteps[$script:steps.Welcome]
	{
		$script:lblStepDisplayName.Text = `
			$script:stepDisplayNames[$script:currentStep];
		$script:lblStepDescription.Text = `
			$script:stepDescriptions[$script:currentStep];
	}
			#endregion function Initialize-WizardStep
	[System.EventHandler]$hndlStepForward = {
		# serve controls' data
		Initialize-WizardStep;
		# switch panels
		Invoke-WizardStep $true;
	}
	[System.EventHandler]$hndlStepBack = {
		# switch panels
		Invoke-WizardStep $false;
	}
	$script:btnNext.add_Click($script:hndlStepForward);
	$script:btnBack.add_Click($script:hndlStepBack);
		#endregion steps
	#endregion events of the wizard controls
	#region wizard initialization
		#region function Initialize-Wizard
	function Initialize-Wizard
	# This is one more selfsufficient function written to make
	# the latest preparations for the form run
	{
		#region control settings
		$script:frmWizard.Width = $script:constWizardWidth;
		$script:frmWizard.Height = $script:constWizardHeight;
		#endregion control settings
		Initialize-WizardStep;
	}
		#endregion function Initialize-Wizard
	#endregion wizard initialization
#endregion the resulting wizard

		# Step 1: Welcome
		New-WizardStep 'Welcome' 'This is the first step' 'Welcome to the PowerShell Wizard, the our dear customer!';
		# Add a label
		# Please note that we can use the enumeration $steps which is being created runtime
		# on a call of the New-WizardStep function
		Add-ControlToStep $steps.Welcome System.Windows.Forms.Label 'lblWelcome' 20 10 50 300 'This Wizard carries you through the steps you need to collect the files from a given path';

		# Step 2
		New-WizardStep 'Input' 'Step Two' 'Here you type some in controls, plz';
		# Add a label
		Add-ControlToStep $steps.Input System.Windows.Forms.Label 'lblInput' 20 10 20 300 'Please type the path to a catalog';
		# Add a text box
		Add-ControlToStep $steps.Input System.Windows.Forms.TextBox 'txtInput' 40 10 20 300

		# Unfortunately, there is no way right now to test the user input with ease
		# So we can disable the Next button manually (to be improved soon)
		# this code works wrong:
		#if ($wzdSteps[$steps.Input].Controls['txtInput'].Text.Length -eq 0)
		#{
		#	$btnNext.Enabled = $false;
		#}
		#else
		#{
		#	$btnNext.Enabled = $true;
		#}

		# Step 3
		New-WizardStep 'Progress' 'The third one' 'Wait, please. Sip a coffee' ;
		# Add a progress bar
		Add-ControlToStep $steps.Progress 'System.Windows.Forms.ProgressBar' 'pbDir' 200 50 100 400

		# Step 4
		New-WizardStep 'Output' 'Fourth' 'Now awake and read the output';
		# Add a list box
		Add-ControlToStep $steps.Output System.Windows.Forms.ListBox lbxFiles 50 50 300 400

		# Step 5: Finish
        New-WizardStep 'Finish' 'Finish!' 'Bye!';

		Initialize-Wizard;
		# Set the second step as active
		Invoke-WizardStep -Forward $true;

		$script:frmWizard.ShowDialog() | Out-Null;

Sorry for such a long output! The same code attached here.

WordPress inspired me with ‘Copy a Post’ tool (StatusStrip + ProgressBar)

leave a comment »

Today’s update announced by WordPress motivated me to add a ProgressBar to the same post that is about a StatusStrip. Why? One of two today’s new features is ‘Copy a Post’. What is it? In spite of the availability of that page, I’ll say a little about blogger’s work.

Usually, the PowerShell (or of any programming environment) blogger needs an idea, a time, a code that will be put out and a description. Also, categories and/or tags to make a post visible throughout the Internet.

WordPress provided such a very simple, even boring feature as a copying of existing post. But imagine that on May, 1st I bought a brand new, lightweight Android 2.2 netbook. A great thing for blogging, skyping and facebooking, it was brought to Finland on 2nd and used on the way to see Google maps. How long did I manage not to write a post? I wrote one today, not a post, but a pleasantry great deal of code. Great under these unusual circumstances as a non-Windows/Linux keyboard layout and lack of habitual tools support.

I used some RDP application (demo with no more one host at a time) to connect to my home Windows 7 with Script Editor and ISE. Some key sequences on an Android’s are not habitual, there aren’t such keys as Insert, Home (to a beginning of text string I mean), Del, all Fs (it’s really pity if you use Far or Unreal Commander). The right shift worked as Tab, the left one went weird from time to time, but the vast majority o code was written on Android’s keyboard!

When I was ready to save me first draft accomplished on an Android device, I noticed these features. How do you think whether I was glad or excited after all that fighting with Android OS? The post will be put out later, maybe tomorrow.

And now, this Copy a Post feature again helps me: I thought that there isn’t a reason not to all System.Windows.Forms.ProgressBar example to the very similar StatusBar code. No doubts, Copy a Post is the first I did.

After this long introduction, let’s turn to a sample. I added a progress bar, three radio buttons, two group boxes (to split up radio buttons to two groups) and added some additional code. Should I add more details? If so, please write request in comments.

I’ll simply describe the functions until questions arrived:

– setStatusStrip used for labels and a bar of the $script:stStrip control

– setProgressBar is the same but for a ProgressBar

– createRadioButton creates radio buttons and adds a handler to enable the user set a style to the StatusStrip and ProgressBar

cls
Set-StrictMode -Version Latest
#region $form1
[System.Windows.Forms.Form]$form1 = New-Object System.Windows.Forms.Form;
$form1.Width = 500;
$form1.Height = 500;
[System.Windows.Forms.Button]$btnDirC = New-Object System.Windows.Forms.Button;
$btnDirC.Text = 'dir c:\';
$btnDirC.Left = 300;
$btnDirC.Top = 100;
$btnDirC.add_Click(([System.EventHandler]$handler =
	{
		processDirC;
	}));
$form1.Controls.Add($btnDirC);
[System.Windows.Forms.GroupBox]$script:groupProgressBar = `
	New-Object System.Windows.Forms.GroupBox;
$script:groupProgressBar.Left = 20;
$script:groupProgressBar.Top = 20;
$script:groupProgressBar.Width = 250;
$script:groupProgressBar.Height = 100;
$script:groupProgressBar.Text = "ProgressBar";
[System.Windows.Forms.GroupBox]$script:groupStatusStrip = `
	New-Object System.Windows.Forms.GroupBox;
$script:groupStatusStrip.Left = 20;
$script:groupStatusStrip.Top = 200;
$script:groupStatusStrip.Width = 250;
$script:groupStatusStrip.Height = 150;
$script:groupStatusStrip.Text = "StatusStrip";
$form1.Controls.AddRange(@($script:groupProgressBar, $script:groupStatusStrip));
function processDirC
{
	[int]$maxNumber = (dir c:\).Length;
	setStatusStrip -OperationName $null `
		-OperationProgress $null `
		-ProgressBarMinimum 1 `
		-ProgressBarMaximum $maxNumber `
		-ProgressBarValue 1;
	setProgressBar -ProgressBarMinimum 1 `
		-ProgressBarMaximum $maxNumber `
		-ProgressBarValue 1;
	[int]$counter = 1;
	dir c:\ | `
		%{
          setStatusStrip -OperationName $_ `
			-OperationProgress "$($counter) of $($maxNumber)" `
			-ProgressBarMinimum $null `
			-ProgressBarMaximum $null `
			-ProgressBarValue $counter;
		  setProgressBar  -ProgressBarMinimum $null `
			-ProgressBarMaximum $null `
			-ProgressBarValue $counter;
          [System.Windows.Forms.Application]::DoEvents();
		  sleep -Milliseconds 500;
		  $counter++;
		  }
}
#endregion $form1
#region StatusBar
$form1.Controls.Add(
	(([System.Windows.Forms.StatusStrip]$script:stStrip = `
	#region StatusBar properties
	New-Object System.Windows.Forms.StatusStrip) `
	| %{#at the bottom of the window
		$script:stStrip.Dock = [System.Windows.Forms.DockStyle]::Bottom;
		$script:stStrip.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor `
			[System.Windows.Forms.AnchorStyles]::Left;
		#layout style by default
		$script:stStrip.LayoutStyle = [System.Windows.Forms.ToolStripLayoutStyle]::Table;
		#operation name
		[System.Windows.Forms.ToolStripLabel]$script:stStripLabelOperation = `
			New-Object System.Windows.Forms.ToolStripLabel;
		$script:stStripLabelOperation.Text = "Write the operation name here";
		#which item is being processed
		[System.Windows.Forms.ToolStripLabel]$script:stStripLabelProgress = `
			New-Object System.Windows.Forms.ToolStripLabel;
		$script:stStripLabelProgress.Text = "Write the item number here";
		#the progress bar
		[System.Windows.Forms.ToolStripProgressBar]$script:stStripProgressBar = `
			New-Object System.Windows.Forms.ToolStripProgressBar;
		$script:stStrip.Items.AddRange([System.Windows.Forms.ToolStripItem[]]@(
									$script:stStripLabelOperation,
									$script:stStripLabelProgress,
									$script:stStripProgressBar
									));
		$script:stStrip.Name = "stStrip";
		$script:stStrip.AutoSize = $true;
		$script:stStrip.Left = 0;
		$script:stStrip.Visible = $true;
		$script:stStrip.Enabled = $true;
		$script:stStripLabelOperation.Width = 50;
		$script:stStripLabelProgress.Width = 50;
		$script:stStripProgressBar.Width = 50;
		$script:stStripProgressBar.Visible = $false;
	#endregion StatusBar properties
	$script:stStrip;}
)	);
#endregion StatusBar
#region ToolStripLayoutStyle
	#region function createRadioButton
function createRadioButton
{
	param([string]$ControlName,
		  [ref]$Top
		  )
	$rb = $null;
	[System.Windows.Forms.RadioButton]$private:rb = `
		New-Object System.Windows.Forms.RadioButton;
	$private:rb.Name = $ControlName;
	$private:rb.Text = $ControlName;
	$private:rb.Left = 20;
	$private:rb.Top = ($Top.Value += 20);
	$private:rb.Width = 200;
	$private:rb.add_CheckedChanged(([System.EventHandler]$handler =
	{
		param($sender)
		if ($sender.Checked){
			if ($sender.Parent -eq $script:groupStatusStrip)
			{
				$script:stStrip.LayoutStyle = `
					([System.Windows.Forms.ToolStripLayoutStyle]($sender.Name));
			}
			else
			{
				$script:pgBar.Style = `
					([System.Windows.Forms.ProgressBarStyle]($sender.Name));
			}
		}
	}));
	return $private:rb;
}
	#endregion function createRadioButton
[int]$top = 0;
$script:groupStatusStrip.Controls.AddRange(
	[System.Windows.Forms.Control[]]@(
	(createRadioButton ([System.Windows.Forms.ToolStripLayoutStyle]::Flow.ToString()) ([ref]$top)),
	(createRadioButton ([System.Windows.Forms.ToolStripLayoutStyle]::HorizontalStackWithOverflow.ToString()) ([ref]$top)),
	(createRadioButton ([System.Windows.Forms.ToolStripLayoutStyle]::StackWithOverflow.ToString()) ([ref]$top)),
	(createRadioButton ([System.Windows.Forms.ToolStripLayoutStyle]::Table.ToString()) ([ref]$top)),
	(createRadioButton ([System.Windows.Forms.ToolStripLayoutStyle]::VerticalStackWithOverflow.ToString()) ([ref]$top))
	));
$script:groupStatusStrip.Controls[([System.Windows.Forms.ToolStripLayoutStyle]::Table.ToString())].Checked = $true;
#endregion ToolStripLayoutStyle
#region ProgressBarStyle
[int]$top = 0;
$script:groupProgressBar.Controls.AddRange(
	[System.Windows.Forms.Control[]]@(
	(createRadioButton ([System.Windows.Forms.ProgressBarStyle]::Blocks.ToString()) ([ref]$top)),
	(createRadioButton ([System.Windows.Forms.ProgressBarStyle]::Continuous.ToString()) ([ref]$top)),
	(createRadioButton ([System.Windows.Forms.ProgressBarStyle]::Marquee.ToString()) ([ref]$top))
	));
$script:groupProgressBar.Controls[([System.Windows.Forms.ProgressBarStyle]::Blocks.ToString())].Checked = $true;
#endregion ProgressBarStyle
#region function setStatusStrip
function setStatusStrip
{
	param($OperationName = "",
		  $OperationProgress = "",
		  $ProgressBarMinimum = 0,
		  $ProgressBarMaximum = 0,
		  $ProgressBarValue = 0
		  )
		try{$null = $script:stStrip;
			if ($OperationName -ne $null -and `
				$script:stStripLabelOperation -ne $null)
				{
					$script:stStripLabelOperation.Text = $OperationName;
					$script:stStripLabelOperation.Width = 200;
				}
			if ($OperationProgress -ne $null -and `
				$script:stStripLabelProgress -ne $null)
				{
					$script:stStripLabelProgress.Text = $OperationProgress;
					$script:stStripLabelProgress.Width = 100;
				}
			if ($script:stStripProgressBar -ne $null){
				if ($ProgressBarMinimum -ne $null)
					{$script:stStripProgressBar.Minimum = $ProgressBarMinimum;}
				if ($ProgressBarMaximum -ne $null)
					{$script:stStripProgressBar.Maximum = $ProgressBarMaximum;}
				if ($ProgressBarValue -ne $null)
					{$script:stStripProgressBar.Value = $ProgressBarValue;}
				if ($script:stStripProgressBar.Minimum -eq `
					$script:stStripProgressBar.Maximum)
					{$script:stStripProgressBar.Visible = $false;}
				else
					{$script:stStripProgressBar.Visible = $true;}
			}
		}
		catch{}
}
#endregion function setStatusStrip
#region ProgressBar
[System.Windows.Forms.ProgressBar]$script:pgBar = New-Object System.Windows.Forms.ProgressBar;
$script:pgBar.Left = 100;
$script:pgBar.Top = 150;
$script:pgBar.Visible = $true;
$script:pgBar.Minimum = 1;
$form1.Controls.Add($script:pgBar);
	#region function setProgressBar
function setProgressBar
{
	param($ProgressBarMinimum = 0,
		  $ProgressBarMaximum = 0,
		  $ProgressBarValue = 0
		  )
		try{$null = $script:pgBar;
			if ($script:pgBar -ne $null){
				if ($ProgressBarMinimum -ne $null)
					{$script:pgBar.Minimum = $ProgressBarMinimum;}
				if ($ProgressBarMaximum -ne $null)
					{$script:pgBar.Maximum = $ProgressBarMaximum;}
				if ($ProgressBarValue -ne $null)
					{$script:pgBar.Value = $ProgressBarValue;}
				if ($script:pgBar.Minimum -eq `
					$script:pgBar.Maximum)
					{$script:pgBar.Visible = $false;}
				else
					{$script:pgBar.Visible = $true;}
			}
		}
		catch{}
}
	#endregion function setProgressBar
#endregion ProgressBar
$form1.ShowDialog() | Out-Null;

The code is downloadable as a script from here.

StatusStrip. An easy way

leave a comment »

Long operations are among things annoying most. To prevent users from getting bored with application that is mining data or doing multi-change, it’s common to use a kind of progressbar.

Let’s take a StatusStrip control. It’s relatively new control, firstly introduced in .NET 2.0 (or like) and it’s container. This container allows you to add a label, a progress bar and a couple of other controls.

Below is a typical function working with a statusstrip that contains two label and one progress bar. For our purposes, the first label is operation name (the name of an action performed now) and the second is a counter like ‘3 of 87’.

cls
Set-StrictMode -Version Latest
#region $form1
[System.Windows.Forms.Form]$form1 = New-Object System.Windows.Forms.Form;
$form1.Width = 500;
[System.Windows.Forms.Button]$btnDirC = New-Object System.Windows.Forms.Button;
$btnDirC.Text = 'dir c:\';
$btnDirC.Left = 300;
$btnDirC.Top = 100;
$btnDirC.add_Click(([System.EventHandler]$handler = 
	{
		processDirC;
	}));
$form1.Controls.Add($btnDirC);
function processDirC
{
	[int]$maxNumber = (dir c:\).Length;
	setStatusStrip -OperationName $null `
		-OperationProgress $null `
		-ProgressBarMinimum 1 `
		-ProgressBarMaximum $maxNumber `
		-ProgressBarValue 1;
	[int]$counter = 1;
	dir c:\ | `
		%{
          setStatusStrip -OperationName $_ `
			-OperationProgress "$($counter) of $($maxNumber)" `
			-ProgressBarMinimum $null `
			-ProgressBarMaximum $null `
			-ProgressBarValue $counter;
          [System.Windows.Forms.Application]::DoEvents();
		  sleep -Milliseconds 500;
		  $counter++;
		  }
}
#endregion $form1
#region StatusBar
$form1.Controls.Add(
	(([System.Windows.Forms.StatusStrip]$script:stStrip = `
	#region StatusBar properties
	New-Object System.Windows.Forms.StatusStrip) `
	| %{#at the bottom of the window
		$script:stStrip.Dock = [System.Windows.Forms.DockStyle]::Bottom;
		$script:stStrip.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor `
			[System.Windows.Forms.AnchorStyles]::Left;
		#layout style by default
		$script:stStrip.LayoutStyle = [System.Windows.Forms.ToolStripLayoutStyle]::Table;
		#operation name
		[System.Windows.Forms.ToolStripLabel]$script:stStripLabelOperation = `
			New-Object System.Windows.Forms.ToolStripLabel;
		$script:stStripLabelOperation.Text = "Write the operation name here";
		#which item is being processed
		[System.Windows.Forms.ToolStripLabel]$script:stStripLabelProgress = `
			New-Object System.Windows.Forms.ToolStripLabel;	
		$script:stStripLabelProgress.Text = "Write the item number here";
		#the progress bar
		[System.Windows.Forms.ToolStripProgressBar]$script:stStripProgressBar = `
			New-Object System.Windows.Forms.ToolStripProgressBar;
		$script:stStrip.Items.AddRange([System.Windows.Forms.ToolStripItem[]]@(
									$script:stStripLabelOperation,
									$script:stStripLabelProgress,
									$script:stStripProgressBar
									));					
		$script:stStrip.Name = "stStrip";							
		$script:stStrip.AutoSize = $true;
		$script:stStrip.Left = 0;
		$script:stStrip.Visible = $true;
		$script:stStrip.Enabled = $true;
		$script:stStripLabelOperation.Width = 50;
		$script:stStripLabelProgress.Width = 50;
		$script:stStripProgressBar.Width = 50;
		$script:stStripProgressBar.Visible = $false;
	#endregion StatusBar properties
	$script:stStrip;}
)	);
#endregion StatusBar
#region ToolStripLayoutStyle
	#region function createRadioButton
function createRadioButton
{
	param([string]$ControlName,
		  [ref]$Top
		  )
	$rb = $null;
	[System.Windows.Forms.RadioButton]$private:rb = `
		New-Object System.Windows.Forms.RadioButton;
	$private:rb.Name = $ControlName;
	$private:rb.Text = $ControlName;
	$private:rb.Left = 20;
	$private:rb.Top = ($Top.Value += 20);
	$private:rb.add_CheckedChanged(([System.EventHandler]$handler = 
	{
		param($sender)
		if ($sender.Checked){
		$script:stStrip.LayoutStyle = `
			([System.Windows.Forms.ToolStripLayoutStyle]($sender.Name));
		}
	}));
	return $private:rb;
}
	#endregion function createRadioButton
[int]$top = 0;
$form1.Controls.AddRange(
	[System.Windows.Forms.Control[]]@(
	(createRadioButton ([System.Windows.Forms.ToolStripLayoutStyle]::Flow.ToString()) ([ref]$top)),
	(createRadioButton ([System.Windows.Forms.ToolStripLayoutStyle]::HorizontalStackWithOverflow.ToString()) ([ref]$top)),
	(createRadioButton ([System.Windows.Forms.ToolStripLayoutStyle]::StackWithOverflow.ToString()) ([ref]$top)),
	(createRadioButton ([System.Windows.Forms.ToolStripLayoutStyle]::Table.ToString()) ([ref]$top)),
	(createRadioButton ([System.Windows.Forms.ToolStripLayoutStyle]::VerticalStackWithOverflow.ToString()) ([ref]$top))
	));
$form1.Controls[([System.Windows.Forms.ToolStripLayoutStyle]::Table.ToString())].Checked = $true;
#endregion ToolStripLayoutStyle
#region function setStatusStrip
function setStatusStrip
{
	param($OperationName = "",
		  $OperationProgress = "",
		  $ProgressBarMinimum = 0,
		  $ProgressBarMaximum = 0,
		  $ProgressBarValue = 0
		  )
		try{$null = $script:stStrip;
			if ($OperationName -ne $null -and `
				$script:stStripLabelOperation -ne $null)
				{
					$script:stStripLabelOperation.Text = $OperationName;
					$script:stStripLabelOperation.Width = 200;
				}
			if ($OperationProgress -ne $null -and `
				$script:stStripLabelProgress -ne $null)
				{
					$script:stStripLabelProgress.Text = $OperationProgress;
					$script:stStripLabelProgress.Width = 100;
				}
			if ($script:stStripProgressBar -ne $null){
				if ($ProgressBarMinimum -ne $null)
					{$script:stStripProgressBar.Minimum = $ProgressBarMinimum;}
				if ($ProgressBarMaximum -ne $null)
					{$script:stStripProgressBar.Maximum = $ProgressBarMaximum;}
				if ($ProgressBarValue -ne $null)
					{$script:stStripProgressBar.Value = $ProgressBarValue;}
				if ($script:stStripProgressBar.Minimum -eq `
					$script:stStripProgressBar.Maximum)
					{$script:stStripProgressBar.Visible = $false;}
				else
					{$script:stStripProgressBar.Visible = $true;}
			}
		}
		catch{}
}
#endregion function setStatusStrip
$form1.ShowDialog() | Out-Null;

In short, the sample consists of a form $form1, a button that demonstrates a comparatively long operation, the status bar itself and five radio buttons showing how the style can be changed.

The setStatusBar function sets texts for the first label (the name of each file), the second label (the counter), minimum and maximum of the progress bar (can be set once), the current progress bar value (in my sample is the same as value used for the second label).

The code is downloadable as a script from here.