PowerShell and WPF: Radio Button

I talked about working with CheckBoxes in my previous article and mentioned briefly how you can have them behave like a Radio button, but in the end, nothing beats the actual thing no matter how close you can make another control behave. A Radio button only allows a single selection for its specific group. When you select something else, the current item will be unchecked while the new item is checked and there are only two options unlike a checkbox: Checked and Unchecked.

#Build the GUI
[xml]$xaml = @"
<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="Window" Title="Initial Window" WindowStartupLocation = "CenterScreen"
    SizeToContent = "WidthAndHeight" ShowInTaskbar = "True" Background = "lightgray"> 
    <StackPanel > 
        <RadioButton x:Name="Item1" Content = 'Item1'/>
        <RadioButton x:Name="Item2" Content = 'Item2'/>
        <RadioButton x:Name="Item3" Content = 'Item3'/>  
        <TextBox />      
    </StackPanel>
</Window>
"@
 
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )


$xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach {
    Set-Variable -Name ($_.Name) -Value $Window.FindName($_.Name)
}

$Window.Showdialog() | Out-Null

 

image

Nothing really special here. I use <RadioButton> in my XAML code to create the control. By default, every radio button is part of a default group which means that all radio buttons will follow the same suit of being unchecked while a single button is checked. There is a property that you can use called GroupName which can be used to (as the name implies) group radio buttons to handle different parts and lets you have multiple selections with radio buttons.

#Build the GUI
[xml]$xaml = @"
<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="Window" Title="Initial Window" WindowStartupLocation = "CenterScreen"
    SizeToContent = "WidthAndHeight" ShowInTaskbar = "True" Background = "lightgray"> 
    <StackPanel > 
        <RadioButton x:Name="Item1" Content = 'Item1(Group1)' GroupName='Group1'/>
        <RadioButton x:Name="Item2" Content = 'Item2(Group1)' GroupName='Group1'/>
        <RadioButton x:Name="Item3" Content = 'Item3(Group1)' GroupName='Group1'/>  
        <Separator/>
        <RadioButton x:Name="Item4" Content = 'Item4(Group2)' GroupName='Group2'/>  
        <RadioButton x:Name="Item5" Content = 'Item5(Group2)' GroupName='Group2'/>  
        <RadioButton x:Name="Item6" Content = 'Item6(Group2)' GroupName='Group2'/>  
        <TextBox />      
    </StackPanel>
</Window>
"@
 
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )

$xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach {
    Set-Variable -Name ($_.Name) -Value $Window.FindName($_.Name)
}

$Window.Showdialog() | Out-Null

image

Anything in Group1 is bound by the limitations of the radio button and the same goes for the items in Group2. Only one item in each group can be checked, but because I am using GroupNames, I can have one of each checked in their respective groups.

If you are not a fan of the default foreground and background colors, we can change those up as well using the Background and Foreground properties in the XAML code.

#Build the GUI
[xml]$xaml = @"
<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="Window" Title="Initial Window" WindowStartupLocation = "CenterScreen"
    SizeToContent = "WidthAndHeight" ShowInTaskbar = "True" Background = "lightgray"> 
    <StackPanel x:Name='StackPanel'> 
        <RadioButton x:Name="Item1" Content = 'Item1(Group1)' GroupName='Group1' Background='Black' Foreground='Red'/>
        <RadioButton x:Name="Item2" Content = 'Item2(Group1)' GroupName='Group1' Background='Black' Foreground='Red'/>
        <RadioButton x:Name="Item3" Content = 'Item3(Group1)' GroupName='Group1' Background='Black' Foreground='Red'/>  
        <Separator/>
        <RadioButton x:Name="Item4" Content = 'Item4(Group2)' GroupName='Group2' Background='Yellow' Foreground='Blue'/>  
        <RadioButton x:Name="Item5" Content = 'Item5(Group2)' GroupName='Group2' Background='Yellow' Foreground='Blue'/>  
        <RadioButton x:Name="Item6" Content = 'Item6(Group2)' GroupName='Group2' Background='Yellow' Foreground='Blue'/>  
        <TextBox />      
    </StackPanel>
