2009-07-14 8 views
36

Ho un marshalling IntPtr su un limite non gestito/gestito che corrisponde a un'icona Handle. Convertirlo in un'icona è banale tramite il metodo FromHandle(), e questo è stato soddisfacente fino a poco tempo fa.Converti System.Drawing.Icon in System.Media.ImageSource

Fondamentalmente, ho abbastanza stranezze di thread in corso ora che il ballo MTA/STA che ho suonato per mantenere un WinForm ospitato dalla rottura dell'interfaccia utente principale (WPF-tastic) dell'applicazione è troppo fragile da attaccare con. Quindi il WinForm deve andare.

Quindi, come posso ottenere una versione ImageSource di un'icona?

Nota, ho provato ImageSourceConverter senza alcun risultato.

Per inciso, posso ottenere la risorsa di fondo per qualche ma non tutte le icone coinvolti e in genere esistere al di fuori del montaggio della mia domanda (in realtà, spesso esistono in non gestito dll).

risposta

46

Prova questo:

Icon img; 

Bitmap bitmap = img.ToBitmap(); 
IntPtr hBitmap = bitmap.GetHbitmap(); 

ImageSource wpfBitmap = 
    Imaging.CreateBitmapSourceFromHBitmap(
      hBitmap, IntPtr.Zero, Int32Rect.Empty, 
      BitmapSizeOptions.FromEmptyOptions()); 

UPDATE: Incorporando il suggerimento di Alex e che lo rende un metodo di estensione:

internal static class IconUtilities 
{ 
    [DllImport("gdi32.dll", SetLastError = true)] 
    private static extern bool DeleteObject(IntPtr hObject); 

    public static ImageSource ToImageSource(this Icon icon) 
    {    
     Bitmap bitmap = icon.ToBitmap(); 
     IntPtr hBitmap = bitmap.GetHbitmap(); 

     ImageSource wpfBitmap = Imaging.CreateBitmapSourceFromHBitmap(
      hBitmap, 
      IntPtr.Zero, 
      Int32Rect.Empty, 
      BitmapSizeOptions.FromEmptyOptions()); 

     if (!DeleteObject(hBitmap)) 
     { 
      throw new Win32Exception(); 
     } 

     return wpfBitmap; 
    } 
} 

allora si può fare:

ImageSource wpfBitmap = img.ToImageSource(); 
+2

Dopo aver eseguito questa conversione, è necessario utilizzare la chiamata DeleteObject (IntPtr hObject) in gdi32.dll su hBitmap per evitare perdite di memoria. – Alex

+0

Anche la soluzione aggiornata potrebbe causare il problema "Il parametro non è valido" http://blog.lavablast.com/post/2007/11/The-Mysterious-Parameter-Is-Not-Valid-Exception.aspx in alcuni determinati situazioni, avrei bisogno di indagare di più però. Potrebbe essere più sicuro usare la soluzione di Darren invece –

+0

Ho guardato in giro per Internet negli sforzi per ottenere una risposta a questa soluzione. La maggior parte consiglia di utilizzare "Risorsa" anziché "Risorsa incorporata" al loro progetto. Il problema è che se non si desidera distribuire e nascondere le informazioni raccolte all'interno del progetto, è necessario incorporarle. Ciò che è stato interessante notare è stato il della risorsa restituita. Ad esempio, in WPF, per impostare un'icona nella finestra è necessario un tipo . L'accesso a "Properties.Resource. " ha un tipo GDI nativo e dovrà essere convertito in Windows.Media. – Latency

9
MemoryStream iconStream = new MemoryStream(); 
myForm.Icon.Save(iconStream); 
iconStream.Seek(0, SeekOrigin.Begin); 
_wpfForm.Icon = System.Windows.Media.Imaging.BitmapFrame.Create(iconStream); 
+1

Bello stare tutto in un codice facile da gestire. –

+1

È necessario disporre di MemoryStream? – devios1

+2

+1 per tutto il codice gestito. –

10

Quando si utilizzano flussi monouso, è quasi sempre consigliabile utilizzare i blocchi "uso" per forzare il corretto rilascio delle risorse.

using (MemoryStream iconStream = new MemoryStream()) 
{ 
    icon.Save(iconStream); 
    iconStream.Seek(0, SeekOrigin.Begin); 

    this.TargetWindow.Icon = System.Windows.Media.Imaging.BitmapFrame.Create(iconStream); 
} 

