Pregunta

Estoy intentando generar un BitmapFrame que se basa en un UIElement . Aquí está mi función:

private BitmapFrame RenderToBitmap2()
{
    RenderTargetBitmap renderBitmap = new RenderTargetBitmap(200, 200, 96, 96, PixelFormats.Pbgra32);

    DrawingVisual drawingVisual = new DrawingVisual();
    DrawingContext drawingContext = drawingVisual.RenderOpen();
    VisualBrush aVisualBrush = new VisualBrush(GenerateTestStackPanel());
    drawingContext.DrawRectangle(aVisualBrush, new Pen(Brushes.Green, 2), new Rect(new Size(150, 150)));

    drawingContext.Close();

    renderBitmap.Render(drawingVisual);

    return BitmapFrame.Create(renderBitmap);
}

Para fines de prueba y depuración, estoy usando una función adicional que crea un StackFrame simple que debería crear un elemento visual válido que se pueda representar:

private StackPanel GenerateTestStackPanel()
{
    // Create a red Ellipse.
    Ellipse myEllipse = new Ellipse();

    myEllipse.Fill = Brushes.Green;
    myEllipse.StrokeThickness = 2;
    myEllipse.Stroke = Brushes.Black;

    // Set the width and height of the Ellipse.
    myEllipse.Width = 200;
    myEllipse.Height = 200;
    // Add the Ellipse to the StackPanel.
    StackPanel myStackPanel = new StackPanel();
    myStackPanel.Children.Add(myEllipse);
    return myStackPanel;
}

Por alguna razón, VisualBrush no se representa en la función DrawRetangle (...). Puedo ver el borde verde pero nada más. Además, si cambio el VisualBrush con un pincel estándar, funciona muy bien:

drawingContext.DrawRectangle(Brushes.Plum, new Pen(Brushes.Green, 2), new Rect(new Size(150, 150)));

¡Gracias de antemano!

¿Fue útil?

Solución

Eche un vistazo a esto para obtener una forma alternativa de crear un BitmapSource a partir de un UIElement :

MSDN Thread

También he estado tratando de hacer que VisualBrush funcione sin suerte, lo que me llevó a este hilo.

public static BitmapSource CreateBitmapSourceFromVisual(
    Double width,
    Double height,
    Visual visualToRender,
    Boolean undoTransformation)
    {
        if (visualToRender == null)
        {
            return null;
        }
        RenderTargetBitmap bmp = new RenderTargetBitmap((Int32)Math.Ceiling(width),
            (Int32)Math.Ceiling(height), 96, 96, PixelFormats.Pbgra32);

        if (undoTransformation)
        {
            DrawingVisual dv = new DrawingVisual();
            using (DrawingContext dc = dv.RenderOpen())
            {
                VisualBrush vb = new VisualBrush(visualToRender);
                dc.DrawRectangle(vb, null, new Rect(new Point(), new Size(width, height)));
            }
            bmp.Render(dv);
        }
        else
        {
            bmp.Render(visualToRender);
        }
      return bmp;
    }

Otros consejos

Tuve el mismo problema antes, quería copiar el contenido de un UIElement a una imagen, utilicé el mismo enfoque en la respuesta anterior y parece funcionar bien, solo el problema que tengo, Quería que funcionara en tiempo real, así que tuve que profundizar más, encontré algunas referencias sobre el uso de las API de Windows para copiar el contenido del elemento en un mapa de bits, y luego este mapa de bits debe convertirse en un BitmapSource para que se pueda usar en WPF

hasta ahora funciona bien, pero parece perder memoria (no estoy seguro de esto). Intentaré reutilizar el UIElement hwnd Handle y el objeto de mapa de bits para un mejor rendimiento y la pérdida de memoria (si existe)

[DllImport("gdi32.dll")]
private static extern bool BitBlt(
  IntPtr hdcDest, // handle to destination DC
  int nXDest, // x-coord of destination upper-left corner
  int nYDest, // y-coord of destination upper-left corner
  int nWidth, // width of destination rectangle
  int nHeight, // height of destination rectangle
  IntPtr hdcSrc, // handle to source DC
  int nXSrc, // x-coordinate of source upper-left corner
  int nYSrc, // y-coordinate of source upper-left corner
  System.Int32 dwRop // raster operation code
);

[DllImport("User32.dll")]
public extern static System.IntPtr GetDC(System.IntPtr hWnd);

[DllImport("User32.dll")]
public extern static int ReleaseDC(System.IntPtr hWnd, System.IntPtr hDC); //modified to include hWnd

//[DllImport("gdi32.dll")]
//[return: MarshalAs(UnmanagedType.Bool)]
//internal static extern bool DeleteObject(IntPtr hObject);

private static Bitmap GetBitmapFromControl(Window element, int width, int height)
{
    HwndSource hWnd = (HwndSource)HwndSource.FromVisual(element);
    System.IntPtr srcDC = GetDC(hWnd.Handle);

    Bitmap bm = new Bitmap(width, height);
    Graphics g = Graphics.FromImage(bm);
    System.IntPtr bmDC = g.GetHdc();
    BitBlt(bmDC, 0, 0, bm.Width, bm.Height, srcDC, 0, 0, 0x00CC0020 /*SRCCOPY*/);
    ReleaseDC(hWnd.Handle, srcDC);
    g.ReleaseHdc(bmDC);
    g.Dispose();

    return bm;
}

public static BitmapSource ToWpfBitmap(this Bitmap bitmap)
{
    using (MemoryStream stream = new MemoryStream())
    {
        bitmap.Save(stream, ImageFormat.Bmp);

        stream.Position = 0;
        BitmapImage result = new BitmapImage();
        result.BeginInit();
        // According to MSDN, "The default OnDemand cache option retains access to the stream until the image is needed."
        // Force the bitmap to load right now so we can dispose the stream.
        result.CacheOption = BitmapCacheOption.OnLoad;
        result.StreamSource = stream;
        result.EndInit();
        result.Freeze();


        //DeleteObject(bitmap.GetHbitmap());
        bitmap.Dispose();

        return result;
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top