1

I have tried the code mentioned in this answer: https://stackoverflow.com/a/27089652/

It works fine and I want to use it for running a PowerShell script in for loop. GUI was freezing initially then I tried the code mentioned in this answer: https://stackoverflow.com/a/35735760/

Now GUI does not freeze while the PowerShell script is running in the background although nothing is updated in the textbox until for loop is complete. I want to see the results updating in real time. Here is the code I am running:

        private async void run_click(object sender, RoutedEventArgs e)
        {
            Text1.Text = "";
            
            await Task.Run(() => PS_Execution(Text1));

        }

        internal async Task PS_Execution(TextBox text)
        {

            PowerShell ps = PowerShell.Create();
            ps.AddScript(script.ToString());

            {

                Collection<PSObject> results = ps.Invoke();
                foreach (PSObject r in results)
                {
                    text.Dispatcher.Invoke(() =>
                    {
                        text.Text += r.ToString();
                    });
                    await Task.Delay(100);
                }                
            }
        }

Maybe I am missing something important. Please help me understand how to solve this problem.

hellouniverse
  • 113
  • 1
  • 12
  • 1
    Does this answer your question? [Write PowerShell Output (as it happens) to WPF UI Control](https://stackoverflow.com/a/36716964/1701026) or simply use `Start-Job` as in [here](https://stackoverflow.com/a/37376311/1701026) – iRon Aug 01 '21 at 07:39
  • 1
    The most important thing you are missing is your ignorance of the main way that UI elements receive data: Bindings to Data Context Properties. If text.Text had been assigned a binding, then no Dispatcher call would be required. It would be sufficient to change the value of the Bind source property. – EldHasp Aug 01 '21 at 08:10

1 Answers1

7

Instead of using ps.Invoke() which is synchronous call and will wait for all results to return use ps.BeginInvoke() instead. Then subscribe to the DataAdded event of the output PSDataCollection and use the action to update your ui.

private async void run_click(object sender, RoutedEventArgs e)
{
    Text1.Text = "";
    await Task.Run(() => PS_Execution(Text1));
}


internal async Task PS_Execution(TextBox text)
{
    using PowerShell ps = PowerShell.Create();
    ps.AddScript(script.ToString());

    PSDataCollection<string> input = null;
    PSDataCollection<string> output = new();
    IAsyncResult asyncResult = ps.BeginInvoke(input, output);

    output.DataAdded += (sender, args) =>
    {
        var data = sender as PSDataCollection<string>;
        text.Dispatcher.Invoke(() =>
        {
            text.Text += data[args.Index];
        });

    };
}

Daniel
  • 4,792
  • 2
  • 7
  • 20