C# GDI+编程(五)
调用API函数,在窗口非客户区绘图
GDI+的Graphics类里有个FromHdc函数,这个函数可以根据窗口设备上下文(DC)创建Graphics对象,在vc++中,窗口客户区与非客户区的绘图无非就是GetWindowDC和GetDC函数的不同调用。前者获得整个窗口DC,后者获得窗口客户区DC。
那么我们就可以在C#里,调用GetWindowDC函数获取整个窗口DC,然后通过FromHdc加载进去,这样我们就能针对整个窗口绘图了。
C#要如何调用WINDOWS API呢,或者说如何调用动态链接库(DLL)里的函数。
跟VC++的大同小异,先导入动态链接库,然后再声明API函数,如下:
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern IntPtr GetWindowDC(IntPtr hwnd);
当然上面是最简单的,还有一些细节没有讲,先就这样吧,会基本使用就行了,那些细节问题以后再详细说明。
在C#中,我们发现API函数的参数类型都不一样了,比如在VC++中的句柄HDC,HWND。在这里声明时,都用了IntPtr代替,这是没有办法的事,因为C#没有指针这个概念,而我们通过查HDC,和HWND类型定义时发现,它们都是指针类型。
所以在C#中,这些“句柄”类型都用IntPtr代替,包括区域句柄HRGN,HICON图标,HFONT字体句柄等。
看一个示例吧,(接着上一章的)
public partial class Form1 : Form
{
//导入动态链接库,声明函数,这个函数是声明在Form1类里的。
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern IntPtr GetWindowDC(IntPtr hwnd);
//存储PNG非透明部分的路径
private GraphicsPath path = new GraphicsPath();
//加载PNG图片
Bitmap bmp = new Bitmap("d:\\Image\\win.png");
public Form1()
{
InitializeComponent();
//判断每个像素的颜色值,获取图片的显示区域
for (int y = 0; y < bmp.Height; y++)
for (int x = 0; x < bmp.Width; x++)
{
Color cor = bmp.GetPixel(x, y);
int argb = cor.ToArgb();
byte[] bargb = BitConverter.GetBytes(argb);
//像素颜色值不是透明的
if (bargb[3] != 0)
{
//把这个像素点区域添加到路径里去
path.AddRectangle(new Rectangle(x, y, 1, 1));
}
}
//设置窗口显示区域,通过路径创建区域
this.Region = new Region(path);
this.Paint += formPaint;
}
private void formPaint(object sender, PaintEventArgs e)
{
OnPaintBackground(e);
//Handle是窗口句柄,它是一个IntPtr类型
IntPtr hdc = GetWindowDC(this.Handle);
//根据窗口DC创建Graphics对象
Graphics gr = Graphics.FromHdc(hdc);
//绘制图片
gr.DrawImage(bmp, new Rectangle(0, 0, bmp.Width, bmp.Height));
}
protected override void OnPaintBackground(PaintEventArgs e)
{
//透明画刷填充
//base.OnPaintBackground(e);
e.Graphics.FillRectangle(Brushes.Transparent, this.ClientRectangle);
}
}
怎么样,效果不错吧,但一拖动窗口就原形毕露了,注意到苹果下方的阴影了么,就是为了实现这个效果才会带来一些问题,或者说麻烦了许多吧。只是我没去解决。移动窗口,或者最大化窗口,都没有完全刷新整个窗口,才会导致这种问题出现。这个问题留待以后解决吧,
在兴趣的朋友也可以去解决一下这个问题。
另外,我用透明画刷填充的只是窗口的客户区,如果想填充整个窗口(包括标题栏),方法跟在整个窗口绘图一样,获得WindowDC,然后
创建Graphics对象,绘制窗口背景。
(题外话:在vc++中,客户区与非客户区有着不同的重绘消息,WM_PAINT和WM_NCPAINT,这一点要注意了,在刷新非客户区的时候,别重绘客户区,虽说不会出什么问题,但影响了效率总是不好的,能避免就避免)
自绘窗口非客户区(包括标题栏,最大,最小化,关闭按钮)
重写消息处理函数WndProc
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0xA3)//WM_NCLBUTTONDBLCLK 双击标题消息
MessageBox.Show("你双击了标题栏");
//默认消息处理
base.WndProc(ref m);
}
}
这样双击标题栏的时候就会给出一个提示,然后再默认处理。
查消息对应的数值,可以到VC++编译器里去查,比如打上WM_LBUTTONDOWN然后右击,选择转到定义就可以查看了。
m.HWnd存储有窗口句柄,m.LParam和m.WParam是消息的附带信息,可以参考CreateWindow函数里的WPARAM和LPARAM参数解释。
自绘非客户区工作量实在是太大了,这里我只给个大概的思路,方向,以后有空再来做吧。
前提当然是把各项数据计算出来,比如窗口有无边框,如果有的话,获取边框宽度,高度,然后计算四个边框的矩形区域。
最后就判断窗口有无最大,最大小化属性,然后获得三个按钮的区域。
而SystemInformation类里就存储有这些数据,比如SystemInformation.CaptionButtonSize存储有标题栏按钮的大小,得到了大小,就可以
确定按钮的区域了,因为这三个按钮都在窗口的右上角,除去边框的高宽。
而SystemInformation.CaptionHeight存储有标题栏的高度,边框的高宽存储在SystemInformation.BorderSize或者SystemInformation.Border3DSize,这个根据窗口的FormBorderStyle决定。窗口的是否处于最大化可以判断MaximizeBox,为true最大化。
得到了上面那些数据,就响应非客户区的各种消息,如鼠标左键消息WM_NCLBUTTONDOWN和WM_NCLBUTTONUP。
鼠标移动消息WM_NCMOUSEMOVE,接着就开始自绘了。
另Rectangle类里的Contains函数,可以判断一个点是否在一个矩形区域内。
更多C# GDI+编程(五)相关文章请关注PHP中文网!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Sujets chauds

