2011-05-21 13 views
7

No necesito bloquear la IU mientras dibujo muchas formas simples usando WPF.WPF DrawingVisual en un hilo de fondo?

En WinForms, configuraba el búfer de fondo y dibujaba el búfer en una cadena de fondo, luego dibujaba el búfer resultante en el control. Funciona muy bien.

En WPF, he experimentado con el uso de un DrawingVisual, pero parece bloquear el hilo de la interfaz de usuario mientras compone el dibujo.

¿Cómo movería todo en DrawingVisual.RenderOpen() a un hilo de fondo para que, mientras esté trabajando, el hilo de la interfaz de usuario no esté bloqueado?

Respuesta

5

Eche un vistazo a library creado por Dwayne Need. Le permitirá procesar la IU en múltiples hilos. También vea la publicación del blog this.

+0

DwayneNeed usa HostVisual para dibujar el pincel, pero cuando el Pincel es VisualBrush, en el subproceso UI, no lo dibujará en otro subproceso. – lindexi

0

Quiero agregar una forma de dibujar un VisualBrush a DrawingBrush en otro hilo.

Como todos sabemos, el VisualBrush debe usar en el subproceso de IU y el bloqueo de VisualBrush que no se puede usar en otro hilo.

Si desea utilizar VisualBrush en otro hilo y dibujarlo en DrawingVisual, debe cambiar VisualBrush a Image.

Para cambiar VisualBrush de imagen como el código:

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

y se puede dibujar en el otro hilo.

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

Pero si debe dibujar una cierta VisualBrush a DrawingVisual y cambiar el DrawingVisual a BitmapImage y mostrar la imagen.

Debe wirte la VsisualBrush a la imagen en 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); 
     } 

Para VisualTaget debe utilizar el Visual, cómo cambiar el DrawingVisual a BitmapImage y muestran?

 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; 

El elemento es nuestro CustomControl que tiene una propiedad para obtener su ubicación.

Pero no es una buena manera para Dispatcher.InvokeAsync necesita demasiado tiempo.

Cuestiones relacionadas