我想使用特定的ICO
文件作为我的图标和WinForms应用程序。由于我希望能够在Alt-Tabbing中为标题栏和普通图标(32x32)指定一个小图标(16x16),我不能使用接受单个Form.Icon
对象的System.Drawing.Icon
属性,这迫使我使用低分辨率图标或普通图标。
我发布了一个related question,它提出了一个非常简单的解决方案,适用于本机Win32应用程序:
SetClassLong(hWnd, GCL_HICON, hIcon32x32);
SetClassLong(hWnd, GCL_HICONSM, hIcon16x16);
尝试在Form
上应用相同的技巧不起作用。我有以下P / Invoke定义:
[DllImport ("User32.dll")]
extern static int SetClassLong(System.IntPtr hWnd, int index, int value);
const int GCL_HICON = -14;
const int GCL_HICONSM = -34;
然后我打电话给:
System.IntPtr hIcon32x32 = ...;
System.IntPtr hIcon16x16 = ...;
SetClassLong(this.Handle, GCL_HICON, hIcon32x32.ToInt32());
SetClassLong(this.Handle, GCL_HICONSM, hIcon16x16.ToInt32());
并且永远不会致电Form.Icon
。但是,这不起作用:
...但是,有趣的是,当我按下Alt-Tab时,我会在很短的时间内看到我使用GCL_HICON
定义的图标(如果我不使用GCL_HICONSM
GCL_HICON
)。似乎在幕后发生了一些事情,迫使Windows使用WinForms默认图标绘制图标。
我无法弄清楚我做错了什么以及幕后发生了什么。
编辑:我真的希望能够提供动态创建的两个不同的图标,而不是将Form.Icon
绑定到磁盘上的图标。这就是我尝试使用P / Invoke代码在内存中指定图标的原因。
答案 0 :(得分:3)
我实际上并没有通过测试或查看反汇编的WinForms代码来验证这一点,所以我不确定这个答案是否会满足“可信和/或官方来源”的赏金条件。但我觉得我很可靠,所以无论如何我都会试一试!
您正在设置与类窗口关联的图标。您使用SetClassLong[Ptr]
函数和GCL_HICON
/ GCL_HICONSM
索引进行此操作,但它与在类{}时WNDCLASSEX
结构中设置它具有相同的效果已经注册。这将为该类的窗口设置默认图标。
但是,单个窗口可以设置自己的图标,覆盖其类提供的默认图标。您可以发送WM_SETICON
消息,将ICON_BIG
或ICON_SMALL
作为wParam
传递,并将图标句柄作为lParam
传递。据推测,这就是WinForms正在做的事情。这就是出现“默认”WinForms图标而不是您正在分配的默认窗口类图标的原因,因为WinForms使用WM_SETICON
设置其默认图标,而不是通过窗口类。关于WinForms图标的唯一“默认”是,如果您没有分配不同的自定义图标,它将由框架自动分配。它不适合“默认”的任何其他定义 - 当然不是可能从Win32角度使用的定义。
Form.Icon
属性肯定使用WM_SETICON
来修改图标,这就是它按预期工作的原因。现在,您说您不想设置Icon属性,因为
我真的希望能够提供动态创建的两个不同的图标,而不是将Form.Icon绑定到磁盘上的图标。这就是我尝试使用P / Invoke代码在内存中指定图标的原因。
但这并不意味着您无法设置Icon
属性。您可以在此处指定图标的句柄(HICON
),就像使用P / Invoke一样。您所需要的只是静态Icon.FromHandle
方法,它从指定的Icon
创建一个新的HICON
对象。然后,将此Icon
对象分配给表单的Icon
属性。
const int WM_SETICON = 0x80;
enum IconType
{
ICON_BIG = 1;
ICON_SMALL = 0;
}
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd,
int message,
IntPtr wParam,
IntPtr lParam);
然后,将其称为与您拥有的相似:
IntPtr hIcon32x32 = ...;
IntPtr hIcon16x16 = ...;
SendMessage(this.Handle, WM_SETICON, (IntPtr)IconType.ICON_BIG, hIcon32x32);
SendMessage(this.Handle, WM_SETICON, (IntPtr)IconType.ICON_SMALL, hIcon16x16);
只有一件事你做错了:假设“大”图标总是32x32像素而“小”图标总是16x16像素。至少,我假设你是从变量的名字这样做的。如果是这样,那是一个无效的假设。这些只是最常见的尺寸。并不保证它们在所有环境中都是相同的。这就是为什么在.ico文件中提供更大的图标很重要的原因;例如,48x48图标。由于您正在动态设置图标,因此Windows无法访问较大的图标以进行缩减示例,并且当您的32x32图标按比例放大时,您可能会看到非常模糊和丑陋的内容。
要检索实际尺寸,请调用GetSystemMetrics
功能。 SM_CXICON
和SM_CYICON
标志将分别告诉您“大”图标的X和Y尺寸。 SM_CXSMICON
和SM_CYSMICON
标记将分别告诉您“小”图标的X和Y尺寸。
const int SM_CXICON = 11;
const int SM_CYICON = 12;
const int SM_CXSMICON = 49;
const int SM_CYSMICON = 50;
[DllImport("user32.dll")]
static extern int GetSystemMetrics(int smIndex);
static Size GetBigIconSize()
{
int x = GetSystemMetrics(SM_CXICON);
int y = GetSystemMetrics(SM_CYICON);
return Size(x, y);
}
static Size GetSmallIconSize()
{
int x = GetSystemMetrics(SM_CXSMICON);
int y = GetSystemMetrics(SM_CYSMICON);
return Size(x, y);
}
答案 1 :(得分:2)
您可以使用Form.Icon
。您只需要一个图标文件,其中包含图标的16x16和32x32像素版本。
我刚试了一次,使用的图标文件包含一个32x32像素的红色圆圈和一个16x16的蓝色矩形。小窗口图标显示蓝色矩形,alt-tab图标显示红色圆圈。
根本不需要P / Invoke。