2009-05-06 12 views
3

Je tente de générer un BitmapFrame basé sur un UIElement. Voici ma fonction:Générer BitmapSource à partir de UIElement

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

Pour des fins de test et de débogage, j'utilise une fonction supplémentaire qui crée simple StackFrame que devrait créer un élément visuel valide qui peut être représenté:

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

Pour une raison quelconque, VisualBrush n'est pas rendu dans la fonction DrawRetangle (...). Je peux voir la frontière verte mais rien d'autre. De plus, si j'échange le VisualBrush avec une brosse standard, il fonctionne très bien:

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

Merci à l'avance!

Répondre

4

Jetez un oeil à cela pour une autre façon de créer un BitmapSource d'un UIElement:

MSDN Thread

J'ai également essayé d'obtenir le VisualBrush de travailler sans chance qui m'a amené à ce fil .

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

J'ai eu le même problème avant, je voulais copier le contenu d'un UIElement à une image, j'ai utilisé la même approche dans la réponse ci-dessus et il semble fonctionner très bien, seul problème que j'a, je le voulais pour travailler en temps réel, j'ai donc dû creuser plus profondément, j'ai trouvé quelques références sur l'utilisation des API Windows pour copier le contenu de l'élément dans un bitmap, et ensuite cette bitmap doit être convertie en BitmapSource pour qu'elle soit utilisable dans WPF

jusqu'à présent cela fonctionne très bien, mais il semble y avoir une fuite de mémoire (pas sûr de cela). Je vais essayer de réutiliser la poignée hwnd UIElement et l'objet bitmap pour une meilleure performance et la fuite de la mémoire (si elle 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; 
    } 
}