调用UI更新时获取异常

时间:2015-09-09 09:35:07

标签: c# multithreading

我是C#,WPF&穿线也是。我正在使用MVC5开发Web应用程序。

我知道我应该在更新主线程以外的线程中的UI元素时调度调度程序。但我并不完全了解我需要做的改变。

我有一个GetBitmapForDistributionChart方法,可以像这样进行UI更新。

public byte[] GetBitmapForDistributionChart(int width, int height, DistributionChartParameters disrtibutionParams)
    {
        // delegate control instantiation to STA Thread
        return DelegateBitmapGenerationToSTAThread(() =>
        {
            Chart canvasChart = new Chart(languageService);
            canvasChart.Width = width;
            canvasChart.Height = height;

            canvasChart.Measure(new Size(width, height));
            canvasChart.Arrange(new Rect(0, 0, width, height));

            return GenerateBitmapFromControl(canvasChart, width, height);
        });
    }

其中DelegateBitmapGenerationToSTAThread的定义如下所示:

private byte[] DelegateBitmapGenerationToSTAThread(Func<byte[]> controlBitmapGenerator)
    {
        byte[] imageBinaryData = null;

        Thread thread = new Thread(() =>
        {
            var renderer = new BitmapCreator(this.languageService);
            imageBinaryData = controlBitmapGenerator();
        });

        //Set the thread to STA
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();

        //Wait for the thread to end
        thread.Join();

        return imageBinaryData;
    }

我得到一个异常“不能使用属于与其父Freezable不同的线程的DependencyObject。”在canvasChart.Arrange,我在Chart类中添加以下行:

rect.Fill = distributionParams.rangeData.ElementAt(i).barColor;

在主线程中。

如果我将同一行更改为不依赖于右侧类的内容,则可以正常工作。

Like, rect.Fill = new SolidColorBrush(Colors.Red);

我不知道如何解决这个问题。

注意: 我也得到异常“调用线程无法访问此对象,因为另一个线程拥有它。”在尝试这样做的时候:

rect.Fill = new SolidColorBrush(distributionParams.rangeData.ElementAt(i).barColor.Color);

distributionParams结构如下所示:

public struct DistributionParams
{
    public struct RangeData
    {
        public string range;
        public double distributionValue;
        public SolidColorBrush barColor;
    }
    public List<RangeData> rangeData;
}

请帮我解决这个问题。

1 个答案:

答案 0 :(得分:5)

因此,在DelegateBitmapGenerationToSTAThread中启动新的STA线程。然后你试图访问DistributionParams.RangeData.barColor,它是SolidColorBrush类型。您在另一个(主UI)线程中创建这些画笔,这就是您获得此异常的原因。你可以做些什么来解决这个问题:

  1. 尝试在创建后冻结画笔。

    d.barColor = new SolidColorBrush(...);
    d.barColor.Freeze();
    
  2. 使用预定义的画笔,它们已被冻结:

    d.barColor = Brushes.Blue;
    
  3. 使用颜色代替SolidColorBrush

    d.barColor = Colors.Blue;
    
  4. 然后,在必要时创建SolidColorBrush

     rect.Fill = new SolidColorBrush(d.barColor);
    

    更新以回答评论中的问题。 SolidColorBrush可能看起来很无辜,但这仍然是与UI相关的对象,它定义了如何呈现界面。 WPF(和WinForms)中的此类对象具有线程关联性 - 它们只能由一个线程(创建它们的线程)访问。为何如此限制?因为并行和有效地实现并发更改对影响渲染的此类元素的属性并不容易。在SolidColorBrush的情况下,想象10个线程改变它的颜色,UI线程尝试渲染所有这些。因此,因为允许更改 - 读取也不安全。

    现在,如果您的类继承自Freezable,WPF会以特殊方式处理它。如果它是Freezable并且被冻结,类作者保证不再更改对象(将在任何更改或其他任何内容上抛出异常)。然后,即使此对象与UI相关,也可以安全地从任何线程访问此类对象。

    返回SolidColorBrush。当您创建它时(使用任何颜色,甚至是预定义的颜色),默认情况下它不会被冻结,您可以随时更改其Color属性。如果你使用预定义的画笔(例如Brushes.Red) - 它已经为你冻结了,你不能做Brushes.Red.Color = Colors.Blue。

相关问题