调用UI更新时出现异常
本文关键字:异常 UI 更新 调用 | 更新日期: 2023-09-27 18:11:46
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;
}
我得到一个异常"不能使用一个DependencyObject,它属于一个不同于它的父线程Freezable."在canvasChart。当我在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;
}
请帮我解决这个问题
所以,在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。当您创建它(使用任何颜色,甚至是预定义的颜色)时,默认情况下它不会冻结,并且您可以随时更改其color属性。如果您使用预定义的画笔(画笔。(例如红色)-它已经为你冻结了,你不能再刷了。Red. color = Colors.Blue.
您需要执行Dispatcher.Invoke()调用来切换到UI线程。我现在身体不舒服,所以我将链接到一个so答案来帮助你。