使用Parallel.Foreach或Threads更新WPF UIElements

时间:2015-10-23 16:07:35

标签: c# wpf multithreading canvas parallel.foreach

使用Parallel.Foreach或IEnumerable.AsParallel()。ForAll()或Threads更新Canvas的内容时遇到一些麻烦。

我在画布中有很多行,当用户调整窗口大小时,我需要将它们的坐标相关更改为Canvas的新大小。

我把我的行放在

    IEnumerable<Line> lineCollection = canvas.Children.OfType<Line>();

然后我尝试使用Parallel.Foreach或IEnumerable.AsParallel()来并行循环它们.ForAll()

在这种情况下我收到了AggregateException。它说,调用线程无法访问此对象,因为主线程拥有它。

我如何用我的UIElements做到这一点。

这是我的代码:

    private void canvas_SizeChanged(object sender, SizeChangedEventArgs e)
    {

        Double hDelta = e.NewSize.Height / e.PreviousSize.Height;

        if (Double.IsInfinity(hDelta)) return;

        IEnumerable<Line> LineCollection = canvas.Children.OfType<Line>();
        try
        {
            Parallel.ForEach(LineCollection, (line) =>
            {
                Double topProp = (Double)line.GetValue(Canvas.TopProperty) * hDelta;

                line.SetValue(Canvas.TopProperty, topProp);
            });
        }
        catch (AggregateException ae)
        {
            ae.Handle((x) =>
            {
                if (x is Exception)
                {
                    MessageBox.Show(x.ToString(), "error");
                }
                return false;
            });
        }
    }

我在这一行收到错误:

line.SetValue(Canvas.TopProperty, topProp);

1 个答案:

答案 0 :(得分:0)

这解决了我的问题。

        Line[] keysLineCollection = YAxisKeys.Children.OfType<Line>().ToArray();
        Label[] keysLabelCollection = YAxisKeys.Children.OfType<Label>().ToArray();
        try
        {
            new Thread(delegate ()
            {
                Parallel.ForEach(keysLineCollection, (line, loopstate, elementIndex) =>
                {

                    /*keysLineCollection[elementIndex]*/
                    line.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        Double topProp = ((Double)line.GetValue(Canvas.TopProperty) - 11.0) * hDelta + 11.0;
                        switch (topProp - 11.0 < 0)
                        {
                            case true:
                                line.Visibility = Visibility.Hidden;
                                break;
                            default:
                                line.Visibility = Visibility.Visible;
                                break;
                        }
                        line.SetValue(Canvas.TopProperty, topProp);
                    }));
                    keysLineCollection[elementIndex] = line;
                });
            }).Start();

            new Thread(delegate ()
            {
                Parallel.ForEach(keysLabelCollection, (label, loopstate, elementIndex) =>
                {

                    /*keysLabelCollection[elementIndex]*/
                    label.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        Double topProp = (Double)label.GetValue(Canvas.TopProperty) * hDelta;
                        switch (topProp < 0)
                        {
                            case true:
                                label.Visibility = Visibility.Hidden;
                                break;
                            default:
                                label.Visibility = Visibility.Visible;
                                break;
                        }
                        label.SetValue(Canvas.TopProperty, topProp);
                    }));
                    keysLabelCollection[elementIndex] = label;
                });
            }).Start();
        }
        catch (AggregateException ae)
        {
            ae.Handle((x) =>
            {
                if (x is Exception)
                {
                    MessageBox.Show(x.ToString(), "error");
                }
                return false;
            });
        }
        catch(Exception ex)
        {
            MessageBox.Show(ex.ToString(), "error");
        }

我用过

Line[] keysLineCollection = YAxisKeys.Children.OfType<Line>().ToArray();

而不是

IEnumerable<Line> lineCollection = canvas.Children.OfType<Line>();

并将Dispatcher应用于数组中的元素。

它对我很有用。