2016-01-05 4 views
0

Я пытаюсь реализовать фоновые команды, которые будут запускаться после нажатия пользователем кнопки, и до сих пор я всегда закрывал свой пользовательский интерфейс, когда работа продолжается. В настоящее время я использую несколько времени для цикла, чтобы проверить, заблокирован ли мой пользовательский интерфейс. Что я могу сделать, чтобы работа работала в фоновом режиме при освобождении пользовательского интерфейса. Я не уверен, что хочу усложнить код, добавив пробелы. Что я делаю неправильно?Неужели работа действительно работает в фоновом режиме в powershell?

$inputXML = @" 
<Window x:Class="WpfApplication2.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:local="clr-namespace:WpfApplication2" 
     mc:Ignorable="d" 
     Title="Create User" Height="448.05" Width="656.017" ResizeMode="NoResize"> 
    <Grid Margin="0,0,-6.8,-0.8" Background="#FFD7D7D7"> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="403*"/> 
      <RowDefinition Height="18*"/> 
     </Grid.RowDefinitions> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition/> 
     </Grid.ColumnDefinitions> 
     <Button x:Name="Create_User" Content="Create User" HorizontalAlignment="Left" Margin="67,224,0,0" VerticalAlignment="Top" Width="300" Height="26" RenderTransformOrigin="0.551,-0.671" IsEnabled="True"> 
      <Button.Background> 
       <LinearGradientBrush EndPoint="0,1" StartPoint="0,0"> 
        <GradientStop Color="#FFF3F3F3" Offset="0"/> 
        <GradientStop Color="#FFEBEBEB" Offset="0.5"/> 
        <GradientStop Color="#FFDDDDDD" Offset="0.5"/> 
        <GradientStop Color="#FFCDCDCD" Offset="1"/> 
       </LinearGradientBrush> 
      </Button.Background> 
     </Button> 
     <Label x:Name="fname" Content="" HorizontalAlignment="Left" Margin="43,11,0,0" VerticalAlignment="Top" Height="26" Width="10"/> 
     <Label x:Name="fname1" Content="First Name" HorizontalAlignment="Left" Margin="88,54,0,0" VerticalAlignment="Top" Width="72" RenderTransformOrigin="0.513,1.469" Height="26"/> 
     <Label x:Name="lname" Content="Last Name" HorizontalAlignment="Left" Margin="88,83,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.167,-0.458" Width="72" Height="25"/> 
     <TextBox x:Name="fnametxt" Height="23" Margin="167,54,0,0" TextWrapping="Wrap" VerticalAlignment="Top" RenderTransformOrigin="0.501,0.452" HorizontalAlignment="Left" Width="136"/> 
     <Button x:Name="exitbtn" Content="Exit" HorizontalAlignment="Left" VerticalAlignment="Top" Width="135" Margin="447,365,0,0" Height="38" RenderTransformOrigin="0.489,0.462"/> 
     <Label x:Name="label" Content="Password" HorizontalAlignment="Left" Margin="92,113,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.064,0.601" Height="26" Width="68"/> 
     <PasswordBox x:Name="passwordBox" Margin="167,113,0,0" VerticalAlignment="Top" Height="24" HorizontalAlignment="Left" Width="136"/> 
     <TextBox x:Name="stsbox" HorizontalAlignment="Left" Height="62" Margin="67,267,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="300" Background="#FFDADADA" Foreground="Black" Opacity="0.45" SelectionBrush="#FF1D6EBF" RenderTransformOrigin="0.503,-0.59" IsReadOnly="True"/> 
     <TextBox x:Name="lnametxt" Height="23" Margin="167,85,0,0" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Left" Width="136"/> 
    </Grid> 
</Window> 
"@ 

$inputXML = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window' 
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework') 
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") 
[xml]$xaml = $inputXML 

#Read XAML 
$reader=(New-Object System.Xml.XmlNodeReader $xaml) 
try{$Form=[Windows.Markup.XamlReader]::Load($reader)} 
catch{Write-Host "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .net is installed."} 
$xaml.SelectNodes("//*[@Name]") | %{Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name) -Scope Global} 


