【问题标题】:WPF window not updating from PowerShellWPF 窗口未从 PowerShell 更新
【发布时间】:2020-09-17 08:04:06
【问题描述】:

我无法让 WPF 窗口继续从 PowerShell 更新,也不会冻结。

这个想法是,当我单击按钮时,将运行一些函数来更新进度条的值。但是无论我使用哪种方法或指南,我都无法让它发挥作用。进度条正在更新并在任务完成时填充到 100%。但在操作过程中,窗口冻结。我可以让窗口不冻结的唯一方法是在updateblock 中进行简单的增值。但我需要能够运行不同的功能并一次又一次地更新进度条。

我需要做什么才能从 Click-event 运行函数或脚本块,以使窗口不会冻结并且进度条会更新?

PowerShell 代码:

Add-Type -Assembly PresentationFramework

$syncHash = [hashtable]::Synchronized( @{} )
$syncHash.Counter = 0.0
$syncHash.Do = {
    param ( $syncHash, $ToDo )
    $rsR = [runspacefactory]::CreateRunspace()
    $rsR.ApartmentState = "STA"
    $rsR.ThreadOptions = "ReuseThread"
    $rsR.Open()
    $rsR.SessionStateProxy.SetVariable( "syncHash", $syncHash )
    $c = [powershell]::Create().AddScript( $ToDo )
    $c.Runspace = $rsR
    $h = $c.BeginInvoke()
    do { start-sleep -millisecond 10 } until ( $h.IsCompleted -eq $true )
    $c.EndInvoke( $h )
    $rsR.Close()
    $rsR.Dispose()
}

$syncHash.Window    = [Windows.Markup.XamlReader]::Load( ( New-Object System.Xml.XmlNodeReader ( [xml]( Get-Content .\tt.xml ) ) ) )
$syncHash.Button    = $syncHash.Window.FindName( "Button" )
$syncHash.Progress  = $syncHash.Window.FindName( "Progress" )
$Timer = New-Object System.Windows.Threading.DispatcherTimer

$Task = { 1..100 | % { start-sleep -millisecond 10; $syncHash.Counter = $_ } }
$syncHash.Button.Add_Click( { Something } )
$syncHash.Window.Add_SourceInitialized( {
    $Timer.Start()
    $Timer.Add_Tick( $UpdateBlock )
    $Timer.Interval = [TimeSpan]"0:0:0.01"
} )

function Something
{
    & $syncHash.Do $syncHash $Task
}

$UpdateBlock =`
{
    $syncHash.Window.Resources["Progress"] = [double]$syncHash.Counter
    $syncHash.Window.Resources["PbText"] = "$( $syncHash.Counter ) %"
}
[void]$syncHash.Window.ShowDialog()

XAML 代码:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:system="clr-namespace:System;assembly=mscorlib"
        Topmost="True"
        WindowStartupLocation="CenterScreen"
        SizeToContent="WidthAndHeight">
   <Window.Resources>
      <system:Double x:Key="Progress">0.0</system:Double>
      <system:String x:Key="PbText"></system:String>
   </Window.Resources>

    <Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition/>
        <RowDefinition/>
    </Grid.RowDefinitions>
        <Button Grid.Row="0" Content="Click" Name="Button"/>
        <Grid Grid.Row="1">
            <ProgressBar Name="Progress" Minimum="0" Value="{DynamicResource Progress}"  Width="230"/>
            <TextBlock Text="{DynamicResource PbText}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        </Grid>
    </Grid>
</Window>

【问题讨论】:

  • 你已经有一个额外的线程(运行空间),但你只是没有充分利用它。您打开另一个线程,然后将您的主/GUI线程发送到睡眠状态,直到任务完成......而是将进度存储在您的$syncHash中具有INotifyPropertyChanged 的对象中,以便您可以触发事件以更新进度条发生变化而不是等待/休眠。
  • 我试图得到这个,但我要么不明白你要解释什么,要么不明白如何实现它:(

标签: wpf powershell progress-bar


【解决方案1】:

我终于找到了该怎么做。不得不重新考虑一些东西,并一起删除了运行空间功能。

多亏了绑定,一切都变得更简单了。

这是我来的,还为进度条的最大值添加了绑定:

Add-Type -Assembly PresentationFramework

$syncHash = @{}
$syncHash.Window    = [Windows.Markup.XamlReader]::Load( ( New-Object System.Xml.XmlNodeReader ( [xml]( Get-Content .\tt.xml ) ) ) )
$syncHash.Button    = $syncHash.Window.FindName( "Button" )
$syncHash.Progress  = $syncHash.Window.FindName( "Progress" )

$Something = { param( $d )
    function SomethingElse { gci }

    $d[1] = ( SomethingElse ).Count
    [system.windows.messagebox]::show($d[1])
    foreach ( $i in ( 1..$d[1] ) ) { Start-Sleep -MilliSeconds 300; $d[0] = [double]( ( $i/$d[1] ) * 100 ) }
}

$syncHash.Button.Add_Click( { ( [powershell]::Create().AddScript( $Something ).AddArgument( $DataContext ) ).BeginInvoke() } )

$DataContext = New-Object System.Collections.ObjectModel.ObservableCollection[Object]
$DataContext.Add( 0.0 )
$DataContext.Add( 0.0 )
$syncHash.Progress.DataContext = $DataContext

$Binding = New-Object System.Windows.Data.Binding -ArgumentList "[0]"
$Binding.Mode = [System.Windows.Data.BindingMode]::OneWay
[void][System.Windows.Data.BindingOperations]::SetBinding( $syncHash.Progress, [System.Windows.Controls.ProgressBar]::ValueProperty, $Binding )

$Binding = New-Object System.Windows.Data.Binding -ArgumentList "[1]"
$Binding.Mode = [System.Windows.Data.BindingMode]::OneWay
[void][System.Windows.Data.BindingOperations]::SetBinding( $syncHash.Progress, [System.Windows.Controls.ProgressBar]::MaximumProperty, $Binding )

[void]$syncHash.Window.ShowDialog()

【讨论】:

    猜你喜欢
    • 2012-12-09
    • 2012-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-23
    • 2019-12-08
    • 2012-12-21
    • 1970-01-01
    相关资源
    最近更新 更多