从Runspace(S)中的WPF GUI中的多个功能



我有一个带有计时器的WPF PowerShell GUI:

    ##############################################
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")  

    $sync = [hashtable]::Synchronized(@{})
    ##############################################
    ##############################################Start form
    $Form = New-Object System.Windows.Forms.Form    
    $Form.Size = New-Object System.Drawing.Size(600,400)
    $Form.Text = "Testor"
    $Form.MaximizeBox = $false
    ############################################## Start functions
    Function Netchk {
    $wlanchk1 = netsh wlan show interfaces | Select-String 'sSSID'
    if ($wlanchk1 -ne $null){$wlanchk = $wlanchk1 -replace ('s','')
    $wlanchk -replace 'SSID:','Connected to:  '}else{$wlanchk = "No wlan connected"}
    $outputBox.Text = $wlanchk
    }

    ############################################## end functions
    ############################################## Start group box
    $groupBox = New-Object System.Windows.Forms.GroupBox
    $groupBox.Location = New-Object System.Drawing.Size(10,10) 
    $groupBox.Autosize = $true 
    $groupBox.text = "Groupbox: " 
    $Form.Controls.Add($groupBox) 
    ############################################## end group box
    ############################################## Start  buttons
    $Button1 = New-Object System.Windows.Forms.Button 
    $Button1.Location = new-object System.Drawing.Point(15,25) 
    $Button1.Size = New-Object System.Drawing.Size(200,30)
    $Button1.Text = "Button1" 
    $groupBox.Controls.Add($Button1)
    $Button1.Add_click({netchk})
    $Button2 = New-Object System.Windows.Forms.Button
    $Button2.Location = new-object System.Drawing.Point(15,55)
    $Button2.Size = New-Object System.Drawing.Size(200,30)
    $Button2.Text = "Button2"
    $groupBox.Controls.Add($Button2)
    $Button2.Add_click({})

    $Button3 = New-Object System.Windows.Forms.Button
    $Button3.Location = new-object System.Drawing.Point(15,85)
    $Button3.Size = New-Object System.Drawing.Size(200,30)
    $Button3.Text = "Button3"
    $groupBox.Controls.Add($Button3)
    $Button3.Add_click({})
    $Button4 = New-Object System.Windows.Forms.Button
    $Button4.Location = new-object System.Drawing.Point(15,115)
    $Button4.Size = New-Object System.Drawing.Size(200,30)
    $Button4.Text = "Button4"
    $groupBox.Controls.Add($Button4)
    $Button4.Add_click({})
    ############################################## end  buttons

    ############################################## Start text field
    $outputBox = New-Object System.Windows.Forms.TextBox 
    $outputBox.Location = New-Object System.Drawing.Size(10,200) 
    $outputBox.Size = New-Object System.Drawing.Size(565,150) 
    $outputBox.MultiLine = $True 
    $outputBox.ScrollBars = "Vertical"
    $outputBox.Text = 0
    $Form.Controls.Add($outputBox)
    $Form.Controls.AddRange(@($sync.Textbox))
    ############################################## end text field

    ############################################## start label
    $InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
    $label1 = New-Object System.Windows.Forms.Label
    $label1.Location = New-Object Drawing.Point (385, 30)
    $label1.Width = 100
    $label1.Height = 60
    $label1.Text = 0
    $label1.Font = New-Object System.Drawing.Font("Courier New",32,1,2,0)
    ############################################## end label
    ############################################## start timer
    $timer1 = New-Object System.Windows.Forms.Timer
    $timer1.Interval = 1000
    $timer1.Enabled = $true
    $time = 60
    $script:StartTime = (Get-Date).AddSeconds($Time)
    $timer1_OnTick = {
    [TimeSpan]$span = $script:StartTime - (Get-Date)
    $label1.Text = '{0:N0}' -f $span.TotalSeconds
    if($span.TotalSeconds -le 0)
       {
            $timer1.Stop()
            $timer1.enabled = $false
            function1
            $Form.Close()
            stop-process -Id $PID
        }
    }
    $timer1.add_tick($timer1_OnTick)
    $Form.Controls.AddRange(@($sync.Timer))
    ##############################################
    $sync.button1 = $button1
    $sync.button2 = $button2
    $sync.button3 = $button3
    $sync.button4 = $button4
    $sync.label1 = $label1
    $sync.TextBox = $outputBox
    $sync.Groupbox = $groupBox
    $sync.Timer = $timer1
    $Form.Controls.AddRange(@($sync.button1, $sync.button2, $sync.button3, $sync.button4, $sync.label1, $sync.TextBox, $sync.Groupbox ))

    $Form.Add_Shown({$Form.Activate()})
    [void] $Form.ShowDialog()

