Question

I am attempting to generate a BitmapFrame that is based on a UIElement. Here is my function:

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

For testing and debugging purposes, I am using an additional function that creates a simple StackFrame that should create a valid visual element that can be represented:

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

For some reason, the VisualBrush is not being rendered in the DrawRetangle(...) function. I can see the green border but nothing else. In addition, if I swap out the VisualBrush with a standard brush, it works great:

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

Thanks in advance!

Was it helpful?

Solution

Take a look at this for an alternate way to create a BitmapSource from a UIElement:

MSDN Thread

I have also been trying to get the VisualBrush to work without any luck which brought me to this thread.

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

OTHER TIPS

I had the same issue before, I wanted to copy content of a UIElement to an image, I used the same approach in the answer above and it seems to work fine, only problem I has, I wanted it to work real-time, so I had to dig deeper, I found some references about using windows APIs to copy the content of the element into a Bitmap, and then this bitmap has to be converted into a BitmapSource so it's usable in WPF

so far it works fine, but it seems to leak memory (not sure about this). I will try to re-use the UIElement hwnd Handle and the bitmap object for better performance and the memory leak (if it exists)

[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;
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top