Function global:Get-FormVariables{ 
if ($global:ReadmeDisplay -ne $true) {$global:ReadmeDisplay=$true} 
#write-host "Found the following interactable elements from our form" -ForegroundColor Cyan 
get-variable WPF* 
} 


Get-FormVariables 


$WPFCreate_User.Add_Click({ 

$test = { for($i=1; $i -le 100000; $i++){ 
$z = $i + $z 
$z 
}} 

$job = Start-Job $test 
Wait-Job $job 

Receive-Job $job -OutVariable results 
Remove-Job $job 
$WPFstsbox.text = "$results`n" 
          }) 

$WPFexitbtn.add_click({ 
$form.Close() | out-null 
exit}) 

$form.ShowDialog() | Out-null 
+0

Что сделка с «выполнения фрагмента кода»? –

+0

@JaquelineVanek они выбрали неправильную кнопку в редакторе, которая добавляет это для них. – briantist

ответ

1

Да, PSJobs действительно "фон" работа.

Когда вы звоните Start-Job, начинается процесс, выполняющий вашу работу/команду.

В вашем скрипте само задание не блокирует вызывающий поток, но ваша последующая команда (Wait-Job $job).


Если вы просто выстрелить Start-Job и возврата из обработчика Click, ваш пользовательский интерфейс не запирать:

$button.add_Click({ 
    $jobCode = { Start-Sleep -Seconds 10 } 
    $job = Start-Job $jobCode 
}) 

вы увидите, что пользовательский интерфейс будет реагировать снова в пути дрожжах, чем 10 секунд.

Проблема в том, что у вас больше нет доступа к $job и больше не нужно контролировать, когда оно должно отображаться.

Чтобы компенсировать это, вам нужен фоновый поток или событие времени, которое может периодически проверять задание (-ы) для вас и выводить результат.

Вы можете использовать Timer и встроенную ТРОЕБОРЬЕ инфраструктуры PowerShell для этого:

# Create timer 
$backgroundTimer = New-Object System.Timers.Timer 
# Set interval (ms) 
$backgroundTimer.Interval = 500 
# Make sure timer stops raising events after each interval 
$backgroundTimer.AutoReset = $false 

# Have powershell "listen" for the event in the background 
Register-ObjectEvent $backgroundTimer -EventName 'Elapsed' -SourceIdentifier 'timerElapsed' -Action { 

    # Loop through any completed jobs with data to return, from "oldest" to "newest" 
    while(($finishedJob = Get-Job |Where-Object {$_.State -eq 'Completed' -and $_.HasMoreData}|Select-Object -First 1)) 
    { 
    # Update the text box on your WPF form with the results 
    # NOTE: the event is executed in a separate scope, thus the "global:" scope prefix 
    $global:WPFstsbox.Text = "$(Receive-Job $finishedJob)`n" 

    # Clean up the job 
    Remove-Job $finishedJob 
    } 

    # Restart timer 
    $backgroundTimer.Start() 
} 

# Enable the timer 
$backgroundTimer.Enabled = $true 
+0

Спасибо за это, я буквально ударил головой, пытаясь понять это. В случае использования фоновых потоков я попытался использовать ваш метод и, похоже, плюнул 'Can not Подписаться на указанное событие. Абонент с идентификатором источника «timerElapsed» уже существует. Ошибка. – Bregs

+0

Простите за второй комментарий, не удалось изменить мой предыдущий. Кажется, что после запуска кода SourceIdentifier по-прежнему сохраняется в фоновом режиме, и поэтому, когда я перехожу к повторному запуску кода, powershell выплевывает ошибку, говоря, что исходный идентификатор уже существует. Я изучил использование Unregister-Event после завершения работы, однако мне не повезло с командлетом. Что касается прямо сейчас, мне приходится назначать новый атрибут SourceIdentifier каждый раз, когда я перехожу к повторному запуску кода и его работе, как хотелось бы! Я попытаюсь выяснить, как избавиться от ошибки, о которой я упомянул earlie – Bregs

+0

@Bregs правильно, вам нужно вручную очистить регистрацию событий при закрытии приложения WPF. Извините, я должен был упомянуть об этом. Используйте 'Unregister-Event' для этого –

Смежные вопросы