</Window>
"@
 
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )

$xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach {
    Set-Variable -Name ($_.Name) -Value $Window.FindName($_.Name)
}

$Window.Showdialog() | Out-Null

image

The background property defines the background color of the radio button itself while the foreground property defines the text and the radio button when it has been checked.

Much like a Checkbox, we can find out which button is checked using the IsChecked property.

$StackPanel.Children | Where {
    $_ -is [system.windows.controls.radiobutton] -and $_.IsChecked
} | Select Name

 

image

We can also handle events much like the checkboxes on the Checked event.

#Build the GUI
[xml]$xaml = @"
<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="Window" Title="Initial Window" WindowStartupLocation = "CenterScreen"
    SizeToContent = "WidthAndHeight" ShowInTaskbar = "True" Background = "lightgray"> 
    <StackPanel x:Name='StackPanel'> 
        <RadioButton x:Name="Item1" Content = 'Item1'/>
        <RadioButton x:Name="Item2" Content = 'Item2'/>
        <RadioButton x:Name="Item3" Content = 'Item3'/>  
        <Separator/>
        <TextBox x:Name='textbox'/>      
    </StackPanel>
</Window>
"@
 
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )

$xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach {
    Set-Variable -Name ($_.Name) -Value $Window.FindName($_.Name) -Scope Script
}

#Bubble up event handler
[System.Windows.RoutedEventHandler]$Script:CheckedEventHandler = {
    $TextBox.Text = $_.source.name
}
$StackPanel.AddHandler([System.Windows.Controls.RadioButton]::CheckedEvent, $CheckedEventHandler)

$Window.Showdialog() | Out-Null

 

image

The magic here happens with the following code:

#Bubble up event handler
[System.Windows.RoutedEventHandler]$Script:CheckedEventHandler = {
    $TextBox.Text = $_.source.name
}
$StackPanel.AddHandler([System.Windows.Controls.RadioButton]::CheckedEvent, $CheckedEventHandler)

 

I a creating a routed event handler which is basically a script block that be used when a specified event occurs. In this case, the scriptblock just takes the object returned from the event (System.Windows.RoutedEventArgs).

image

The scriptblock then uses the Source property which has the control object (radio button) that we can then use to update the textbox. I set the event handler on the StackPanel which allows me to not have to worry about setting the event handler on each radio button.

That is all to working with Radio buttons in WPF using PowerShell. If you have any questions or other things that you would like to see, feel free to leave a comment!

Posted in powershell, WPF | Tagged , , , | 1 Comment

PowerShell and WPF: Checkboxes

Continuing on with my series on PowerShell and WPF, I am going to talk about using checkboxes in a form.

Checkboxes are a great way to allow a user to select a specific item or items prior to moving on with an action.

An example of this is below:

#Build the GUI
[xml]$xaml = @"
<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="Window" Title="Initial Window" WindowStartupLocation = "CenterScreen"
    SizeToContent = "WidthAndHeight" ShowInTaskbar = "True" Background = "lightgray"> 
    <StackPanel > 
        <CheckBox x:Name="Item1" Content = 'Item1'/>
        <CheckBox x:Name="Item2" Content = 'Item2'/>
        <CheckBox x:Name="Item3" Content = 'Item3'/>  
        <TextBox />      
    </StackPanel>
</Window>
"@
 
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )

$Item1 = $Window.FindName('Item1')
$Item2 = $Window.FindName('Item2')
$Item3 = $Window.FindName('Item3')

$Window.Showdialog() | Out-Null

image

With this approach, we can check 1 or more of these checkboxes.

image

With these items checked, we can check the IsChecked property for each checkbox to see that they have been checked which is great when using a comparison operator such as –eq or –ne.

image

So far, we have seen how there are two states: checked and unchecked. But there is also another state that you can use which requires you to set the property IsThreeState = ‘True’ on the checkbox control. By doing so, you now get something like this:

image

What do you think will happen when we look at the IsChecked properties of each checkbox, especially $Item2.

image

In this case, the checkbox with the alternate checkbox doesn’t have a True or False property, it is simply Null. Something to keep in mind when working with the IsThreeState property.

