Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/321.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 调用UI更新时获取异常_C#_Multithreading - Fatal编程技术网

C# 调用UI更新时获取异常

C# 调用UI更新时获取异常,c#,multithreading,C#,Multithreading,我对C#、WPF和线程也不熟悉。我正在使用MVC5开发一个web应用程序 我知道,当更新除主线程之外的线程中的UI元素时,我应该调用dispatcher。但我并不完全了解我需要做的改变 我有一个方法GetBitmapForDistributionChart,它可以像这样进行UI更新 public byte[] GetBitmapForDistributionChart(int width, int height, DistributionChartParameters disrtibutionP

我对C#、WPF和线程也不熟悉。我正在使用MVC5开发一个web应用程序

我知道,当更新除主线程之外的线程中的UI元素时,我应该调用dispatcher。但我并不完全了解我需要做的改变

我有一个方法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;
    }
public struct DistributionParams
{
    public struct RangeData
    {
        public string range;
        public double distributionValue;
        public SolidColorBrush barColor;
    }
    public List<RangeData> rangeData;
}
在主线程中

如果我将同一行更改为不依赖于右侧类的内容,那么它就工作了

Like, rect.Fill = new SolidColorBrush(Colors.Red);
我不知道如何解决这个问题

注: 此外,我还收到异常“调用线程无法访问此对象,因为另一个线程拥有它。”尝试执行此操作时:

rect.Fill = new SolidColorBrush(distributionParams.rangeData.ElementAt(i).barColor.Color);
distributionParams结构如下所示:

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;
    }
public struct DistributionParams
{
    public struct RangeData
    {
        public string range;
        public double distributionValue;
        public SolidColorBrush barColor;
    }
    public List<RangeData> rangeData;
}
公共结构分发参数
{
公共结构范围数据
{
公共字符串范围;
公共双重分配价值;
公共色;
}
公共数据清单;
}

请帮助我解决此问题。

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

  • 尝试在创建后冻结笔刷

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

    d.barColor = Brushes.Blue;
    
  • 使用颜色而不是SolidColorBrush

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

     rect.Fill = new SolidColorBrush(d.barColor);
    
    更新以回答您评论中的问题。 SolidColorBrush看起来可能是无辜的,但它仍然是定义如何呈现接口的UI相关对象。WPF(和WinForms)中的此类对象具有线程关联性——它们可能只能由一个线程(创建它们的线程)访问。为什么会有这样的限制?因为要正确有效地实现对这些影响渲染的元素的属性的并发更改并不容易。在SolidColorBrush中,想象10个线程改变它的颜色,UI线程尝试渲染所有这些。因此,由于允许更改,读取也不安全

    现在,如果您的类继承自Freezable,那么WPF将以一种特殊的方式处理它。若该对象是可冻结的,则类作者保证该对象不能再被更改(在任何更改或其他情况下都会引发异常)。然后,从任何线程访问这样的对象都是安全的,即使这个对象与UI相关


    回到SolidColorBrush。创建它时(使用任何颜色,甚至预定义的颜色),默认情况下它不会冻结,并且可以随时更改其颜色属性。如果您使用预定义的笔刷(例如brush.Red)-它已经为您冻结,您无法使用brush.Red.Color=Colors.Blue。

    您需要执行Dispatcher.Invoke()调用以切换到UI线程。我现在身体不适,所以我只想链接到一个,让您继续。

    我觉得您提供的代码不合适。您展示了两个使用多线程的方法,然后在使用
    distributionParams.rangeData
    时出现错误,这似乎与这两个方法无关。。。您也没有说明如何在主线程中准备
    distributionParams
    。@Shaaman:很抱歉遗漏了一些内容。我创建图表对象并在其上调用Arrange。所以,在排列步骤中,它抛出异常。distributionParams是这样准备的:var d=新DistributionChartParameters.RangeData();d=新的DistributionChartParameters.RangeData();d、 范围=“0-100”;d、 分配值=3;d、 barColor=新的SolidColorBrush(Colors.Blue);范围数据。添加(d);名单上还有很多。但我只展示了一个项目。请告诉我你需要更多关于这个的信息。击中目标了。问题正是你上面说的。真是太棒了。谢谢但是你能告诉我为什么我要冻结我创建的纯色画笔吗?我将自己创建的自定义颜色作为参数传递给SolidColorBrush,在某些情况下不是预定义的笔刷。所以,我应该冻结,因为这是一个自定义的颜色,我正在创建?更新后的帖子回答你的问题。