2008-09-18 12 views

Respuesta

77

Gracias a Hallgrim, aquí está el código que terminó con:

ScreenCapture = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
    bmp.GetHbitmap(), 
    IntPtr.Zero, 
    System.Windows.Int32Rect.Empty, 
    BitmapSizeOptions.FromWidthAndHeight(width, height)); 

también terminé la unión a un BitmapSource en lugar de un BitmapImage como en mi pregunta original

+2

¡Estupendo!¿Por qué no seleccionas tu propia respuesta como la respuesta a la pregunta? Tu es mucho mejor ahora. – Hallgrim

+1

Dado que la suya ya es la respuesta aceptada, puede editar su respuesta para hacerlo más completo. –

+39

Tenga en cuenta que este código pierde un HBitmap. Consulte http://stackoverflow.com/questions/1118496/using-image-control-in-wpf-to-display-system-drawing-bitmap/1118557#1118557 para obtener una corrección –

10

Lo más fácil es si puede hacer el mapa de bits WPF directamente desde un archivo.

De lo contrario, deberá usar System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap.

6

Trabajo en un proveedor de imágenes y escribí un adaptador para WPF en nuestro formato de imagen que es similar a System.Drawing.Bitmap.

de escribir este KB a explicar a nuestros clientes:

http://www.atalasoft.com/kb/article.aspx?id=10156

y no hay código de allí que lo hace. Necesita reemplazar AtalaImage con Bitmap y hacer lo equivalente que estamos haciendo, debería ser bastante sencillo.

+0

Gracias Lou - pude hacer lo que necesitaba con una línea del código – Kevin

242

¿Qué hay de cargarlo desde MemoryStream?

using(MemoryStream memory = new MemoryStream()) 
{ 
    bitmap.Save(memory, ImageFormat.Png); 
    memory.Position = 0; 
    BitmapImage bitmapImage = new BitmapImage(); 
    bitmapImage.BeginInit(); 
    bitmapImage.StreamSource = memory; 
    bitmapImage.CacheOption = BitmapCacheOption.OnLoad; 
    bitmapImage.EndInit(); 
} 
+10

Puede agregar este código como método de extensión en System.Drawing.Bitmap, algo así como ToBitmapImage() –

+35

Usar ImageFormat.Bmp es un orden de magnitud más rápido. – RandomEngy

+20

En caso de que otros tengan problemas con este código: Tuve que agregar 'ms.Seek (0, SeekOrigin.Begin);' antes de establecer 'bi.StreamSource'. Estoy usando .NET 4.0. – mlsteeves

51

Sé que esto ha sido respondido, pero aquí hay un par de métodos de extensión (para .NET 3.0+) que hacen la conversión. :)

 /// <summary> 
    /// Converts a <see cref="System.Drawing.Image"/> into a WPF <see cref="BitmapSource"/>. 
    /// </summary> 
    /// <param name="source">The source image.</param> 
    /// <returns>A BitmapSource</returns> 
    public static BitmapSource ToBitmapSource(this System.Drawing.Image source) 
    { 
     System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(source); 

     var bitSrc = bitmap.ToBitmapSource(); 

     bitmap.Dispose(); 
     bitmap = null; 

     return bitSrc; 
    } 

    /// <summary> 
    /// Converts a <see cref="System.Drawing.Bitmap"/> into a WPF <see cref="BitmapSource"/>. 
    /// </summary> 
    /// <remarks>Uses GDI to do the conversion. Hence the call to the marshalled DeleteObject. 
    /// </remarks> 
    /// <param name="source">The source bitmap.</param> 
    /// <returns>A BitmapSource</returns> 
    public static BitmapSource ToBitmapSource(this System.Drawing.Bitmap source) 
    { 
     BitmapSource bitSrc = null; 

     var hBitmap = source.GetHbitmap(); 

     try 
     { 
      bitSrc = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
       hBitmap, 
       IntPtr.Zero, 
       Int32Rect.Empty, 
       BitmapSizeOptions.FromEmptyOptions()); 
     } 
     catch (Win32Exception) 
     { 
      bitSrc = null; 
     } 
     finally 
     { 
      NativeMethods.DeleteObject(hBitmap); 
     } 

     return bitSrc; 
    } 

y la clase NativeMethods (para apaciguar FxCop)

/// <summary> 
/// FxCop requires all Marshalled functions to be in a class called NativeMethods. 
/// </summary> 
internal static class NativeMethods 
{ 
    [DllImport("gdi32.dll")] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    internal static extern bool DeleteObject(IntPtr hObject); 
} 
+1

