I am new to C#, WPF & threading too. I am working on a web application with MVC5.
I got to know that I should do invoke on dispatcher when updating the UI elements in a thread other than main thread. But i am not exactly getting to know the changes I need to do.
I have a method GetBitmapForDistributionChart which does UI updation like this.
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);
});
}
where definition of DelegateBitmapGenerationToSTAThread looks like this:
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;
}
I get an exception "Cannot use a DependencyObject that belongs to a different thread than its parent Freezable." at canvasChart.Arrange when I add the following line in Chart class:
rect.Fill = distributionParams.rangeData.ElementAt(i).barColor;
which is in main thread.
If i change the same line to something which doesn't depend on right hand side class then it works.
Like, rect.Fill = new SolidColorBrush(Colors.Red);
I am not aware on how to fix this problem.
Note: Also I get exception "The calling thread cannot access this object because a different thread owns it." when trying to do this:
rect.Fill = new SolidColorBrush(distributionParams.rangeData.ElementAt(i).barColor.Color);
distributionParams structure looks like this:
public struct DistributionParams
{
public struct RangeData
{
public string range;
public double distributionValue;
public SolidColorBrush barColor;
}
public List<RangeData> rangeData;
}
Please help me fix this problem.
So, in DelegateBitmapGenerationToSTAThread you start new STA thread. Then you are trying to access DistributionParams.RangeData.barColor there, which is of type SolidColorBrush. You create those brushes in another (main UI) thread, that is why you are getting this exception. What you can do to solve that:
Try to freeze your brushes after creation.
d.barColor = new SolidColorBrush(...);
d.barColor.Freeze();
Use predefined brushes, they are frozen already:
d.barColor = Brushes.Blue;
Use color instead of SolidColorBrush
d.barColor = Colors.Blue;
then, create SolidColorBrush when necessary
rect.Fill = new SolidColorBrush(d.barColor);
UPDATE to answer the question in your comment. SolidColorBrush might look innocent, but that is still UI-related object which defines how interface would be rendered. Such objects in WPF (and WinForms) have thread-affinity - they might be accessed only by one thread (thread on which they were created). Why such limitation? Because it's not easy to correctly and efficiently implement concurrent changes to the properties of such elements which affect rendering. In case of SolidColorBrush, imagine 10 threads changing it's Color, and UI thread tries to render all that. So because changes are allowed - reads are not safe too.
Now, if your class inherits from Freezable, it is treated in a special way by WPF. If it's Freezable and frozen, class author guarantees that object cannot be changed any more (will throw exception on any change or whatever). Then it's safe to access such object from any thread, even if this object is UI-related.
Back to SolidColorBrush. When you create it (with any color, even predefined one), it's not frozen by default, and you can change its Color property any time. If you use predefined brush (Brushes.Red for example) - it is frozen already for you, you cannot do Brushes.Red.Color = Colors.Blue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With