PowerShell and WPF: Textbox

The next control in my series on working with WPF using PowerShell is Textboxes. The textbox control is as it sounds, a box that can hold text. You can use it both as a input box as well as an output box to display information. Using this control, you can give people greater control over what they can add to query, pick a computer to run something against or anything else that you can think of.

Lets get started by creating a single textbox to write text in.

#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" ResizeMode="NoResize"
    SizeToContent = "WidthAndHeight" ShowInTaskbar = "True" Background = "lightgray"> 
    <StackPanel >  
        <Label Content='Type in this textbox' />
        <TextBox x:Name="InputBox" Height = "50" />  
    </StackPanel>
</Window>
"@
 
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )


$Window.ShowDialog() | Out-Null

You can type in the textbox and you will notice that as soon as you hit the end of the textbox, the text will just continue on but you won’t be able to see what is at the beginning of the textbox unless you make the window larger! How can we fix this? I will show you after the pictures below.

image

image

image

Returns and Text Wrapping

Ok, so this works and such, but try hitting Enter on your keyboard and watch what happens. Absolutely nothing! Why does this happen you ask? It is because we did not specify the AcceptsReturn property and set it to True. Once we do that, the Textbox will respect the Enter key and move to the next line. Also, to make this respond better, I will set the TextWrapping property on this control to Wrap. By doing this, instead of cutting of some of the text when it reaches the end, it will actually continue displaying the text on the next line.

#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"
    Width = "25" Height = "120" ShowInTaskbar = "True" Background = "lightgray"> 
    <StackPanel >  
        <Label Content='Type in this textbox' />
        <TextBox x:Name="InputBox" Height = "80" AcceptsTab="True" AcceptsReturn="True"
        TextWrapping="Wrap" VerticalScrollBarVisibility = "Auto"/> 
    </StackPanel>
</Window>
"@
 
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )


$Window.ShowDialog() | Out-Null

image

Tabbing and ScrollBars

A couple of other items that I added were the AcceptsTab=True and VerticalScrollBarVisibility=Auto. This allows me to use the Tab key and also if the text goes beyond the set height of the Textbox, it will add a scroll bar that you can use to scroll and read the text as shown below.

image

Input/Output Window

Ok, now lets do something a little different by setting a input/output window. With this, whatever you type in the bottom of the window, will show up in the top window but will be in a read-only state. So you can copy it, but not write to the top textbox. Kind of like what you would see with a chat client.

#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" ResizeMode="NoResize"
    Width = "313" Height = "280" ShowInTaskbar = "True" Background = "lightgray"> 
    <StackPanel >
        <Label Content='Text sent will appear below' />
        <TextBox x:Name="OutputBox" IsReadOnly = "True" Height = "75"
        TextWrapping="Wrap" VerticalScrollBarVisibility = "Auto"/>    
        <StackPanel Orientation = 'Horizontal'>  
            <Button x:Name = "button1" Height = "75" Width = "75" Content = 'Send' Background="Yellow" />
            <Button x:Name = "button2" Height = "75" Width = "75" Content = 'Clear' Background="Yellow" Margin = "50,0,0,0" />  
        </StackPanel>
        <Label Content='Type in this textbox and press Send' />
        <TextBox x:Name="InputBox" Height = "50" AcceptsTab="True" AcceptsReturn="True" 
        TextWrapping="Wrap" VerticalScrollBarVisibility = "Auto"/>  
    </StackPanel>
</Window>
"@
 
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )

#Connect to Controls
$button1 = $Window.FindName('button1')
$button2 = $Window.FindName('button2')
$OutputBox = $Window.FindName('OutputBox')
$InputBox = $Window.FindName('InputBox')

#Events
$button1.Add_Click({
    $OutputBox.AppendText(("{0}`r" -f $InputBox.Text))
    $InputBox.Clear()
})

$button2.Add_Click({
    $InputBox.Clear()
    $OutputBox.Clear()
})

$Window.ShowDialog() | Out-Null

You will notice some events have been set up to handle the button clicks for the Send and Clear buttons. The send button will take the text from the input textbox that resides in the $Inputbox.Text property and copy it over to the $Outputbox. The event will also clear out the $Inputbox by using the Clear() method. The same method is used during the Clear button event when pressed.

image

Also notice that everything I did with tabbing and returning as well as the text wrapping is respected in the output textbox. Now if the input textbox was smaller and the output textbox was larger, the text wrapping may have not happened.

Spellchecking

Believe it or not, but spellchecking is pretty painless to setup with a textbox. Simply add the following to your XAML code for the Textbox that will support spell checking:

SpellCheck.IsEnabled="True"

Now when you type a misspelled word, it will give you the familiar red underline and let you change the spelling or ignore by right clicking on the word.

image

TextChanged Event

One last thing to show that is pretty cool is the TextChanged event. What this means that whenever the text changes (just like the event says!), you can specify an action to take! Pretty cool stuff to use! Lets take a look at an example that will filter the available PowerShell cmdlets as you start typing the name.

