[英]Multiple functions in WPF GUI from runspace(s)
我有一个带计时器的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}-> {open new runspace}-> {run function},而计时器仍在单独计时。 netchk
函数是一个简单的任务,我想让GUI执行。 我想了解这个=)请向我解释。
好的,您已经完成了同步哈希表并设置gui的工作(尽管您应该将哈希表标记为像$GLOBAL:sync
这样的全局变量,但您真正要做的就是继续并创建运行空间,然后调用它,我将在下面显示。
首先,您需要将需要运行的所有代码包装在脚本块内部的单独运行空间中
$SB = {YOUR CODE HERE}
接下来,您需要创建一个新的运行空间
$newRunspace =[runspacefactory]::CreateRunspace()
然后,您将要设置一些选项,WPF应用程序需要单元状态(不确定所使用的WinForms),建议使用线程选项以提高性能。
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"
现在必须打开运行空间
$newRunspace.Open()
现在,您可以将同步哈希表添加到运行空间,此命令将通过代码内的变量$syncHash
对其进行访问。
$newRunspace.SessionStateProxy.SetVariable("SyncHash",$sync)
创建运行空间后,您还需要创建一个新的powershell对象并添加脚本。 如果需要,您还可以在此处添加参数,但是我发现将所需的任何数据保存在同步的哈希表中并从那里开始使用更容易。
$psCmd = [PowerShell]::Create().AddScript($SB)
然后,您需要将运行空间与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()
因此,为了使运行空间进行交互,我们需要使用全局同步的哈希表,因此请像这样设置现有的$sync
$GLOBAL:sync = [hashtable]::Synchronized(@{})
一旦有了,就需要将netchk函数中当前拥有的代码添加到脚本块中,如下所示
$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")
}
现在不用担心调度程序的问题,我们将在稍后介绍。 创建脚本块后,单击按钮时需要启动运行空间,为此,我为您创建了一个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()
}
您只需要将脚本块作为参数传递给它,它将为您创建并运行异步运行空间。 您将其称为如下事件
$Button1.Add_click({Start-Runspace $SB})
好了,现在回到奇怪的调度程序。 当您想要更改连接到不同线程的对象时,需要使用同步的哈希表,以便可以访问变量,但是您还需要使用分派器,因为您正在使用的线程不拥有对话框中,由于您已经将控件添加到哈希表中,因此只需要调用它的调度程序,并让它知道要执行的操作即可,就像上面显示的那样,您可以在脚本块中执行此操作。
$GLOBAL:sync.txtbox.Dispatcher.Invoke([action]{$GLOBAL:outputBox.Text = $wlanchk}, "Normal")
从那里开始,只需对每个按钮和操作重复一次即可。 希望这可以帮助您前进
编辑
只是想补充一下,如果您有兴趣使用XAML而不是使用绘图api定义GUI,那么您应该查看我在此处做过的一小篇博客文章,该站点上还有一些其他运行空间的内容,但是比使用GUI更适合于多任务处理。
编辑
$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")
一切都很好,出现了文本。 它出什么问题了? 顺便说一句,迈克,当我使用您的函数时,文本不会改变。 我认为我做错了什么。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.