因为我是PowerShell的新手,所以我仍然无法理解Runspaces启动方法等。如何从自己的Runspace运行计时器,以及如何将功能添加到按钮中?我需要一些东西{click} -> {打开新的runspace} -> {run function},而计时器仍在隔离中滴答作用。netchk功能是一个简单的任务,我希望GUI做什么。我想理解这一点=)请向我解释。

好的,因此您在那里创建了同步的哈希表并设置了GUI(尽管您应该将哈希表标记为像 $GLOBAL:sync这样的全局变量,但实际上是您真正的一切需要做的就是继续创建runspace然后调用它,我将在下面显示。

首先,您需要将需要在脚本块内部单独的runspace中运行的任何代码

$SB = {YOUR CODE HERE}

接下来,您需要创建一个新的Runspace

$newRunspace =[runspacefactory]::CreateRunspace()

然后您需要设置一些选项,WPF应用程序需要公寓状态(不确定您使用的Winforms),并且建议使用线程选项以进行性能。

$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"

现在必须打开runspace

$newRunspace.Open()

现在,您可以将同步的哈希表添加到Runspace,此命令将通过代码中的变量$syncHash访问它。

$newRunspace.SessionStateProxy.SetVariable("SyncHash",$sync)

创建Runspace后,您还需要创建一个新的PowerShell对象并添加脚本。您还可以在此处添加参数,但我发现在同步哈希表中保存所需的任何数据并从那里使用它更容易。

$psCmd = [PowerShell]::Create().AddScript($SB)

然后,您需要将Runspace与PowerShell对象相关联

$psCmd.Runspace = $newRunspace

最后开始调用新创建的对象

$psCMD.BeginInvoke()

一起看起来像这个

$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"         
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable("SyncHash",$HashTable)
$psCmd = [PowerShell]::Create().AddScript($ScriptBlock)
$psCmd.Runspace = $newRunspace
$psCMD.BeginInvoke()

因此,要使Runspaces Interact我们需要使用一个全局同步的哈希表,因此将您现有的$sync设置为

$GLOBAL:sync = [hashtable]::Synchronized(@{})

一旦有了您需要将Netchk功能中当前具有的代码添加到脚本块中,看起来像So

$SB = {
    $wlanchk1 = netsh wlan show interfaces | Select-String 'sSSID'
    if ($wlanchk1 -ne $null){$wlanchk = $wlanchk1 -replace ('s','')
    $wlanchk -replace 'SSID:','Connected to:  '}else{$wlanchk = "No wlan connected"}
    $GLOBAL:sync.txtbox.Dispatcher.Invoke([action]{$GLOBAL:outputBox.Text = $wlanchk}, "Normal")
}

不必担心现在的调度员东西,我们将在一秒钟内完成。创建了脚本板后,您需要在单击按钮时启动Runspace,为此,我为您创建了一个封装上述代码的Start-Runspace函数。

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

您只需要将其作为参数传递,它将为您创建并运行异步Runspace。您将其称为下面的活动

$Button1.Add_click({Start-Runspace $SB})

现在可以回到那个奇怪的调度员东西。当您想更改附加到不同线程的事物时,您需要使用同步哈希表,以便您可以访问变量,但是您还需要使用调度程序对话框,由于您已经将控件添加到了哈希表中,因此您只需要调用其调度程序,然后让它知道执行什么操作,您在ScriptBlock中执行的操作如我上面所示。

$GLOBAL:sync.txtbox.Dispatcher.Invoke([action]{$GLOBAL:outputBox.Text = $wlanchk}, "Normal") 

从那里有一个重复您的按钮和操作的问题。希望这可以帮助您开始

编辑

只想补充一点,如果您有兴趣使用XAML更简单地使用XAML而不是使用图纸API定义GUI,那么您应该查看我在这里做的小博客文章,还有一些其他runspace的东西网站,但它更适合多任务处理,而不是GUI使用。

edit

 $Global:uiHash = [hashtable]::Synchronized(@{})
        $newRunspace =[runspacefactory]::CreateRunspace()
        $newRunspace.ApartmentState = "STA"
        $newRunspace.ThreadOptions = "ReuseThread"          
        $newRunspace.Open()
        $newRunspace.SessionStateProxy.SetVariable("uiHash",$Global:uiHash)
    $psCmd = [PowerShell]::Create().AddScript({
        $Global:uiHash.Error = $Error
        Add-Type -AssemblyName PresentationFramework,PresentationCore,WindowsBase    
        [xml]$xaml = @"
    <Window 
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            ResizeMode="NoResize"
            Title="Zapuskator" Height="350" Width="500">
        <Grid>
         <Button Name="button" Content="Button" HorizontalAlignment="Left" Margin="21,21,0,0" VerticalAlignment="Top" Width="200"/>
         <Button Name="button1" Content="Button1" HorizontalAlignment="Left" Margin="21,48,0,0" VerticalAlignment="Top" Width="200"/>
         <Button Name="button2" Content="Button2" HorizontalAlignment="Left" Margin="21,75,0,0" VerticalAlignment="Top" Width="200"/>
         <Button Name="button3" Content="Button3" HorizontalAlignment="Left" Margin="21,102,0,0" VerticalAlignment="Top" Width="200"/>
         <TextBox Name="textBox" HorizontalAlignment="Right" Height="151" Margin="0,159,60,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="422"/>
         <GroupBox Name="groupBox" Header="Choose your session: " Margin="0,0,260,0" VerticalAlignment="Top" Height="138" HorizontalAlignment="Right" Width="222"/>
         <Label Name="label" Content="60" HorizontalAlignment="Left" Margin="350,60,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.263,0.116" Height="60" Width="60"/>
        </Grid>
    </Window>
"@
    $reader=(New-Object System.Xml.XmlNodeReader $xaml)
    $Global:uiHash.Window=[Windows.Markup.XamlReader]::Load( $reader )
    $Global:uiHash.Button = $Global:uiHash.window.FindName("Button")
    $Global:uiHash.Button1 = $Global:uiHash.window.FindName("Button1")
    $Global:uiHash.Button2 = $Global:uiHash.window.FindName("Button2")
    $Global:uiHash.Button3 = $Global:uiHash.window.FindName("Button3")
    $Global:uiHash.TextBox = $Global:uiHash.window.FindName("textBox")
    $Global:uiHash.groupBox = $Global:uiHash.window.FindName("groupBox")
    $Global:uiHash.label = $Global:uiHash.window.FindName("label")
    $Global:uiHash.Button.Add_click({$Global:uiHash.Window.Dispatcher.Invoke([action]{$Global:uiHash.TextBox.AppendText("FFFFFFFF")}, "Normal")})
    $Global:uiHash.Window.ShowDialog() | Out-Null
    })
    $psCmd.Runspace = $newRunspace
    $handle = $psCmd.BeginInvoke()

好的。按预期工作的功能。我可以更改墙壁电话或做其他事情。但是我无法将文本传递给文本框。用$GLOBAL:uiHash变量重新设计UI,它可以正常工作,但是...像这样,我无法在单击时更改文本。但是当我尝试这样更改时:

    Start-Sleep -s 5
$Global:uiHash.Window.Dispatcher.Invoke([action]{$Global:uiHash.TextBox.AppendText("FFFFF")},"Normal")

一切都很好,出现文字..什么..?它出什么问题了?顺便说一下,迈克(Mike),当我使用您的功能时,文本不会改变。我想,我做错了什么。

最新更新