开发者

Parallel Programming: Can't access UI Parallel?

开发者 https://www.devze.com 2023-01-10 22:36 出处:网络
I\'m trying to create parallel execution of a function in wpf c# which also runs actions on the UI. But when running there is always an exception at methods on UI Controls: The calling thread cannot a

I'm trying to create parallel execution of a function in wpf c# which also runs actions on the UI. But when running there is always an exception at methods on UI Controls: The calling thread cannot access this object because a different thread owns it. The exception is always called on the second instance of the loop being run, so it isn't possible to manipulate the UI in two parallel running instances?

Is it possible to acces the UI in parallel?

Code:

do
{  
    if (listBox_Copy.SelectedIndex < listBox_Copy.Items.Count - 1)
    {
        listBox_Copy.SelectedIndex = listBox_Copy.SelectedIndex + 1;
        listBox_Copy.ScrollIntoView(listBox_Copy.SelectedItem);
    }
    listBox_Copy.Focus();
    huidigitem = listBox_Copy.SelectedItem as ListBoxItem;
    currentitemindex = listBox_Copy.SelectedIndex;
    currentitem = listBox_Copy.ItemContainerGenerator.ContainerFromIndex(currentitemindex) as ListBoxItem;
    itemgrid = FindVisualChild<Grid>(currentitem);
    senderbutton = (Button)sender;
    Button playcues = itemgrid.FindName("Playbutton") as Button;
    cuetrigger = itemgrid.FindName("cuetrigger") as TextBlock;
    Jobs.Add(playcues);
} while (cuetrigger.Text != "go");

Parallel.ForEach(Jobs, playcues => { play(playcues, new RoutedEventArgs()); });

And then it crashes at the play function

private void play(object sender, System.Windows.RoutedEventA开发者_运维知识库rgs e)
{
    Grid itemgrid = VisualTreeHelperExtensions.FindAncestor<Grid>(playcue);
    ...
}


It is not possible to access the UI from a background thread, all your updates must be on the main thread. You can do this by using the Dispatcher

Something like this

        Action x = (Action)delegate {
           //do my UI updating
        };
        Dispatcher.Invoke(x, new object[] { });


The trick is to use an IProgress<T> to report updates to the main thread. The IProgress<T> constructor accepts an anonymous function that will be run in the main thread and can thus update the UI.

Quoting from https://blog.stephencleary.com/2012/02/reporting-progress-from-async-tasks.html :

public async void StartProcessingButton_Click(object sender, EventArgs e)
{
  // The Progress<T> constructor captures our UI context,
  //  so the lambda will be run on the UI thread.
  var progress = new Progress<int>(percent =>
  {
    textBox1.Text = percent + "%";
  });

  // DoProcessing is run on the thread pool.
  await Task.Run(() => DoProcessing(progress));
  textBox1.Text = "Done!";
}

public void DoProcessing(IProgress<int> progress)
{
  for (int i = 0; i != 100; ++i)
  {
    Thread.Sleep(100); // CPU-bound work
    if (progress != null)
      progress.Report(i);
  }
}

Now, a little bit of self-promotion :) I created an IEnumerable<T> extension to run a parallel for with event callbacks that can directly modify the UI. You can have a look at it here:

https://github.com/jotaelesalinas/csharp-forallp

Hope it helps!

0

精彩评论

暂无评论...
验证码 换一张
取 消