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


    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

  • 相关阅读:
    (转)SpringMVC学习总结
    Golang-函数的defer
    Golang-闭包
    Golang-匿名函数
    Golang-init()
    Golang-递归
    Golang-函数、包、变量的作用域
    Golang-for、break、continue、goto、return
    Golang-程序流程控制 if、switch
    Golang-进制、源码反码补码、位运算
  • 原文地址:https://www.cnblogs.com/findumars/p/5811407.html
Copyright © 2020-2023  润新知