新普京网站-澳门新普京 > 前端 > 【新普京网站】框架绘图技术,Winform实现炫酷的透明动画界面

【新普京网站】框架绘图技术,Winform实现炫酷的透明动画界面

2019/12/30 02:00

做过.NET Winform窗体美化的人应有都很熟识UpdateLayeredWindow吧,UpdateLayeredWindow能够达成窗体的妄动透明,效果很好,不会有毛边。然而使用那一个API之后,会有叁个标题就是回天无力接受普通控件,何况还没Paint音信。为掌握除这么些主题素材,有三种方法。

当编辑叁个特出的Windows 窗体程序时,窗体和控件的绘图、效果等操作是没有需求特意加以酌量的。那是为什么吗?因为经过行使 .Net 框架,开辟人士能够拖动豆蔻梢头种类的控件到窗体上,并挥笔一些简便的与事件相关联的代码然后在IDE中按F5,三个完完全全的窗体程序就诞生了!全部控件都将本人绘制本人,窗体只怕控件的轻重和缩放都调节自如。在这里处常常会用到的,且必要引起有些小心的就是控件效果。游戏,自定义图表控件以至显示屏爱护程序的编纂会必要工程师额外撰写用于响应 Paint 事件的代码。

大器晚成、使用双层窗体,底层窗体使用UpdateLayeredWindow作为背景,上层窗体用通常窗体,并且能够接收TransparencyKey或然Region来完毕去除无需的窗体内容,让上层窗体能看出底层的窗体。

  本文针对那二个Windows 窗体开采职员并推动她们在应用程序编制进程中接纳轻松的绘图技巧。首先,大家议和论一些为主的绘图概念。到底哪个人在背负进行绘图操作?Windows 窗体程序是怎么着了然几时该举办绘图的?那一个绘制代码终归被停放在什么地方?之后,还将介绍图像绘制的重复缓冲区技能,你将会看出它是何许专门的学问的,怎么着通过叁个格局来实现缓存和事实上呈现的图像间的更动。最终,大家将会究查”智能无效区域”,实际就是单纯重绘也许撤消应用程序窗体上的不行部分,加速程序的来得和响应速度。希望那一个概念和技能可以指点读者阅读完本文,并且有支持更快和更使得的费用Windows 窗体程序。

二、直接单层窗体,使用控件的DrawToBitmap把控件图像绘制到UpdateLayeredWindow的窗体上,那样就能够看到普通控件了。可是那些也是有毛病:1.控件内容无法自动更新  2.功用低,相当多控件使用DrawToBitmap绘制出的图像缺损,以至绘制不出图像。比方TextBox无法呈现光标,WebBrowser不可能呈现内容。

  Windows 窗体使用GDI+图像引擎,在本文中的全数绘图代码都会涉嫌使用托管的.Net 框架来支配和选拔Windows GDI+图像引擎。

三、选拔DirectUI才能,重写全体根底控件。效果最棒,可是职业量庞大。

  即使本文用于中央的窗体绘图操作,然则它类似提供了便捷的、有效的且拉动抓牢程序质量的技能和艺术。所以,在通读本文从前建议读者对.Net框架有个宗旨的垂询,包罗Windows 窗体育赛事件管理、简单的GDI+对象比如Line,Pen和Brush等。熟习Visual Basic .Net只怕C#编制程序语言。

动用UpdateLayeredWindow时,平时是亟需对Bitmap缓存起来,通过设置剪辑区域,局地重绘来提升效用。另外还是可以异步重绘,模拟Winform的失灵到重绘。

  概念

