简体   繁体   中英

Powershell $button.add_click executes the whole code inside the add_click block

I am new to WPF and event-handling using powershell, what I want to achieve is on click of a button, the progress bar should be shown from 0-100. But when I am running the following piece of code, it computes the whole code inside the add_click block and it only shows the last iteration in the loop. I know maybe it is a silly solution, but I do need some help in this.

$syncHash = [hashtable]::Synchronized(@{})
$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"         
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)

$psCmd = [PowerShell]::Create().AddScript({   
    $XamlPath="C:\Forms\MyFormsv1.xaml" 
    $inputXML = Get-Content -Path $XamlPath
    [xml]$Global:xmlWPF = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N'  -replace '^<Win.*', '<Window'

    #Add WPF and Windows Forms assemblies
    try{
    Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase,system.windows.forms
    } catch {
    Throw “Failed to load Windows Presentation Framework assemblies.”
    }


    $syncHash.Window=[Windows.Markup.XamlReader]::Load((new-object System.Xml.XmlNodeReader $xmlWPF))

    [xml]$XAML = $xmlWPF

    $xmlWPF.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | %{
    $Global:synchash.Add($_.Name,$synchash.Window.FindName($_.Name) )}

    function tool1_progress{
        $var1=50
        for($index=1 ; $index -le $var1; $index++)
        {            
            [int]$tmpProgNum= ($index/$var1) * 100
            $syncHash.tool1_pb.Value= $tmpProgNum
            $syncHash.consoleOutput.Text=$index
            Start-Sleep -Milliseconds 250 
        }
    }
    $syncHash.myButton.add_click({tool1_progress})  
    $syncHash.Window.ShowDialog() | out-null      
})
$psCmd.Runspace = $newRunspace
$data = $psCmd.BeginInvoke()

$tool1 is a label. $tool1_pb is a progress bar. $consoleOutput is a text box.

I integrate WPF and PowerShell all the time. This website helped me immensely. You can't update a UI that's running on the same thread, so you need to invoke a new runspace using BeginInvoke()

$psCmd = [PowerShell]::Create().AddScript({   
$Global:uiHash.Error = $Error
Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase
$xaml = @"YOURXAMLHERE"
$Global:uiHash.Window=[Windows.Markup.XamlReader]::Parse($xaml )

[xml]$XAML = $xaml
    $xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | %{
    $Global:uihash.Add($_.Name,$uihash.Window.FindName($_.Name) )}
$Global:uiHash.Window.ShowDialog() | out-null
})
$psCmd.Runspace = $newRunspace
$handle = $psCmd.BeginInvoke()

Then to update whatever it is you need, you would use Window.Dispatcher.Invoke

$Global:uiHash.Window.Dispatcher.Invoke([action]{$Global:uiHash.Window.Title = "MyWindowTitle"},"Normal")

EDIT

$Global:uiHash.Button1.Add_Click(tool1_progress)

EDIT

Throw in your XML and this will work. You have to create another runspace on top of the other in order to keep updating the textbox and progress bar. Note that synchash MUST be $Global:synchash . Functions courtesy of this post.

$Global:syncHash = [hashtable]::Synchronized(@{})
$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"         
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)

$psCmd = [PowerShell]::Create().AddScript({   
$XamlPath="C:\Forms\MyFormsv1.xaml" 
$inputXML = @"YOURXMLHERE"@
[xml]$Global:xmlWPF = $inputXML 

#Add WPF and Windows Forms assemblies
try{
Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase,system.windows.forms
} catch {
Throw “Failed to load Windows Presentation Framework assemblies.”
}


$Global:syncHash.Window=[Windows.Markup.XamlReader]::Load((new-object System.Xml.XmlNodeReader $xmlWPF))

[xml]$XAML = $xmlWPF

$xmlWPF.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | %{
$Global:synchash.Add($_.Name,$Global:syncHash.Window.FindName($_.Name) )}

function Start-Runspace{
param($scriptblock)
$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"         
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable("SyncHash",$global:synchash)
$psCmd = [PowerShell]::Create().AddScript($ScriptBlock)
$psCmd.Runspace = $newRunspace
$psCMD.BeginInvoke()
}

   $SB = { 
    $var1=50
    for($index=1 ; $index -le $var1; $index++)
    {            
        [int]$tmpProgNum= ($index/$var1) * 100
        $Global:syncHash.Window.Dispatcher.Invoke([action]{$Global:Synchash.tool1_pb.value = "$tmpprognum"},"Normal")
        $Global:syncHash.Window.Dispatcher.Invoke([action]{$Global:Synchash.consoleoutput.text = "$index"},"Normal")
        Start-Sleep -Milliseconds 250 

    }

}


$Global:syncHash.myButton.add_click({Start-Runspace $SB})  
$Global:syncHash.Window.ShowDialog() | out-null      
})
$psCmd.Runspace = $newRunspace
$data = $psCmd.BeginInvoke()
start-sleep -Milliseconds 300

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM