2011-05-21 44 views
7

我需要不要阻止用户界面,而我使用WPF绘制很多简单的形状。在后台线程上的WPF DrawingVisual?

在WinForms中,我将设置后台缓冲区并在后台线程上绘制到缓冲区,然后将得到的缓冲区绘制到控件中。它工作得很好。在WPF中,我已经尝试过使用DrawingVisual,但它似乎在组成绘图时阻塞UI线程。

如何将DrawingVisual.RenderOpen()下的所有内容移动到后台线程,以便在UI线程处于工作状态时不会被阻止?

回答

5

看看由Dwayne Need创建的library。它将允许您在多个线程上呈现UI。另见this博客文章。

+0

DwayneNeed使用HostVisual绘制画笔,但当画笔是在UI线程中的VisualBrush时,您不会在其他线程中绘制它。 – lindexi 2017-06-21 02:47:09

0

我想添加一种方法来绘制一个VisualBrush DrawingBrush在其他线程。

正如我们所知,VisualBrush应该在UI线程中使用,而VisualBrush不能在其他线程中使用Freeze。

如果要在其他线程中使用VisualBrush并将其绘制到DrawingVisual,则应将VisualBrush更改为Image。

要改变VisualBrush以图片作为代码:

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; 
    } 

而且你可以在其他线程绘制。

 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; 
     } 

但是,如果您应该绘制一些VisualBrush DrawingVisual并将DrawingVisual更改为bitmapImage并显示图像。

你应该WIRTE的VsisualBrush到图像中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); 
     } 

对于VisualTaget应该使用Visual,如何将DrawingVisual更改为BitmapImage的,并显示它?

 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; 

元素是我们的CustomControl,它有一个属性来获取它的位置。

但是它对于Dispatcher.InvokeAsync来说不是一个好方法,需要太长时间。