2011-04-13 130 views
4

我試圖在PowerShell中添加進度條到窗體。我不想使用PowerShell的Write-Progress cmdlet(因爲當我從命令行運行腳本時,它顯示基於文本的進度條,並且我總是希望基於表單/圖形的欄)。Windows窗體中的Powershell進度條

我已經試過這一點,似乎工作(在網上找到):

[reflection.assembly]::loadwithpartialname("System.Windows.Forms") | Out-Null 
[reflection.assembly]::loadwithpartialname("System.Drawing") | Out-Null 

$form_main = New-Object System.Windows.Forms.Form 
$progressBar1 = New-Object System.Windows.Forms.ProgressBar 
$timer1 = New-Object System.Windows.Forms.Timer 

$timer1_OnTick = { 
    $progressBar1.PerformStep() 
} 

$form_main.Text = 'ProgressBar demo' 

$progressBar1.DataBindings.DefaultDataSourceUpdateMode = 0 
$progressBar1.Step = 1 
$progressBar1.Name = 'progressBar1' 

$form_main.Controls.Add($progressBar1) 

$timer1.Interval = 100 
$timer1.add_tick($timer1_OnTick) 
$timer1.Start() 

$form_main.ShowDialog()| Out-Null 

不過,我不想一個事件,更新進度條(如示例中做$ timer1_OnTic以上)我想在我的腳本撥打電話如來更新它自己:

$progressBar1.PerformStep() 

或者

$progressBar1.Value = 10 

因此,似乎我需要某種後臺工作人員,每當我調用PerformStep()或更改progressBar值時,都會更新進度欄。調用ShowDialog將停止腳本內的所有處理,直到窗體關閉爲止。

回答

5

如果我理解正確,您應該可以將ShowDialog()更改爲Show(),它將顯示對話框而不會阻止腳本。然後,您可以繼續執行並更新進度欄。

您可能會對形式的交互性缺乏感到失望。

+1

謝謝你,Show()方法做到了。非常感激。 – user577240 2011-04-14 00:24:53

3

我已經取得了一些成功的方法是使用GUI的子運行空間(在本例中爲WPF),因此它不鎖定腳本。可以通過會話狀態代理在父級和子級運行空間中訪問數據。

例如

# define the shared variable 
$sharedData = [HashTable]::Synchronized(@{}); 
$sharedData.Progress = 0; 
$sharedData.state = 0; 
$sharedData.EnableTimer = $true; 

# Set up the runspace (STA is required for WPF) 
$rs = [RunSpaceFactory]::CreateRunSpace(); 
$rs.ApartmentState = "STA"; 
$rs.ThreadOptions = "ReuseThread"; 
$rs.Open(); 

# configure the shared variable as accessible from both sides (parent and child runspace) 
$rs.SessionStateProxy.setVariable("sharedData", $sharedData); 

# define the code to run in the child runspace 
$script = { 
    add-Type -assembly PresentationFramework; 
add-Type -assembly PresentationCore; 
add-Type -assembly WindowsBase; 

[xml]$xaml = @" 
<Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    MaxHeight="100" MinHeight="100" Height="100" 
    MaxWidth="320" MinWidth="320" Width="320" 
    WindowStyle="ToolWindow"> 

<Canvas Grid.Row="1"> 
    <TextBlock Name="ProgressText" Canvas.Top="10" Canvas.Left="20">Hello world</TextBlock> 
    <ProgressBar Name="ProgressComplete" Canvas.Top="30" Canvas.Left="20" Width="260" Height="20" HorizontalAlignment="Center" Value="20" /> 
</Canvas> 

</Window> 
"@ 

    # process the xaml above 
    $reader = New-Object System.Xml.XmlNodeReader $xaml; 
    $dialog = [Windows.Markup.XamlReader]::Load($reader); 

    # get an handle for the progress bar 
    $progBar = $dialog.FindName("ProgressComplete"); 
    $progBar.Value = 0; 

    # define the code to run at each interval (update the bar) 
    # DON'T forget to include a way to stop the script 
    $scriptBlock = { 
     if ($sharedData.EnableTimer = $false) { 
      $timer.IsEnabled = $false; 
      $dialog.Close(); 
     } 

     $progBar.value = $sharedData.Progress; 
    } 

    # at the timer to run the script on each 'tick' 
    $dialog.Add_SourceInitialized({ 
     $timer = new-Object System.Windows.Threading.DispatherTimer; 
     $timer.Interface = [TimeSpan]"0:0:0.50"; 
     $timer.Add_Tick($scriptBlock); 
     $timer.Start(); 
     if (!$timer.IsEnabled) { 
      $dialog.Close(); 
     } 
    }); 

    # Start the timer and show the dialog 
    &$scriptBlock; 
    $dialog.ShowDialog() | out-null; 
} 

$ps = [PowerShell]::Create(); 
$ps.Runspace = $rs; 
$ps.AddScript($script).BeginInvoke(); 

# if you want data from your GUI, you can access it through the $sharedData variable 
Write-Output $sharedData; 

如果你試試這個代碼,一旦顯示在對話框中你可以通過設置$sharedData.Progress

此值更改進度條讓我寫很多對話框的工具,我約束通過我們的基礎架構在運行空間內使用powershell,WPF似乎比表單更好。