Cet article explore les défis des déréférences du pointeur nul dans C. Il soutient que le problème n'est pas nul lui-même, mais son utilisation abusive. L'article détaille les meilleures pratiques pour prévenir les déréférences, y compris les contrôles de pré-déréférence, le pointeur initiali

Cet article guide les débutants sur le choix d'un compilateur C. Il fait valoir que le CCG, en raison de sa facilité d'utilisation, de sa large disponibilité et de ses vastes ressources, est la meilleure pour les débutants. Cependant, il compare également GCC, Clang, MSVC et TCC, mettant en évidence leur différenciation

Cet article explique comment créer de nouvelles caractères en C en C en utilisant la séquence \ n Escape dans printf et met des fonctions. Il détaille les fonctionnalités et fournit des exemples de code démontrant son utilisation pour les ruptures de ligne en sortie.

Cet article met l'accent sur l'importance continue de Null dans la programmation C moderne. Malgré les progrès, NULL reste crucial pour la gestion explicite du pointeur, empêchant les défauts de segmentation en marquant l'absence d'une adresse mémoire valide. Meilleur prac

Cet article passe en revue les compilateurs C en ligne pour les débutants, en se concentrant sur la facilité d'utilisation et les capacités de débogage. Onlinegdb et Rep.Il sont mis en évidence pour leurs interfaces conviviales et leurs outils de débogage utiles. Autres options comme Programiz et Compil

Cet article compare les plateformes de programmation C en ligne, mettant en évidence les différences de fonctionnalités telles que les outils de débogage, les fonctionnalités IDE, la conformité standard et les limites de mémoire / d'exécution. Il soutient que la "meilleure" plate-forme dépend des besoins des utilisateurs,

Cet article traite de la copie de code efficace dans les Cides. Il souligne que la copie est une fonction IDE, pas une fonctionnalité de compilateur, et détaille des stratégies pour une efficacité améliorée, y compris l'utilisation d'outils de sélection IDE, le pliage de code, la recherche / remplacer, Templa

Cet article dépanne les fenêtres de sortie manquantes dans la compilation du programme C. Il examine des causes telles que le non-exécution de l'exécutable, des erreurs de programme, des paramètres de compilateur incorrects, des processus de fond et une terminaison rapide du programme. Les solutions impliquent ch
