将 System.Drawing.Icon 转换为 System.Media.ImageSource
Posted
技术标签:
【中文标题】将 System.Drawing.Icon 转换为 System.Media.ImageSource【英文标题】:Convert System.Drawing.Icon to System.Media.ImageSource 【发布时间】:2010-11-10 18:56:48 【问题描述】:我有一个跨非托管/托管边界封送的 IntPtr,该边界对应于一个图标句柄。通过 FromHandle() 方法将其转换为图标很简单,直到最近才令人满意。
基本上,我已经有足够多的线程奇怪了,因为我一直在玩 MTA/STA 舞蹈以防止托管的 WinForm 破坏应用程序的主要(WPF-tastic)UI 太脆弱而无法坚持.所以 WinForm 必须离开。
那么,我怎样才能获得 ImageSource 版本的 Icon?
注意,我试过 ImageSourceConverter 无济于事。
顺便说一句,我可以获得一些的底层资源,但不是所有涉及的图标,它们通常存在于我的应用程序程序集之外(实际上,它们通常存在于非托管 dll 中)。
【问题讨论】:
【参考方案1】:简单的转换方法,无需创建任何额外的对象:
public static ImageSource ToImageSource(this Icon icon)
ImageSource imageSource = Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
return imageSource;
【讨论】:
这个解决方案似乎比使用 Imaging.CreateBitmapSourceFromHBitmap 的解决方案更优雅。当 Imaging 可以直接从 Icon 句柄创建 BitmapSource 时,无需创建非托管位图(然后必须记住处理它)。 您正在将过时的System.Drawing
拖入 WPF...如果您是 WPF 纯粹主义者,那么这是不行的。
CreateBitmapSourceFromHIcon
位于 System.Windows.Interop
命名空间中,因此请务必添加您的 Using 或 Import 语句。【参考方案2】:
试试这个:
Icon img;
Bitmap bitmap = img.ToBitmap();
IntPtr hBitmap = bitmap.GetHbitmap();
ImageSource wpfBitmap =
Imaging.CreateBitmapSourceFromHBitmap(
hBitmap, IntPtr.Zero, Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
更新:采纳 Alex 的建议并使其成为扩展方法:
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;
那么你可以这样做:
ImageSource wpfBitmap = img.ToImageSource();
【讨论】:
完成此转换后,您应该在 hBitmap 上的 gdi32.dll 中使用 DeleteObject( IntPtr hObject ) 调用以避免内存泄漏。 在某些特定情况下,即使更新的解决方案也可能导致“参数无效”问题blog.lavablast.com/post/2007/11/…,但我需要进行更多调查。使用 Darren 的解决方案可能更安全 我在互联网上浏览了很长一段时间,试图找到这个解决方案的答案。大多数人建议在他们的项目中使用“资源”而不是“嵌入式资源”。问题在于,如果您不想在项目中分发和隐藏资源信息,则需要嵌入它们。值得注意的是要返回的资源的在使用一次性流时,几乎总是建议使用“使用”块来强制正确释放资源。
using (MemoryStream iconStream = new MemoryStream())
icon.Save(iconStream);
iconStream.Seek(0, SeekOrigin.Begin);
this.TargetWindow.Icon = System.Windows.Media.Imaging.BitmapFrame.Create(iconStream);
在哪里
icon
是源 System.Drawing.Icon,this.TargetWindow
是目标 System.Windows.Window。
【讨论】:
用这种方法得到非常低质量的图像 MemoryStream.Dispose 并没有释放任何东西,referencesource.microsoft.com/#mscorlib/system/io/… 这会导致我的 WPF 应用程序崩溃并处理异常。因此,Windows 稍后在显示图标时需要该内存流。您需要在窗口的整个生命周期中保持该内存流活跃。并在窗口关闭时将其丢弃。 我修复了它...使用这种格式,您可以像上面的示例一样处理内存流:BitmapFrame.Create(iconStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad)
【参考方案4】:
MemoryStream iconStream = new MemoryStream();
myForm.Icon.Save(iconStream);
iconStream.Seek(0, SeekOrigin.Begin);
_wpfForm.Icon = System.Windows.Media.Imaging.BitmapFrame.Create(iconStream);
【讨论】:
您需要处理 MemoryStream 吗? 应该在流上使用 Dispose 并且由于某种原因这给了我非常低质量的黑白图像。其他人有这个问题吗? @Patrick,我在使用 MemoryStream 时也遇到了问题,并且输出质量非常低。我按字节(从下方)使用this solution。 您不需要释放 MemoryStream,它除了将其标记为已释放之外什么都不做:referencesource.microsoft.com/#mscorlib/system/io/…。但是它会稍微提高 GC 的性能,因为 Stream.Dispose 会将其从终结队列中移除。【参考方案5】:从上面的一些内容为我自己创造了最高质量的图标。从字节数组加载图标。我使用缓存加载,因为如果你不这样做,当你释放内存流时你会得到一个释放的异常。
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);
【讨论】:
【参考方案6】:有点类似的例子,只是根据开发者的用例调整...
[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
【讨论】:
【参考方案7】:这个问题有一个非常简单的解决方案。
步骤:
(1) 在解决方案资源管理器中添加图片到资源 -> resources.resx (2) 在解决方案资源管理器中编辑“资源”目录中的图像属性并将“构建操作”更改为“资源”
在 xaml 中,添加以下...
Icon="resources/name of image"(其中“name of image”是您添加到资源中的图像的名称 - 请参见第 (1) 点。
【讨论】:
以上是关于将 System.Drawing.Icon 转换为 System.Media.ImageSource的主要内容,如果未能解决你的问题,请参考以下文章
我用VS2010写的一个C#窗体程序打包后安装,启动后有一个界面无法启动,报错啥异常文本,怎么回事啊
将 A 转换为 1 B 转换为 2 ... Z 转换为 26,然后将 AA 转换为 27 AB 转换为 28(Excel 中列引用的列索引)
将rowdatapacket转换为数组,如何将mysql node.js api rowdatapacket转换为数组,将字符串转换为数组