#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" ResizeMode="NoResize"
    Width = "313" Height = "425" ShowInTaskbar = "True" Background = "lightgray"> 
    <StackPanel >
        <Label Content='Cmdlets will appear below' />
        <TextBox x:Name="OutputBox" IsReadOnly = "True" Height = "300"
        TextWrapping="Wrap" VerticalScrollBarVisibility = "Auto"/>    
        <StackPanel Orientation = 'Horizontal'>  
 
        </StackPanel>
        <Label Content='Type in this textbox' />
        <TextBox x:Name="InputBox" Height = "50" AcceptsTab="True" AcceptsReturn="True" 
        TextWrapping="Wrap" VerticalScrollBarVisibility = "Auto"  SpellCheck.IsEnabled="True"/>  
    </StackPanel>
</Window>
"@
 
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )

#Connect to Controls
$OutputBox = $Window.FindName('OutputBox')
$InputBox = $Window.FindName('InputBox')

#Events
$InputBox.Add_TextChanged({
    $cmdlets = @(Get-Command -CommandType Cmdlet -Name ("{0}*" -f $InputBox.Text) | Select -ExpandProperty Name)
    $OutputBox.Text = ($cmdlets | ForEach {"{0}`r" -f $_})
})

$Window.ShowDialog() | Out-Null

image

image

Again, it is pretty neat to do. If I were using a listbox to display the output, I could then add some other events to allow you to view the help for a cmdlet, but that will be for another article. Other items that I didn’t touch on but can be used are the various Font properties available for the textboxes that I have shown in previous articles.

Updated SysInfo Tool

If you remember the simple utility I wrote in my last article to get system information via WMI, it was lacking a bit by using labels to handle the output. That changes with textboxes for both handling the output better, but also allowing me to add better query information such as picking a remote system and even specifying the WMI class to query!

image

image

image

Updated SysInfo_Textbox Code

Function Invoke-WMIQuery {
        $wmiClass = ($WMI_txtbx.Text).Trim()
    Try {        
        $Data = Get-WmiObject -ComputerName $Computer_txtbx.Text -Class $wmiClass -ErrorAction Stop
        $OutputBox.Foreground = "White"
        $OutputBox.Text = $Data | Out-String
    } Catch {
        $OutputBox.Foreground = "Red"
        $OutputBox.Text = $_.Exception.Message
    }
}
#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="WMI Query" WindowStartupLocation = "CenterScreen" ResizeMode="NoResize"
    SizeToContent = "WidthAndHeight" ShowInTaskbar = "True" Background = "lightgray"> 
    <StackPanel Orientation = 'Horizontal'>
        <StackPanel>    
            <StackPanel Orientation = 'Horizontal'>  
                <Label Content = "Computername"/>        
                <TextBox x:Name="Computer_txtbx" Height = "25" Width = "122" AcceptsReturn="True" SpellCheck.IsEnabled="True" />   
            </StackPanel>
            <StackPanel Orientation = 'Horizontal'>  
                <Label Content = "WMI Class"/>        
                <TextBox x:Name="WMI_txtbx" Height = "25" Width = "150" AcceptsReturn="True" SpellCheck.IsEnabled="True" />   
            </StackPanel>            
            <StackPanel Orientation = 'Horizontal'>
                <Button x:Name = "query_btn" Height = "50" Width = "75" Content = 'Query' Background="Yellow"/>
                <Button x:Name = "clear_btn" Height = "50" Width = "75" Content = 'Clear' Background="Yellow" Margin = "50,0,0,0" />  
            </StackPanel>
        </StackPanel>    
        <TextBox x:Name="OutputBox" IsReadOnly = "True" Height = "300" Width = "500" TextWrapping="Wrap" 
        Background = "Black" Foreground = 'White' FontWeight = 'Bold' VerticalScrollBarVisibility = "Visible"/>            
    </StackPanel>
</Window>
"@
 
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load( $reader )

#Connect to Controls
$query_btn = $Window.FindName('query_btn')
$clear_btn = $Window.FindName('clear_btn')
$OutputBox = $Window.FindName('OutputBox')
$Computer_txtbx = $Window.FindName('Computer_txtbx')
$WMI_txtbx = $Window.FindName('WMI_txtbx')

#Events
$query_btn.Add_Click({    
    Invoke-WMIQuery
})

$clear_btn.Add_Click({
    $OutputBox.Clear()
})

$Window.Add_KeyDown({
    If ($_.Key -eq "F5") {
        Invoke-WMIQuery
    }
})

$Window.ShowDialog() | Out-Null

Up Next

Up next in my continuing series on WPF will be TextBlock.

This entry was posted in powershell, WPF and tagged , , . Bookmark the permalink.

7 Responses to PowerShell and WPF: Textbox

  1. EM says:

    Hello Boe,
    I would like to see an example of the autocomplete, like you have, bit doesn’t smash the query that is in the change event. Ie: If you’re querying 50K Users in AD, you don’t want to smash that query on each character typed.

  2. Pingback: Error creating WPF text box in PowerShell. - How to Code .NET

  3. Pingback: [Question] WPF real-time search of list of AD groups read into program? - How to Code .NET

  4. FoxDeploy says:

    Hey Boe, never saw this before. It’s AWESOME! Do you think you could add a handler for any event just by using the syntax Add_, like Add_LostFocus()?

    I’m really curious now!

  5. Pingback: PowerShell and WPF: ListBox | Learn Powershell | Achieve More

Leave a comment