2011-05-21 43 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來說不是一個好方法,需要太長時間。