http://hi.baidu.com/3582077/item/77d3c1ff60f9fa5ec9f33754

调用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);

当然上面是最简单的,还有一些细节没有讲,先就这样吧,会基本使用就行了,那些细节问题以后再详细说明。

GetWindowDC函数可以参考:http://hi.baidu.com/3582077/blog/item/14bb7201f559eb187bec2cab.html第八十六个参数。

在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

消息机制参考http://hi.baidu.com/3582077/blog/item/6a6c2c0990627fda3bc763a2.html里的CreateWindow函数

看一个示例:

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函数,可以判断一个点是否在一个矩形区域内。

http://blog.csdn.net/jiangxinyu/article/details/8097759

调用API函数,在窗口非客户区绘图(通过GetWindowDC获得整个窗口的DC,就可以随意作画了)的更多相关文章

  1. VC----SDK下对窗口非客户区的操作

    窗口分成两大部分:客户区和非客户区.非客户区再次细分:标题栏,如图片中顶部深蓝色:左边框,如图片中红色部分:上边框,如图片中绿色部分:右边框,如图片中右侧天蓝色部分:底边框,如图片中下面棕色部分. 之 ...

  2. C#调用API函数EnumWindows枚举窗口的方法

    原文 http://blog.csdn.net/dengta_snowwhite/article/details/6067928 与C++不同,C#调用API函数需要引入.dll文件,步骤如下: 1. ...

  3. C# 绘制窗体客户非客户区要用WM_PAINT和WM_NCPAINT

    窗体分为两部分:客户区(Client area)和非客户区(Non-Client area) WM_PAINT消息.OnPaint()方法.GetDC()API函数都是处理窗体客户区绘制的   而标题 ...

  4. Delphi - Windows系统下,Delphi调用API函数和7z.dll动态库,自动把文件压缩成.tar.gz格式的文件

    项目背景 应欧美客户需求,需要将文件压缩成.tar.gz格式的文件,并上传给客户端SFTP服务器. 你懂的,7-Zip软件的显著特点是文件越大压缩比越高,在Linux系统上相当于我们Windows系统 ...

  5. Delphi调用API函数获取Windows目录信息、获取System目录信息、获取Temp临时文件目录信息

    var Str1, Str2: Array[..Max_Path]of Char;//开辟缓冲区 Str3: Array[..]of Char; begin GetWindowsDirectory(@ ...

  6. 调用API函数减少c#内存占用(20+m减至1m以下)

    原文:调用API函数减少c#内存占用(20+m减至1m以下) c#虽然内置垃圾回收机制,但是并不能解决程序占用内存庞大的问题,如果我们仔细观察任务管理器,我们会发现一个程序如果最小化的时候,它所占用的 ...

  7. windows 屏幕坐标 窗口坐标 客户区坐标 逻辑坐标 设备坐标之间的关系及转换

    设置坐标映射    (1)Windows坐标系统 Windows坐标系分为逻辑坐标系和设备坐标系两种,GDI支持这两种坐标系.一般而言, GDI的文本和图形输出函数使用逻辑坐标,而在客户区移动或按下鼠 ...

  8. 实现:调用API函数ShowWindow()来隐藏窗口

    只需要将相应代码复制即可. 代码如下: #include <iostream> #include <windows.h> int main() { HWND hDos; //声 ...

  9. 老话题:自己编写只截窗口客户区的截屏软件(VB2010)

    现在能实现截屏的软件很多,就不一一列举了,连WIN7都自带截屏软件,甚至OFFICE2010开始都有截屏的功能. 截屏软件虽多,无外乎三种截屏方式:全屏截图.窗口截图.自定义矩形截图. 其中,窗口截图 ...

随机推荐

  1. Linux 基本命令(持续更新ing)

    cd -> 变换路径                        //文件一般存在/var/路径下,var为可修改存储盘 ls -> 列出所有隐藏文件与相关文件的属性   #ls -al ...

  2. C#中的转义字符

    一些常用的转义字符: \n  换行 \b  backspace,删除光标前面的一个字符 \t  tab键 由多个空格组成的一个字符,具有行与行之间的对齐功能 \\  \ 如果在字符串前面加@的话: 1 ...

  3. Break和Continue的一些注意事项

    Break: 1)可以用于switch-case判断中,用于跳出switch 2)用在循环中,用于立即跳出循环 其中,用于循环的情况下,跳出的是break所在的循环 Continue: 立即结束本次循 ...

  4. 修改Calendar(梅花雨)日历控件 兼容IE9 谷歌 火狐

    修改Calendar日历控件 兼容IE9,谷歌,火狐. 只是能用,出现的位置有所不同,希望有高手再帮我改改吧,谢谢   一. [javascript]   this.iframe     = wind ...

  5. asp.net微信开发第四篇----已关注用户管理

    公众号可通过本接口来获取帐号的关注者列表,关注者列表由一串OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的)组成.一次拉取调用最多拉取10000个关注者的OpenID,可以通过 ...

  6. C# Wpf异步修改UI,多线程修改UI(二)

    1.使用定时器异步修改 这是相对比较简单的方法 在Wpf中定时器使用DiapatcherTimer,不使用Timer原因: 在一个应用程序中,Timer会重复生成time事件,而DispatcherT ...

  7. linux command cp.

    Examples cp file1.txt newdir Copies the file1.txt in the current directory to the newdir subdirector ...

  8. Java方法的参数是按值传递的.【转】

    在Java中,所有的方法参数,都是"按值传递". 有那么一种说法,Java中基本类型是按值传递,对象是按引用传递.这个说法其实是不确切的,确切的说法是 Java中基本类型将值作为参 ...

  9. Python局部变量和全局变量global

    当你在函数定义声明变量的时候,它们与函数外具有相同名称的其它变量没有任何关系,即变量名称对于函数来说是 局部  的.这称为变量的 作用域 .所有变量的作用域是它们被定义的块,从它们的名称被定义的那点开 ...

  10. 小试牛刀-嘿嘿,创建job了

    今天 周六,我写了这个存储过程.用意:检查 数据库中是否有 该类的 job,如果有那么取job_name  赋值给 job_old,    把job_old加上时间戳 改造成 job_new.那么jo ...