几人会说为啥不直接用WPF啊,Wpf和Winform各有优弱点,适应不一致的场地。Winform绝对于选用更轻便一些,系统须要更低。当然须要看人的习贯了良专长的。

  Windows 应用程序是自个儿负责绘制的,当贰个窗体”不到头”了,相当于说窗体制改进变了尺寸,只怕部分被别的程序窗体掩瞒,恐怕从最小化状态回涨时,程序都会收下供给绘制的音信。Windows把这种”不到底”状态称为”无效的(Invalidated卡塔尔(قطر‎”状态,大家理解为:供给重绘,当Windows 窗体程序须求重绘窗体时它会从Windows新闻队列中得到绘制的音信。那几个信息经过.Net框架封装然后传递到窗体的 PaintBackground 和 Paint 事件中去,在上述事件中十二分的书写特地用来绘制的代码就可以。

UpdateLayeredWindow 基本选拔方法:

  轻易的绘图示例如下:

重写窗体的 CreateParams 属性

以下是引用片段:
    using System; 
    using System.Drawing; 
    using System.Windows.Forms; 
    public class BasicX : Form 
    { 
    public BasicX() 
    { 
    InitializeComponent(); 
    } 
    private void BasicX_Paint(object sender,   PaintEventArgs e) 
    { 
    Graphics g = e.Graphics; 
    Pen p = new Pen(Color.Red); 
    int width = ClientRectangle.Width; 
    int height= ClientRectangle.Height; 
    g.DrawLine(p, 0,0, width, height); 
    g.DrawLine(p, 0, height, width, 0); 
    p.Dispose(); 
    } 
    private void InitializeComponent() 
    { 
    this.SetStyle(ControlStyles.ResizeRedraw,   true); 
    this.ClientSize = new   System.Drawing.Size(300, 300); 
    this.Text = "BasicX"; 
    this.Paint += new   PaintEventHandler(this.BasicX_Paint); 
    } 
    [System.STAThreadAttribute()] 
    public static void Main() 
    { 
    Application.Run(new BasicX()); 
    } 
    }

protected   override  CreateParams CreateParams
           {
              get
                  {
                 CreateParams cp  =   base .CreateParams;
                 cp.ExStyle  |=   0x00080000 ;  //  WS_EX_LAYERED 扩展样式
                  return  cp;
             }
         }

  上述代码分成几个为主的步调来创制示范程序。首先 InitializeComponent 方法包涵部分属性的安装和叠加窗体 Paint 事件的管理进程。注意,在格局中央调控件的体制也同期被设置,设置控件的体裁也是自定义Windows 窗体及控件行为的风姿罗曼蒂克种有效路子,比如:控件的"ResizeRedraw"属性提示当窗体的大大小小变化发生之后供给对其完全进行重绘,也等于说重绘时连连需求对全数窗体的客商区域开展重绘。窗体的“客商区域”是指除了标题栏和边框的具备窗体区域。能够扩充一个珠璧交辉的考试,撤消该控件的属性然后再运路程序,大家得以很明显的收看为什么该属性会被平时的安装,因为窗体调度大小后的无效区域根本不会被重绘。

API调用:

  好了,大家须要潜心一下BasicX_Paint方法,正如先前所涉嫌的,Paint 事件在程序须求重绘时被激活,程序窗体利用Paint事件来担任回复要求重绘的连串消息,BasicX_Paint方法的调用必要叁个指标sender 和一个Paint伊夫ntArgs类型的变量,PaintEventArgs类的实例或称为变量 e 封装了多少个关键的数据,第二个就是窗体的 Graphics 对象,该目的表示窗体可绘制的外表也称得上画布用于绘制诸如线、文本以致图像等,第2个数据正是ClipRectangle,该Rectangle对象表示窗体上无效的的矩形范围,或许说便是窗体供给重绘的区域。记住,当窗体的ResizeRedDraw设置后,调节大小后该ClipRectangle的大大小小实际就卓殊窗体整个顾客区域的大大小小,恐怕是被别的程序窗体隐讳的那有个别区划区域。关于部分区划区域的用途大家会在智能重绘章节作更详尽的阐述。

public   void  SetBitmap(Bitmap bitmap,  byte  opacity)
    {
     if  (bitmap.PixelFormat  !=  PixelFormat.Format32bppArgb)
         throw   new  ApplicationException( "位图必须是32位包含alpha 通道" );

    IntPtr screenDc  =  Win32.GetDC(IntPtr.Zero);
    IntPtr memDc  =  Win32.CreateCompatibleDC(screenDc);
    IntPtr hBitmap  =  IntPtr.Zero;
    IntPtr oldBitmap  =  IntPtr.Zero;

     try 
         {
        hBitmap  =  bitmap.GetHbitmap(Color.FromArgb( 0 ));   // 创建GDI位图句柄,效率较低
        oldBitmap  =  Win32.SelectObject(memDc, hBitmap);

        Win32.Size size  =   new  Win32.Size(bitmap.Width, bitmap.Height);
        Win32.Point pointSource  =   new  Win32.Point( 0 ,  0 );
        Win32.Point topPos  =   new  Win32.Point(Left, Top);
        Win32.BLENDFUNCTION blend  =   new  Win32.BLENDFUNCTION();
        blend.BlendOp              =  Win32.AC_SRC_OVER;
        blend.BlendFlags           =   0 ;
        blend.SourceConstantAlpha  =  opacity;
        blend.AlphaFormat          =  Win32.AC_SRC_ALPHA;

        Win32.UpdateLayeredWindow(Handle, screenDc,  ref  topPos,  ref  size, memDc,  ref  pointSource,  0 ,  ref  blend, Win32.ULW_ALPHA);
    }
     finally 
         {
        Win32.ReleaseDC(IntPtr.Zero, screenDc);
         if  (hBitmap  !=  IntPtr.Zero)
              {
            Win32.SelectObject(memDc, oldBitmap);

            Win32.DeleteObject(hBitmap);
        }
        Win32.DeleteDC(memDc);
    }
}

  再一次缓冲区绘图本事

API声明:

  双重缓冲区才能能够使程序的绘图特别迅速和坦荡,有效减少绘制时的图像闪烁。该工夫的基本原理是先将图像绘制到内部存款和储蓄器中的一块画布上,风度翩翩旦具备的绘图操作都做到了,再将内部存款和储蓄器中的画布推到窗体的只怕控件的表面将其出示出来。通过这种操作后的次第能使客户认为其更为高效和美丽。

class  Win32
    {
     public   enum  Bool
        {
        False  =   0 ,
        True
    } ;

    [StructLayout(LayoutKind.Sequential)]
     public   struct  Point
         {
         public  Int32 x;
         public  Int32 y;

          public  Point(Int32 x, Int32 y) 
          {  this .x  =  x;  this .y  =  y; }
    }

    [StructLayout(LayoutKind.Sequential)]
     public   struct  Size
         {
         public  Int32 cx;
         public  Int32 cy;

          public  Size(Int32 cx, Int32 cy) 
            {  this .cx  =  cx;  this .cy  =  cy; }
    }

    [StructLayout(LayoutKind.Sequential, Pack  =   1 )]
     struct  ARGB
        {
         public   byte  Blue;
         public   byte  Green;
         public   byte  Red;
         public   byte  Alpha;
    }

    [StructLayout(LayoutKind.Sequential, Pack  =   1 )]
     public   struct  BLENDFUNCTION
         {
         public   byte  BlendOp;
         public   byte  BlendFlags;
         public   byte  SourceConstantAlpha;
         public   byte  AlphaFormat;
    }

     public   const  Int32 ULW_COLORKEY  =   0x00000001 ;
     public   const  Int32 ULW_ALPHA  =   0x00000002 ;
     public   const  Int32 ULW_OPAQUE  =   0x00000004 ;

     public   const   byte  AC_SRC_OVER  =   0x00 ;
     public   const   byte  AC_SRC_ALPHA  =   0x01 ;

    [DllImport( " user32.dll " , ExactSpelling  =   true , SetLastError  =   true )]
     public   static   extern  Bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,  ref  Point pptDst,  ref  Size psize, IntPtr hdcSrc,  ref  Point pprSrc, Int32 crKey,  ref  BLENDFUNCTION pblend, Int32 dwFlags);

    [DllImport( " user32.dll " , ExactSpelling  =   true , SetLastError  =   true )]
     public   static   extern  IntPtr GetDC(IntPtr hWnd);

    [DllImport( " user32.dll " , ExactSpelling  =   true )]
     public   static   extern   int  ReleaseDC(IntPtr hWnd, IntPtr hDC);

    [DllImport( " gdi32.dll " , ExactSpelling  =   true , SetLastError  =   true )]
     public   static   extern  IntPtr CreateCompatibleDC(IntPtr hDC);

    [DllImport( " gdi32.dll " , ExactSpelling  =   true , SetLastError  =   true )]
     public   static   extern  Bool DeleteDC(IntPtr hdc);

    [DllImport( " gdi32.dll " , ExactSpelling  =   true )]
     public   static   extern  IntPtr SelectObject(IntPtr hDC, IntPtr hObject);

    [DllImport( " gdi32.dll " , ExactSpelling  =   true , SetLastError  =   true )]
     public   static   extern  Bool DeleteObject(IntPtr hObject);

    [DllImport( " user32.dll " , EntryPoint  =   " SendMessage " )]
     public   static   extern   int  SendMessage( int  hWnd,  int  wMsg,  int  wParam,  int  lParam);
    [DllImport( " user32.dll " , EntryPoint  =   " ReleaseCapture " )]

     public   static   extern   int  ReleaseCapture();
     public   const   int  WM_SysCommand  =   0x0112 ;
     public   const   int  SC_MOVE  =   0xF012 ;

     public   const   int  SC_MAXIMIZE  =   61488 ;
     public   const   int  SC_MINIMIZE  =   61472 ;
}

  下边提供的亲自过问程序能够表明双重缓冲区的定义和促成方式,那么些示例所蕴藏的机能已十三分完整,且完全可以在其实使用中应用。在该章节后边还有大概会聊到该工夫应该合营控件的有的属性设置本事达到规定的规范更加好的作用。

内需表现图像的时候调用 SetBitmap 方法。只要优化好,显示功效比普通的Paint重绘方式高超级多,并且不卡不闪烁,扶持任性透明。

  要想清楚双重缓冲区绘图技能所端来的好处就请运营SpiderWeb示例程序吗。程序运行并运转后对窗口大小实行调节,你会意识使用这种绘图算法的功用不高,並且在调节大小的历程中有雅量的闪亮现身。

上边是慈祥开辟出来的效应:

  不持有双重缓冲区工夫的SpiderWeb示例程序

新普京网站 1

  纵观程序的源码你会发现在前后相继Paint事件激活后是经过调用LineDrawRoutine方法来促成线的绘图的。LineDrawRoutine方法有四个参数,第一个是Graphics对象是用于绘制线条的地点,第4个是绘图工具Pen对象用来画线条。代码特别轻巧,二个循环语句,LINEFREQ常量等,程序从窗体表面包车型大巴左下平素划线到其右上。请当心,程序采用浮点数来测算在窗体上的绘图地点,那样做的益处正是当窗体的大小爆发变化时地方数据会更为标准。

新普京网站 2

以下是引用片段:
    private void LineDrawRoutine(Graphics g,   Pen p) 
    { 
    float width = ClientRectangle.Width; 
    float height = ClientRectangle.Height; 
    float xDelta = width / LINEFREQ; 
    float yDelta = height / LINEFREQ; 
    for (int i = 0; i < LINEFREQ; i++) 
    { 
    g.DrawLine(p, 0, height - (yDelta * i),   xDelta * i, 0); 
    } 
    }

新普京网站 3

撰写很简短的用于响应Paint事件SpiderWeb_Paint的代码,正如前方所提到的,Graphics对象就是从Paint事件参数PaintEventArgs对象中领到出来的代表窗体的绘图表面。这么些Graphics对象及其新创制Pen对象一同传递给LineDrawRoutine方法来画出蜘蛛网似的线条,使用完Graphics对象和Pen对象后刑释其占用的能源,那么万事绘制操作就完结了。

那个是用OpenGL绘制的

以下是引用片段:
    private void SpiderWeb_Paint(object sender,   PaintEventArgs e) 
    { 
    Graphics g = e.Graphics; 
    Pen redPen = new Pen(Color.Red); 
    LineDrawRoutine(g, redPen); 
    redPen.Dispose(); 
    g.Dispose(); 
    }

新普京网站 4

  那么毕竟作如何的退换手艺使地点的SpiderWeb程序达成简单的再度缓冲区技艺呢?原理其实一定简单,正是将相应画到窗体表面包车型大巴绘图操作改成先画到内部存款和储蓄器中的位图上,LineDrawRoutine向那一个在内部存款和储蓄器中暗藏的画布执行同生龙活虎的蜘蛛网绘制操作,等到绘制完成再通过调用Graphics.DrawImage方法将藏匿的画布上内容推到窗体表面来突显出来,最终,再加上有的小的变动三个高品质的绘图窗体程序就成功了。

推荐介绍大器晚成款C#界面库:DSkin界面库(Winform平台第八个DirectUI分界面库卡塔尔 **http://d.cskin.net**

  请比较下边双重缓冲区绘图事件与眼下介绍的简约绘图事件间的区分:

还会有一个也是自己付出的无偿分界面库LayeredSkin  也足以兑现无数功用

以下是引用片段:
    private void SpiderWeb_DblBuff_Paint(object   sender, PaintEventArgs e) 
    { 
    Graphics g = e.Graphics; 
    Pen bluePen = new Pen(Color.Blue); 
    Bitmap localBitmap = new   Bitmap(ClientRectangle.Width,ClientRectangle.Height); 
    Graphics bitmapGraphics =   Graphics.FromImage(localBitmap); 
    LineDrawRoutine(bitmapGraphics, bluePen); 
    //把在内存里处理的bitmap推向前台并显示 
    g.DrawImage(localBitmap, 0, 0); 
    bitmapGraphics.Dispose(); 
    bluePen.Dispose(); 
    localBitmap.Dispose(); 
    g.Dispose(); 
    }

Winform也足以很璀璨的!

  上面包车型地铁示范代码创制了内部存款和储蓄器位图对象,它的尺寸也就是窗体的客户区域(便是绘图表面卡塔尔的深浅,通过调用Graphics.FromImage将内部存款和储蓄器中位图的引用传递给Graphics对象,也正是说前面全部对该Graphics对象的操作实际都以对内部存款和储蓄器中的位图进行操作的,该操作在C++中等同于将位图对象的指针复制给Graphics对象,八个指标使用的是均等块内部存储器地址。以往Graphics对象表示的是显示屏后方的一块画布,而它在再一次缓冲区技巧中起到重要的功效。全体的线条绘制操作都早已针对于内部存款和储蓄器中的位图对象,下一步就通过调用DrawImage方法将该位图复制到窗体,蜘蛛网的线条就能够及时展现在窗体的绘图表面而且丝毫并未有闪烁现身。

  那生机勃勃体系的操作完成后还不是专程实用,因为我们原先关系了,控件的样式也是概念Windows 窗体程序行为的一条路线,为了更加好的兑现再一次缓冲区必得安装控件的Opaque属性,那几个本性指明窗体是不担任在后台绘制本人的,换句话说,假如这脾个性设置了,那么必须为废除和重绘操作增添相关的代码。具有双重缓冲区版本的SpiderWeb程序通过以上的安装在每二次索要重绘时都表现优良,窗体表面用其和睦的背景观进行割除,那样就一发减少了闪烁的面世。

以下是引用片段:
    public SpiderWeb_DblBuff() 
    { 
    SetStyle(ControlStyles.ResizeRedraw |   ControlStyles.Opaque, true); 
    } 
    private void SpiderWeb_DblBuff_Paint(object   sender, PaintEventArgs e) 
    { 
    Bitmap localBitmap = new   Bitmap(ClientRectangle.Width, 
    ClientRectangle.Height); 
    Graphics bitmapGraphics =   Graphics.FromImage(localBitmap); 
    bitmapGraphics.Clear(BackColor); 
    LineDrawRoutine(bitmapGraphics, bluePen); 
    }

  结果怎么着?图像的绘图平滑多了。从内部存款和储蓄器团长蜘蛛网的线条推到前台以突显出来是完全未有闪烁的,然则我们依旧略略停顿一下,先将内部存款和储蓄器中的位图修整一下再显示出来,能够拉长后生可畏行代码以便使线条看起来更为平整。

上一篇:国外程序员整理的,框架大全 下一篇:中三种正则表达式比较