Resize system icon in C#

2020-04-14 07:36发布

问题:

I want to use SystemIcons.Warning but it is too big for my need. I want to resize it.

I have tried :

Icon sizedIcon = new Icon(SystemIcons.Warning, new Size(10,10));

But it does not work, icon remains the same.

Any suggestions?

回答1:

The .NET Icon class is pretty crippled, it is stuck in the previous decade due to once-relevant support for Windows 98 and Windows 2000. On top of this, Windows does not support loading the system icons in any size other than the system's default icon size. Usually 32x32. Resizing it is going to inevitably look bad.

Do keep in mind that no icon is ever going to look good at 10x10. It is but a fleck on a modern monitor. You do get ahead a bit by starting with an icon size that's close to the desired final size, the less drastic the required resize, the higher the odds that it still looks reasonable. When you can, do favor sizes that are likely to be present in the icon. Like 16x16, 32x32, 48x48.

Anyhoo, this is fixable. Do keep in mind that this takes considerable hack-o-rama since Windows doesn't directly support this. What's required is reading the icon directly from the system DLL resources. And using a more modern winapi, LoadImage() instead of the one that .NET uses, LoadIcon(). Works on XP and up.

Add a new class to your project and paste this code:

using System;
using System.Drawing;
using System.Reflection;
using System.Runtime.InteropServices;

public class IconEx : IDisposable {
    public enum SystemIcons {
        Application = 100,
        Asterisk = 104,
        Error = 103,
        Exclamation = 101,
        Hand = 103,
        Information = 104,
        Question = 102,
        Shield = 106,
        Warning = 101,
        WinLogo = 100
    }

    public IconEx(string path, Size size) {
        IntPtr hIcon = LoadImage(IntPtr.Zero, path, IMAGE_ICON, size.Width, size.Height, LR_LOADFROMFILE);
        if (hIcon == IntPtr.Zero) throw new System.ComponentModel.Win32Exception();
        attach(hIcon);

    }
    public IconEx(SystemIcons sysicon, Size size) {
        IntPtr hUser = GetModuleHandle("user32");
        IntPtr hIcon = LoadImage(hUser, (IntPtr)sysicon, IMAGE_ICON, size.Width, size.Height, 0);
        if (hIcon == IntPtr.Zero) throw new System.ComponentModel.Win32Exception();
        attach(hIcon);
    }


    public Icon Icon {
        get { return this.icon; }
    }

    public void Dispose() {
        if (icon != null) icon.Dispose();
    }

    private Icon icon;

    private void attach(IntPtr hIcon) {
        // Invoke the private constructor so we can get the Icon object to own the handle
        var ci = typeof(Icon).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, 
            null, new Type[] { typeof(IntPtr), typeof(bool) }, null);
        this.icon = (Icon)ci.Invoke(new object[] { hIcon, true});
    }

    private const int IMAGE_ICON = 1;
    private const int LR_LOADFROMFILE = 0x10;
    private const int LR_SHARED = 0x8000;

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern IntPtr GetModuleHandle(string name);
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern IntPtr LoadImage(IntPtr hinst, string lpszName, int uType,
                                 int cxDesired, int cyDesired, int fuLoad);
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern IntPtr LoadImage(IntPtr hinst, IntPtr resId, int uType,
                                 int cxDesired, int cyDesired, int fuLoad);
}

Sample usage:

    using (var icon = new IconEx(IconEx.SystemIcons.Warning, new Size(10, 10))) {
        e.Graphics.DrawIcon(icon.Icon, 0, 0);
    }

It does look better than SystemIcons.Warning. It's squeaky clean when you use 16x16.



标签: c# icons