I have covered a couple of the more useful properties, now it is time to look at a few events that relate to each of the types of checkbox IsChecked properties.

Using the Checked event, whenever the checkbox is set to the IsChecked=$True property, an event can kick off. Same goes for the Unchecked event with IsProperty=$False. Indeterminate event only works when the checkbox is not a checkbox and not unchecked.

I’ve added a couple more textboxes to handle each checkbox.

#Build the GUI
[xml]$xaml = @"
<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="Window" Title="Initial Window" WindowStartupLocation = "CenterScreen"
    SizeToContent = "WidthAndHeight" ShowInTaskbar = "True" Background = "lightgray"> 
    <StackPanel >
        <CheckBox x:Name="Item1" Content = 'Item1' IsThreeState = 'True' />
        <CheckBox x:Name="Item2" Content = 'Item2' IsThreeState = 'True' />
        <CheckBox x:Name="Item3" Content = 'Item3' IsThreeState = 'True' />  
        <TextBox x:Name='Item1_txt' />      
        <TextBox x:Name='Item2_txt' />      
        <TextBox x:Name='Item3_txt' />      
    </StackPanel>
</Window>
"@
 
$reader=(New-Object System.Xml.XmlNodeReader $xaml)

#Connect to Controls
$Window=[Windows.Markup.XamlReader]::Load( $reader )

$Item1 = $Window.FindName('Item1')
$Item2 = $Window.FindName('Item2')
$Item3 = $Window.FindName('Item3')
$Item1_txt = $Window.FindName('Item1_txt')
$Item2_txt = $Window.FindName('Item2_txt')
$Item3_txt = $Window.FindName('Item3_txt')

#Events
## Item1
$Item1.Add_Checked({
    $Item1_txt.Text = "$($This.name) State: Checked"
})
$Item1.Add_UnChecked({
    $Item1_txt.Text = "$($This.name) State: Unchecked"
})
$Item1.Add_Indeterminate({
    $Item1_txt.Text = "$($This.name) State: Indeterminate"
})

## Item2
$Item2.Add_Checked({
    $Item2_txt.Text = "$($This.name) State: Checked"
})
$Item2.Add_UnChecked({
    $Item2_txt.Text = "$($This.name) State: Unchecked"
})
$Item2.Add_Indeterminate({
    $Item2_txt.Text = "$($This.name) State: Indeterminate"
})

## Item3
$Item3.Add_Checked({
    $Item3_txt.Text = "$($This.name) State: Checked"
})
$Item3.Add_UnChecked({
    $Item3_txt.Text = "$($This.name) State: Unchecked"
})
$Item3.Add_Indeterminate({
    $Item3_txt.Text = "$($This.name) State: Indeterminate"
})


$Window.Showdialog() | Out-Null

 

image

If you are looking to have a group of checkboxes in which only one can be selected in the group, then you have 2 options:

  • Create an event handler to manually uncheck the other checkboxes when a checkbox is checked
  • Use the RadioButton control instead in which this is the controls purpose.

I won’t cover the RadioButton here, but will show a quick example of using a event to to uncheck other checkboxes when one has been checked.

I removed the IsThreeState property from this round and will only focus on the two possible options. Rather than make an event for each checkbox, I am going to make a single event scriptblock and apply it to each control.

#Events
## Checked Events
[System.Windows.RoutedEventHandler]$Script:CheckBoxChecked = {
    $Window.Content.Children | Where {
        $_ -is [System.Windows.Controls.CheckBox] -AND $This.Name  -ne $_.Name
    } | ForEach {
        $_.IsChecked = $False
    }
}
$Window.Content.Children | Where {
    $_ -is [System.Windows.Controls.CheckBox]
} | ForEach {
    $_.AddHandler([System.Windows.Controls.CheckBox]::CheckedEvent, $CheckBoxChecked)
}

Here I define the scriptblock to look for all checkboxes which do not match the name of the checkbox that had been clicked to show a checkbox. From there, I go through each checkbox and set the IsChecked property to $False. After applying each of these event scriptblocks to all of my Checkbox controls, we are set to try and click each checkbox to see if I can have more than one at a time.

