1

I have the following script that should prompt the user to enter data in two fields and assign the input to variables. If the user clicks the OK button, without entering data, I want the form to pop-up again. If they click the Cancel button, the form should close.

When I paste the code into PowerShell, it works fine, but when I run it in a .ps1 file, the UI never closes, regardless of what I do. I have tried running the .ps1 in both normal and elevated sessions. It seems to me that the $userCancelled variable is never changing to $true and I have no idea why. Any ideas? Thanks.

Function Start-PropertyGui {
[CmdletBinding()]

$inputXML = @"
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="UI" Height="227.396" Width="392.131">
    <Grid HorizontalAlignment="Left" Width="382">
        <TextBlock x:Name="textBlock" Margin="10,10,10,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="43"><Run FontWeight="Bold" Text="Click the Request button to request a new token or enter an existing token (ID and key) and click the OK button."/></TextBlock>
        <Button x:Name="requestButton" Content="Request API Token" HorizontalAlignment="Left" Margin="19,0,0,118" VerticalAlignment="Bottom"/>
        <Label x:Name="idLabel" Content="Access ID*" Margin="19,80,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="idTxt" Height="26" Margin="112,80,26,0" TextWrapping="Wrap" VerticalAlignment="Top" ToolTip=""/>
        <Label x:Name="keyLabel" Content="Access Key*" Margin="19,107,0,0" VerticalAlignment="Top"/>
        <TextBox x:Name="keyTxt" Height="26" Margin="112,107,26,0" TextWrapping="Wrap" VerticalAlignment="Top" ToolTip=""/>
        <Button x:Name="okButton" Content="OK" HorizontalAlignment="Left" Margin="19,0,0,19" VerticalAlignment="Bottom" Width="50" IsDefault="True"/>
        <Button x:Name="cancelButton" Content="Cancel" HorizontalAlignment="Left" Margin="80,0,0,19" VerticalAlignment="Bottom" Width="50"/>
    </Grid>
</Window>
"@

    $inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'

    [void][System.Reflection.Assembly]::LoadWithPartialName('PresentationFramework')
    [xml]$XAML = $inputXML

    write-host ("{0}: Attempting to read the XAML data." -f (Get-Date -Format s))

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

    # This line takes the XAML data, adds "WPF" to the front of each XML node name, and creates a global variable.
    $xaml.SelectNodes("//*[@Name]") | Foreach {Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name) -Scope Global}

    # Specify what the 'request' button should do, when clicked.
    $WPFrequestButton.Add_Click({<#thisbuttonworks#>})

    # Specify what the OK button should do, when clicked.
    $WPFokButton.Add_Click({Set-Variable -Name AccessId -Value $WPFidtxt.Text -Scope Global; Set-Variable -Name AccessKey -Value $WPFkeytxt.text -Scope Global; $form.Close()})
    $WPFcancelButton.Add_Click({Set-Variable -Name userCancelled -Value $true -Scope Global -Force -ErrorAction Stop; $form.close()})

    #Now that the form is built, show it, surpressing other messages.
    write-host ("{0}: Starting UI." -f (Get-Date -Format s))

    $form.ShowDialog() | Out-Null
}

$AccessId = $null;$AccessKey = $null
$userCancelled = $false

Do {
    write-host ("{0}: Opening the UI." -f (Get-Date -Format s))

    Start-PropertyGui
} Until (
    (($userCancelled -eq $true) -or (($AccessId.length -eq 6) -and ($AccessKey.length -eq 10) -and ($userCancelled -eq $false)))
)
If ($userCancelled -eq $true) {
    write-host ("{0}: The user cancelled the collection of properties. To prevent errors, {1} will exit." -f (Get-Date -Format s), $MyInvocation.MyCommand)
}

write-host ("{0}: Using {1} and {2}." -f (Get-Date -Format s), $AccessId, $AccessKey)
3
  • 1
    Are you hitting scope issues somehow? Try changing all references of $userCancelled, $AccessId, and $AccessKey to $global:userCancelled, $global:AccessId, and $global:AccessKey Commented Jan 24, 2018 at 17:13
  • @TheMadTechnician called it! Just changed a copy of your code with those changes and it works just fine. Scoping ftw Commented Jan 24, 2018 at 18:21
  • Yup, thanks @TheMadTechnician. If you propose it as an answer, I'll mark it as such. Commented Jan 24, 2018 at 18:36

1 Answer 1

2

Looks like this is a scope issue. Since you are defining the variables once, then again as global, the direct instances need to be set to use the global scoped versions.

To fix it change all references of $userCancelled, $AccessId, and $AccessKey to $global:userCancelled, $global:AccessId, and $global:AccessKey.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.