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.