WPF DrawingVisual on background thread?

I need not to block the interface when I draw a lot of simple shapes using WPF.

In WinForms, I set up a background buffer and draw a buffer in the background thread, and then draw the resulting buffer on the control. It works very well.

In WPF, I experimented using DrawingVisual, but it seems to block the UI thread when it draws the drawing.

How to move everything under DrawingVisual.RenderOpen () to a background thread so that it is not blocked when working with the user interface thread?

+7
source share
2 answers

Check out the library created by Dwayne Need. This will allow you to display the user interface for multiple threads. Also see this blog post.

+5
source

I want to add a way to draw a VisualBrush in a DrawingBrush in another thread.

As we know, VisualBrush should use in the user interface thread, and Visual Cub Cush Freeze, which cannot be used in another thread.

If you want to use VisualBrush in another stream and draw it in DrawingVisual, you must change VisualBrush to an image.

To change VisualBrush to an image as code:

public static BitmapSource ToImageSource(this Brush brush, Size size, double dpiX, double dpiY) { DrawingVisual drawingVisual = new DrawingVisual(); using (DrawingContext drawingContext = drawingVisual.RenderOpen()) drawingContext.DrawRectangle(brush, (Pen) null, new Rect(size)); BitmapImage bitmapImage = new BitmapImage(); if (Math.Abs(size.Width) > 0.001 && Math.Abs(size.Height) > 0.001) { RenderTargetBitmap bitmap = new RenderTargetBitmap((int) (size.Width * dpiX / 96.0), (int) (size.Height * dpiY / 96.0), dpiX, dpiY, PixelFormats.Pbgra32); bitmap.Render((Visual) drawingVisual); bitmapImage.BeginInit(); bitmapImage.DecodePixelWidth = (int) (size.Width * dpiX / 96.0); bitmapImage.DecodePixelHeight = (int) (size.Height * dpiY / 96.0); bitmapImage.StreamSource = (Stream) bitmap.ToMemoryStream(ImageFormat.Png); bitmapImage.EndInit(); bitmapImage.Freeze(); } return (BitmapSource) bitmapImage; } 

And you can draw it in another thread.

  var drawVisual=VisualBrush.ToImageSource(drawBounds.Size the drawBounds is we give, Dpix you can write 96, Dpiy); Thread thread = new Thread(() => { var target = new VisualTarget(hostVisual); s_event.Set(); var dv = new DrawingVisual(); using (var dc = dv.RenderOpen()) { dc.DrawRectangle(new ImageBrush(drawVisual), new Pen(Brushes.Black, 0.0), drawBounds); } target.RootVisual = dv; } 

But if you have to draw a VisualBrush for DrawingVisual and change the DrawingVisual to bitmapImage and show the image.

You must use VsisualBrush for image in UIThread

  List<(ImageSource brush, Rect drawBounds)> drawVisual = new List<(ImageSource, Rect)>(); foreach (var temp in Elements) { UIElement element = temp; Rect descendantBounds = VisualTreeHelper.GetDescendantBounds(element); var drawBounds = descendantBounds; drawBounds.Offset(temp location - new Point()); await Dispatcher.InvokeAsync(() => { var brush = new VisualBrush(element); drawVisual.Add((brush.ToImageSource(drawBounds.Size, Dpix, Dpiy), drawBounds)); }, DispatcherPriority.Input); } 

For VisualTaget, you should use Visual, how to change the DrawingVisual in BitmapImage and show it?

  HostVisual hostVisual = new HostVisual(); List<(ImageSource brush, Rect drawBounds)> drawVisual = new List<(ImageSource, Rect)>(); foreach (var temp in Elements) { Element element = temp; Rect descendantBounds = VisualTreeHelper.GetDescendantBounds(element); var drawBounds = descendantBounds; drawBounds.Offset(temp.Bounds.Location - new Point()); await Dispatcher.InvokeAsync(() => { var brush = new VisualBrush(element); drawVisual.Add((brush.ToImageSource(drawBounds.Size, Dpi.System.X, Dpi.System.Y), drawBounds)); }, DispatcherPriority.Input); } Thread thread = new Thread(() => { var target = new VisualTarget(hostVisual); s_event.Set(); var dv = new DrawingVisual(); using (var dc = dv.RenderOpen()) { foreach (var temp in drawVisual) { dc.DrawRectangle(new ImageBrush(temp.brush), new Pen(Brushes.Black, 0.0), temp.drawBounds); } } var bounds = VisualTreeHelper.GetDescendantBounds(dv); var width = (int) Math.Round(bounds.Width); var height = (int) Math.Round(bounds.Height); var bitmap = new RenderTargetBitmap((int) Math.Round(width * Dpi.System.FactorX), (int) Math.Round(height * Dpi.System.FactorY), Dpi.System.X, Dpi.System.Y, PixelFormats.Pbgra32); bitmap.Render(dv); dv = new DrawingVisual(); using (var dc = dv.RenderOpen()) { dc.DrawImage(bitmap, new Rect(size)); } target.RootVisual = dv; System.Windows.Threading.Dispatcher.Run(); }); thread.TrySetApartmentState(ApartmentState.STA); thread.IsBackground = true; thread.Start(); s_event.WaitOne(); VisualHost.Child = hostVisual; 

An element is our CustomControl that has a property to get its location.

But this is not a good way for Dispatcher.InvokeAsync takes too long.

0
source

All Articles