Al usar identificadores no administrados (por ejemplo, HBITMAP) considere usar SafeHandles, consulte http://stackoverflow.com/questions/1546091/wpf-createbitmapsourcefromhbitmap-memory-leak/7035036#7035036 – Schneider

21

Me tomó un tiempo para conseguir la conversión de trabajo en ambos sentidos, así que aquí están los dos métodos de extensión que se me ocurrió:

using System.Drawing; 
using System.Drawing.Imaging; 
using System.IO; 
using System.Windows.Media.Imaging; 

public static class BitmapConversion { 

    public static Bitmap ToWinFormsBitmap(this BitmapSource bitmapsource) { 
     using (MemoryStream stream = new MemoryStream()) { 
      BitmapEncoder enc = new BmpBitmapEncoder(); 
      enc.Frames.Add(BitmapFrame.Create(bitmapsource)); 
      enc.Save(stream); 

      using (var tempBitmap = new Bitmap(stream)) { 
       // According to MSDN, one "must keep the stream open for the lifetime of the Bitmap." 
       // So we return a copy of the new bitmap, allowing us to dispose both the bitmap and the stream. 
       return new Bitmap(tempBitmap); 
      } 
     } 
    } 

    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(); 
      return result; 
     } 
    } 
} 
+1

Estoy usando esto, pero use ImageFormat.Png. De lo contrario, obtengo un fondo negro en la imagen: http://stackoverflow.com/questions/4067448/converting-image-to-bitmap-turns-background-black –

2

llegué a esta pregunta porque yo estaba tratando de hacer lo mismo, pero en mi caso, el mapa de bits es de un recurso/archivo. He encontrado la mejor solución es como se describe en el siguiente enlace:

http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.bitmapimage.aspx

// Create the image element. 
Image simpleImage = new Image();  
simpleImage.Width = 200; 
simpleImage.Margin = new Thickness(5); 

// Create source. 
BitmapImage bi = new BitmapImage(); 
// BitmapImage.UriSource must be in a BeginInit/EndInit block. 
bi.BeginInit(); 
bi.UriSource = new Uri(@"/sampleImages/cherries_larger.jpg",UriKind.RelativeOrAbsolute); 
bi.EndInit(); 
// Set the image source. 
simpleImage.Source = bi; 
9
// at class level; 
[System.Runtime.InteropServices.DllImport("gdi32.dll")] 
public static extern bool DeleteObject(IntPtr hObject); // https://stackoverflow.com/a/1546121/194717 


/// <summary> 
/// Converts a <see cref="System.Drawing.Bitmap"/> into a WPF <see cref="BitmapSource"/>. 
/// </summary> 
/// <remarks>Uses GDI to do the conversion. Hence the call to the marshalled DeleteObject. 
/// </remarks> 
/// <param name="source">The source bitmap.</param> 
/// <returns>A BitmapSource</returns> 
public static System.Windows.Media.Imaging.BitmapSource ToBitmapSource(this System.Drawing.Bitmap source) 
{ 
    var hBitmap = source.GetHbitmap(); 
    var result = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, System.Windows.Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions()); 

    DeleteObject(hBitmap); 

    return result; 
} 
+0

¿Qué es "DeleteObject()"? –

+0

Ver http://stackoverflow.com/questions/1546091/wpf-createbitmapsourcefromhbitmap-memory-leak – tofutim

3

Mi opinión sobre esta construido a partir de una serie de recursos. https://stackoverflow.com/a/7035036https://stackoverflow.com/a/1470182/360211

using System; 
using System.Drawing; 
using System.Runtime.ConstrainedExecution; 
using System.Runtime.InteropServices; 
using System.Security; 
using System.Windows; 
using System.Windows.Interop; 
using System.Windows.Media.Imaging; 
using Microsoft.Win32.SafeHandles; 

namespace WpfHelpers 
{ 
    public static class BitmapToBitmapSource 
    { 
     public static BitmapSource ToBitmapSource(this Bitmap source) 
     { 
      using (var handle = new SafeHBitmapHandle(source)) 
      { 
       return Imaging.CreateBitmapSourceFromHBitmap(handle.DangerousGetHandle(), 
        IntPtr.Zero, Int32Rect.Empty, 
        BitmapSizeOptions.FromEmptyOptions()); 
      } 
     } 

     [DllImport("gdi32")] 
     private static extern int DeleteObject(IntPtr o); 

     private sealed class SafeHBitmapHandle : SafeHandleZeroOrMinusOneIsInvalid 
     { 
      [SecurityCritical] 
      public SafeHBitmapHandle(Bitmap bitmap) 
       : base(true) 
      { 
       SetHandle(bitmap.GetHbitmap()); 
      } 