Dove icon è lo System.Drawing.Icon sorgente, e this.TargetWindow è lo System.Windows.Window bersaglio.

+1

ottenendo un'immagine di bassa qualità con questo metodo – user230910

+0

MemoryStream.Dispose non libera comunque qualcosa, http://referencesource.microsoft.com/#mscorlib/system/io/memorystream.cs,0b83d17ca69bf8ea,references – poizan42

+0

Ciò causa la mia applicazione WPF arrestarsi in modo anomalo con l'eccezione di smaltimento. Quindi, Windows ha bisogno di quel flusso di memoria in seguito quando si visualizza l'icona. È necessario mantenere vivo quel flusso di memoria durante la vita della tua finestra. e smaltirlo quando la finestra è chiusa. – Andy

69

metodo di conversione semplice senza creare oggetti extra:

public static ImageSource ToImageSource(this Icon icon) 
    { 
     ImageSource imageSource = Imaging.CreateBitmapSourceFromHIcon(
      icon.Handle, 
      Int32Rect.Empty, 
      BitmapSizeOptions.FromEmptyOptions()); 

     return imageSource; 
    } 
+8

Questa soluzione sembra più elegante di quella che usa Imaging.CreateBitmapSourceFromHBitmap. Non è necessario creare una bitmap non gestita (e quindi ricordarsi di eliminarla) quando Imaging può creare direttamente BitmapSource dall'icona. –

+0

Ottima soluzione !!! –

+0

Stai trascinando l'obsoleto 'System.Drawing' in WPF ... e se sei un purista del WPF, questo è un no-go. – Andy

-2

V'è una molto semplice soluzione a questo problema.

Passi:

(1) aggiungere un'immagine alle risorse in Esplora soluzioni -> Resources.resx (2) proprietà Modifica immagine dentro "risorse" di directory in Esplora soluzioni e il cambiamento "build azione" a "risorsa"

in XAML, aggiungere il seguente ...

Icon = "risorse/nome dell'immagine" (dove "nome dell'immagine" è il nome dell'immagine si è aggiunto alle risorse - vedi punto (1).

0

In qualche modo esame simile ple, solo sintonizzati dagli use case dello sviluppatore ...

[DllImport("shell32.dll")] 
    public static extern IntPtr ExtractIcon(IntPtr hInst, string file, int nIconIndex); 

    [DllImport("user32.dll", SetLastError = true)] 
    static extern bool DestroyIcon(IntPtr hIcon); 

    /// <summary> 
    /// Gets application icon from main .exe. 
    /// </summary> 
    /// <param name="setToObject">object to which to set up icon</param> 
    /// <param name="bAsImageSource">true if get it as "ImageSource" (xaml technology), false if get it as "Icon" (winforms technology)</param> 
    /// <returns>true if successful.</returns> 
    public bool GetIcon(object setToObject, bool bAsImageSource) 
    { 
     String path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); 
     path = Path.Combine(path, "yourmainexecutableName.exe"); 
     int iIconIndex = 0; 

     // If your application contains multiple icons, then 
     // you could change iIconIndex here. 

     object o2set = null; 
     IntPtr hIcon = ExtractIcon(IntPtr.Zero, path, iIconIndex); 
     if (hIcon == IntPtr.Zero) 
      return false; 

     Icon icon = (Icon)Icon.FromHandle(hIcon); 
     if (bAsImageSource) 
     { 
      o2set = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
       icon.ToBitmap().GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, 
       System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions()); 
     } else { 
      icon = (Icon)icon.Clone(); 
     } 

     DestroyIcon(hIcon); 
     setToObject.GetType().GetProperty("Icon").SetValue(setToObject, o2set); 
     return true; 
    } //GetIcon 
1

Prendendo da alcuni di sopra questo ha creato la più alta qualità di icone per me stesso. Caricamento delle icone da una matrice di byte. Uso la cache onload perché, se non lo fai, otterrai un'eccezione eliminata quando smaltisci il flusso di memoria.

internal static ImageSource ToImageSource(this byte[] iconBytes) 
    { 
     if (iconBytes == null) 
      throw new ArgumentNullException(nameof(iconBytes)); 
     using (var ms = new MemoryStream(iconBytes)) 
     { 
      return BitmapFrame.Create(ms, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad); 
     } 
    }