image

That is all to working with the Checkbox control using PowerShell and WPF!

Posted in powershell, WPF | Tagged , , | 1 Comment

Building a Clipboard History Viewer Using PowerShell

I saw a question a while back in the Technet PowerShell Forum asking how one might start to build a clipboard viewer using PowerShell that met a few requirements:

  • Have an open window aside from the PowerShell console
  • Automatically list new clipboard items as they come in
  • Allow for filtering to find specific items

Not a lot of requirements here, but they are definitely some nice ones to have for this type of request.

I couldn’t really resist this type of challenge and found it to be a lot of fun. I already had a template of sorts from a previous UI that I created here.

[xml]$xaml = @"
<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="Window" Title="Powershell Clipboard History Viewer" WindowStartupLocation = "CenterScreen" 
    Width = "350" Height = "425" ShowInTaskbar = "True" Background = "White">
    <Grid >
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.Resources>
            <Style x:Key="AlternatingRowStyle" TargetType="{x:Type Control}" >
                <Setter Property="Background" Value="LightGray"/>
                <Setter Property="Foreground" Value="Black"/>
                <Style.Triggers>
                    <Trigger Property="ItemsControl.AlternationIndex" Value="1">
                        <Setter Property="Background" Value="White"/>
                        <Setter Property="Foreground" Value="Black"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Grid.Resources>
        <Menu Width = 'Auto' HorizontalAlignment = 'Stretch' Grid.Row = '0'>
        <Menu.Background>
            <LinearGradientBrush StartPoint='0,0' EndPoint='0,1'>
                <LinearGradientBrush.GradientStops> 
                <GradientStop Color='#C4CBD8' Offset='0' /> 
                <GradientStop Color='#E6EAF5' Offset='0.2' /> 
                <GradientStop Color='#CFD7E2' Offset='0.9' /> 
                <GradientStop Color='#C4CBD8' Offset='1' /> 
                </LinearGradientBrush.GradientStops>
            </LinearGradientBrush>
        </Menu.Background>
            <MenuItem x:Name = 'FileMenu' Header = '_File'>
                <MenuItem x:Name = 'Clear_Menu' Header = '_Clear' />
            </MenuItem>
        </Menu>
        <GroupBox Header = "Filter"  Grid.Row = '2' Background = "White">
            <TextBox x:Name="InputBox" Height = "25" Grid.Row="2" />
        </GroupBox>
        <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"  
        Grid.Row="3" Height = "Auto">
            <ListBox x:Name="listbox" AlternationCount="2" ItemContainerStyle="{StaticResource AlternatingRowStyle}" 
            SelectionMode='Extended'>
            <ListBox.Template>
                <ControlTemplate TargetType="ListBox">
                    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderBrush}">
                        <ItemsPresenter/>
                    </Border>
                </ControlTemplate>
            </ListBox.Template>
            <ListBox.ContextMenu>
                <ContextMenu x:Name = 'ClipboardMenu'>
                    <MenuItem x:Name = 'Copy_Menu' Header = 'Copy'/>      
                    <MenuItem x:Name = 'Remove_Menu' Header = 'Remove'/>  
                </ContextMenu>
            </ListBox.ContextMenu>
            </ListBox>
        </ScrollViewer >
    </Grid>
</Window>
"@

 

image

Now to connect to some controls so I can use them and their events later on.

$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )

#Connect to Controls
$listbox = $Window.FindName('listbox')
$InputBox = $Window.FindName('InputBox')
$Copy_Menu = $Window.FindName('Copy_Menu')
$Remove_Menu = $Window.FindName('Remove_Menu')
$Clear_Menu = $Window.FindName('Clear_Menu')

Ok, with the front end UI out of the way and I have connected to my controls, I can now work to add some code to handle some events and perform various actions based on those events.

But before that, I am going to wrap this UI up within a runspace so I can leave my PowerShell console open for other things.