      [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 
      protected override bool ReleaseHandle() 
      { 
       return DeleteObject(handle) > 0; 
      } 
     } 
    } 
} 
3

Sólo puede compartir el pixeldata entre ambos un espacio de nombres (los medios de comunicación y de dibujo) escribiendo un BitmapSource personalizado. La conversión ocurrirá inmediatamente y no se asignará memoria adicional. Si no desea crear explícitamente una copia de su mapa de bits, este es el método que desea.

class SharedBitmapSource : BitmapSource, IDisposable 
{ 
    #region Public Properties 

    /// <summary> 
    /// I made it public so u can reuse it and get the best our of both namespaces 
    /// </summary> 
    public Bitmap Bitmap { get; private set; } 

    public override double DpiX { get { return Bitmap.HorizontalResolution; } } 

    public override double DpiY { get { return Bitmap.VerticalResolution; } } 

    public override int PixelHeight { get { return Bitmap.Height; } } 

    public override int PixelWidth { get { return Bitmap.Width; } } 

    public override System.Windows.Media.PixelFormat Format { get { return ConvertPixelFormat(Bitmap.PixelFormat); } } 

    public override BitmapPalette Palette { get { return null; } } 

    #endregion 

    #region Constructor/Destructor 

    public SharedBitmapSource(int width, int height,System.Drawing.Imaging.PixelFormat sourceFormat) 
     :this(new Bitmap(width,height, sourceFormat)) { } 

    public SharedBitmapSource(Bitmap bitmap) 
    { 
     Bitmap = bitmap; 
    } 

    // Use C# destructor syntax for finalization code. 
    ~SharedBitmapSource() 
    { 
     // Simply call Dispose(false). 
     Dispose(false); 
    } 

    #endregion 

    #region Overrides 

    public override void CopyPixels(Int32Rect sourceRect, Array pixels, int stride, int offset) 
    { 
     BitmapData sourceData = Bitmap.LockBits(
     new Rectangle(sourceRect.X, sourceRect.Y, sourceRect.Width, sourceRect.Height), 
     ImageLockMode.ReadOnly, 
     Bitmap.PixelFormat); 

     var length = sourceData.Stride * sourceData.Height; 

     if (pixels is byte[]) 
     { 
      var bytes = pixels as byte[]; 
      Marshal.Copy(sourceData.Scan0, bytes, 0, length); 
     } 

     Bitmap.UnlockBits(sourceData); 
    } 

    protected override Freezable CreateInstanceCore() 
    { 
     return (Freezable)Activator.CreateInstance(GetType()); 
    } 

    #endregion 

    #region Public Methods 

    public BitmapSource Resize(int newWidth, int newHeight) 
    { 
     Image newImage = new Bitmap(newWidth, newHeight); 
     using (Graphics graphicsHandle = Graphics.FromImage(newImage)) 
     { 
      graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic; 
      graphicsHandle.DrawImage(Bitmap, 0, 0, newWidth, newHeight); 
     } 
     return new SharedBitmapSource(newImage as Bitmap); 
    } 

    public new BitmapSource Clone() 
    { 
     return new SharedBitmapSource(new Bitmap(Bitmap)); 
    } 

    //Implement IDisposable. 
    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    #endregion 

    #region Protected/Private Methods 

    private static System.Windows.Media.PixelFormat ConvertPixelFormat(System.Drawing.Imaging.PixelFormat sourceFormat) 
    { 
     switch (sourceFormat) 
     { 
      case System.Drawing.Imaging.PixelFormat.Format24bppRgb: 
       return PixelFormats.Bgr24; 

      case System.Drawing.Imaging.PixelFormat.Format32bppArgb: 
       return PixelFormats.Pbgra32; 

      case System.Drawing.Imaging.PixelFormat.Format32bppRgb: 
       return PixelFormats.Bgr32; 

     } 
     return new System.Windows.Media.PixelFormat(); 
    } 

    private bool _disposed = false; 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!_disposed) 
     { 
      if (disposing) 
      { 
       // Free other state (managed objects). 
      } 
      // Free your own state (unmanaged objects). 
      // Set large fields to null. 
      _disposed = true; 
     } 
    } 

    #endregion 
} 
+0

¿Puedes publicar un ejemplo? – shady

+1

Exactamente lo que estaba buscando, espero que esto funcione cuando lo compile = D – Greg

+0

Entonces, si tiene Properties.Resources.Image y desea dibujarlo en un lienzo, ¿necesita 133 líneas de código? WPF no está bien. –

Cuestiones relacionadas