$Runspacehash = [hashtable]::Synchronized(@{})
$Runspacehash.Host = $Host
$Runspacehash.runspace = [RunspaceFactory]::CreateRunspace()
$Runspacehash.runspace.ApartmentState = "STA"
$Runspacehash.runspace.Open() 
$Runspacehash.runspace.SessionStateProxy.SetVariable("Runspacehash",$Runspacehash)
$Runspacehash.PowerShell = {Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase}.GetPowerShell() 
$Runspacehash.PowerShell.Runspace = $Runspacehash.runspace 
$Runspacehash.Handle = $Runspacehash.PowerShell.AddScript({ 
# All my code for this UI
}).BeginInvoke()

 

The trick to the clipboard viewer is a little type call [Windows.Clipboard]  which is only available if you do the following (note this is already available if using the ISE):

Add-Type -AssemblyName PresentationCore

Now you can check out all of the cool static methods which are available.

[windows.clipboard] | 
Get-Member -Static -Type Method

image

I am only focused on Text related methods this time around (GetText() and SetText()) as well as Clear(). But with this I can view and manipulate the contents of the clipboard. I am going to create some custom functions that will utilize this as well as work with some of the UI as well to make things a little easier.

Function Get-ClipBoard {
    [Windows.Clipboard]::GetText()
}
Function Set-ClipBoard {
    $Script:CopiedText = @"
$($listbox.SelectedItems | Out-String)
"@
    [Windows.Clipboard]::SetText($Script:CopiedText)
}
Function Clear-Viewer {
    [void]$Script:ObservableCollection.Clear()
    [Windows.Clipboard]::Clear()
}

With these three functions, I can re-use them when it comes to multiple events that I have planned rather than re-writing the same code for more than one event.

The only thing left to do here is to start working with the events on the various controls that will handle all of the operations of this window.

The first event that I have is one of the most important as it is a Timer which checks the clipboard contents and adds them to the observable collection which in turn updates the listbox on the UI.

$Window.Add_SourceInitialized({
    #Create observable collection
    $Script:ObservableCollection = New-Object System.Collections.ObjectModel.ObservableCollection[string]
    $Listbox.ItemsSource = $Script:ObservableCollection

    #Create Timer object
    $Script:timer = new-object System.Windows.Threading.DispatcherTimer 
    $timer.Interval = [TimeSpan]"0:0:.1"

    #Add event per tick
    $timer.Add_Tick({
        $text =  Get-Clipboard
        If (($Script:Previous -ne $Text -AND $Script:CopiedText -ne $Text) -AND $text.length -gt 0) {
            #Add to collection
            [void]$Script:ObservableCollection.Add($text)
            $Script:Previous = $text
        }     
    })
    $timer.Start()
    If (-NOT $timer.IsEnabled) {
        $Window.Close()
    }
})

The timer is set to 100 milliseconds which should be plenty of time to handle items being added to the clipboard. The observable collection is binded to the listbox ItemsSource property (actually the view is binded, not the actual collection which is useful when we set up a filter later on in this article) which can auto update the UI as things are added to the collection. See this article on more examples of this.

 

Speaking of a filter, one of the requirements was being able to type in some text in a text box and have the contents start filtering automatically. This is accomplished using the TextChanged event on the textbox and using a Predicate for the DefaultCollectionView Filter property of the ListBox.

$InputBox.Add_TextChanged({
    [System.Windows.Data.CollectionViewSource]::GetDefaultView($Listbox.ItemsSource).Filter = [Predicate[Object]]{             
        Try {
            $args[0] -match [regex]::Escape($InputBox.Text)
        } Catch {
            $True
        }
    }    
})

As the text is changing in the textbox, the filter will look for anything that matches it. Pretty handy approach to real time filtering of data.

I also wanted to add some keystroke shortcuts as well to give it a more polished feel.

$Window.Add_KeyDown({ 
    $key = $_.Key  
    If ([System.Windows.Input.Keyboard]::IsKeyDown("RightCtrl") -OR [System.Windows.Input.Keyboard]::IsKeyDown("LeftCtrl")) {
        Switch ($Key) {
        "C" {
            Set-ClipBoard          
        }
        "R" {
            @($listbox.SelectedItems) | ForEach {
                [void]$Script:ObservableCollection.Remove($_)
            }            
        }
        "E" {
            $This.Close()
        }
        Default {$Null}
        }
    }
})

For this, I currently have the following shortcuts available:

  • Ctrl+E
    • Exits the application
  • Ctrl+R
    • Removes the selected items from the clipboard viewer
  • Ctrl+C
    • Copies the selected items from the clipboard viewer

The rest of the events handle things such as closing the application gracefully, handling the menu clicks on the context menu of the listbox and on the title menu as well as giving focus to the filter textbox at startup.

$Clear_Menu.Add_Click({
    Clear-Viewer
})
$Remove_Menu.Add_Click({
    @($listbox.SelectedItems) | ForEach {
        [void]$Script:ObservableCollection.Remove($_)
    }
})
$Copy_Menu.Add_Click({
    Set-ClipBoard
})
$Window.Add_Activated({
    $InputBox.Focus()
})
$Window.Add_Closed({
    $Script:timer.Stop()
    $Script:ObservableCollection.Clear()
    $Runspacehash.PowerShell.Dispose()
})
$listbox.Add_MouseRightButtonUp({
    If ($Script:ObservableCollection.Count -gt 0) {
        $Remove_Menu.IsEnabled = $True
        $Copy_Menu.IsEnabled = $True
    } Else {
        $Remove_Menu.IsEnabled = $False
        $Copy_Menu.IsEnabled = $False
    }
})

After running the script…

.\ClipboardHistoryViewer.ps1

… we may see an item already being shown (it depends on if something is already in the clipboard) and can then start copying various amounts of text and see the viewer start filling up and also select multiple items and then right-click to copy or remove them (or using the keyboard shortcuts).

image

I can filter for just the script download location.

image

I can also use the File>Clear menu to clear out all of the contents of the viewer as well as the clipboard.

Currently these are all of the features that I have for this version, but if others would like to see new things, I can certainly see about updating it.

Download the PowerShell Clipboard History Viewer

http://gallery.technet.microsoft.com/scriptcenter/PowerShell-Clipboard-c414ec78

Posted in powershell | Tagged , , | Leave a comment

Guest Spot on Hey, Scripting Guy! Talking ForEach

Check out my latest guest spot on Hey, Scripting Guy! talking about using ForEach, ForEach-Object and … ForEach (the alias of ForEach-Object). Give it a read and let me know what you think!

http://blogs.technet.com/b/heyscriptingguy/archive/2014/07/08/getting-to-know-foreach-and-foreach-object.aspx

Posted in powershell | Tagged , , | Leave a comment

Quick Hits: Did I Really Lose My Output With Receive-Job By Not Using–Keep?

We have all probable been there at some point. We use Start-Job to run a command in the background like this:

Start-Job -ScriptBlock {
    Get-Process | Where {
        $_.WS -gt 100MB
    }
} -Name HighMemProcess

image

Take note of the HasMoreData property. This means that we have data available which can be used with Receive-Job to get the output stream of the background job.

When completed, we typically used Receive-Job to get the data from the output of the PSJob.

Receive-Job -Name HighMemProcess

image

 

Now let’s go back to the job and see what is there.

image

HasMoreData is now showing false, which means that if we use Receive-Job again against this job, no data will be returned.

image

That could be problematic if we didn’t use the –Keep parameter to ensure that the data will still be available in the PSJob because now we have to re-run the code again.

But wait! There is a way to still pull the data without having to re-run the job. Let’s examine the job a little more and see what is available.

Get-Job -Name HighMemProcess | Select *

image

What we are seeing is actually a parent job of the child job (Job3) which is actually doing the work. This parent job is really just monitoring the state of the child job and let’s you know when it has completed. With that knowledge, let’s take a look at the child job and see what is there.

$Job = Get-Job -Name HighMemProcess
$Job.ChildJobs | Select *

SNAGHTMLa7e8728

Check out the Output stream, all of the data we need that was originally available when using Receive-Job is still there!

$Job.ChildJobs.output

image

 

Now we know that even if we accidently forget to use the –Keep parameter on Receive-Job we can still dig into the child job of the parent job and pull the data from the Output stream.

Posted in powershell | Tagged , , , | 1 Comment