最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Winform?控件優(yōu)化LayeredWindow無鋸齒圓角窗體

 更新時(shí)間:2022年09月01日 14:02:14   作者:代碼迷途  
這篇文章主要為大家介紹了Winform?控件優(yōu)化LayeredWindow實(shí)現(xiàn)無鋸齒圓角窗體示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

在一般能搜到的所有實(shí)現(xiàn)圓角窗體的示例中,都是通過繪制圓角的路徑,并創(chuàng)建對(duì)應(yīng)的窗體Region區(qū)域?qū)崿F(xiàn)。

目前所知,重新創(chuàng)建Region的所有方法,產(chǎn)生的Region都是有鋸齒的【估計(jì)要通過消除鋸齒的算法額外處理才可能解決】,也就是說,幾乎所有圓角窗體的示例都是有鋸齒的,其效果幾乎不能看,慘不忍睹。

后面看到Creating Smooth Rounded Corners in WinForm Applications介紹了繪制無鋸齒的光滑圓角窗體的實(shí)現(xiàn)。了解了下,總體非常不錯(cuò),因此對(duì)其進(jìn)行借鑒并進(jìn)行了修改。

根據(jù)后續(xù)的了解,其實(shí)現(xiàn)原理是通過LayeredWindow進(jìn)行繪制(CreateParams樣式要添加CreateParams.ExStyle |= 0x00080000),而其理論上,可以在Layered Window上繪制更復(fù)雜的(任意形狀)圖形,并且沒有閃爍、邊界鋸齒的問題。

關(guān)于Layered Windows(分層窗體)

Layered Windows:使用一個(gè)分層窗口可以顯著提高復(fù)雜形狀、動(dòng)畫特效、透明通道混合效果等類型的窗體的性能和視覺效果。

系統(tǒng)自動(dòng)構(gòu)造和繪制分層的窗口以及基礎(chǔ)應(yīng)用的窗體,分層窗體光滑流暢的渲染、沒有復(fù)雜窗體區(qū)域的典型閃爍,同時(shí)支持半透明。

在窗體創(chuàng)建后通過調(diào)用CreateWindowExSetWindowLong函數(shù)指定WS_EX_LAYERED額外窗口樣式,然后通過調(diào)用 SetLayeredWindowAttributesUpdateLayeredWindow 使分層窗口可見。

幾個(gè)關(guān)于LayeredWindow的資料:

用UpdateLayeredWindow實(shí)現(xiàn)任意異形窗口

UpdateLayeredWindowIndirect function

這就是引用的stackoverflow中無鋸齒圓角窗體的實(shí)現(xiàn)的基本原理。

關(guān)于同樣的實(shí)現(xiàn)使用Layered Windows與使用透明窗體的區(qū)別

不使用Layered Windows時(shí),如果在設(shè)置窗體Form透明的情況下,在OnPaint中繪制,無論執(zhí)行或不執(zhí)行SetBitmap()設(shè)置透明通道,在圓角邊緣處都會(huì)有白邊出現(xiàn)。

// 設(shè)置窗體透明
this.BackColor = Color.Empty;
this.TransparencyKey = BackColor;

注意:繼承窗體,在設(shè)計(jì)器中

Control.DrawToBitmap()將控件繪制到Bitmap

啟用分層窗體后,原本的窗體將會(huì)隱藏,因此需要在Layered Windows上進(jìn)行新形狀(如圓角)窗體的繪制,才會(huì)顯示看到,結(jié)合透明混合通道的處理,實(shí)現(xiàn)正確顯示繪制的圖形窗體和無鋸齒的效果。

顯示繪制的分層窗體后,會(huì)同樣覆蓋原窗體上控件,導(dǎo)致只有一個(gè)窗體,不會(huì)顯示內(nèi)部的控件。

因此,除了繪制分層窗體圖形,還需要將原窗體的控件繪制上去。

Control的DrawToBitmap方法:Control.DrawToBitmap(bitmap, Rectangle targetBounds),用于將控件的targetBounds范圍繪制到bitmap,通常指定控件的ClientRectangle,將控件整體繪制到bitmap。

然后,繪圖對(duì)象將控件的bitmap繪制到圖像的指定位置(對(duì)應(yīng)于原空間位置)。

ctrl.DrawToBitmap(bmp, ctrl.ClientRectangle); 
graphics.DrawImage(bmp, ctrl.Location);

這樣可以實(shí)現(xiàn)控件繪制到圖像上,其顯示的渲染效果和使用,與正??丶]有任何區(qū)別。

注意在OnPaint方法中,對(duì)背景色進(jìn)行與分層窗體相同的繪制。

最終效果

下面是稍微修改后,通過繼承窗體,在設(shè)計(jì)器和生成結(jié)果中,不同的顯示效果

public class RoundedFormTest : RoundedForm
{
    // 其他相關(guān)代碼
}

幾個(gè)小問題

StartPosition設(shè)置窗體初始位置設(shè)置無效

不知為何,設(shè)置窗體初始位置無效StartPosition,具體原因未知。

比如,直接使用StartPosition = FormStartPosition.CenterScreen并不能設(shè)置窗體居中

public RoundedForm()
{
    this.FormBorderStyle = FormBorderStyle.None;
    // 居中無效
    //StartPosition = FormStartPosition.CenterScreen;
}

構(gòu)造函數(shù)中設(shè)置Location位置無效

Location位置的設(shè)置應(yīng)該放在窗體的Load事件方法中,提前設(shè)置并無效果。

public RoundedForm()
{
    this.FormBorderStyle = FormBorderStyle.None;
    // 構(gòu)造函數(shù)中設(shè)置Location無效
    // StartPosition = FormStartPosition.Manual;
    // Location = new Point(200, 200);
    // 無效
    //Left = 200;
    //Top = 200; 
}

繼承窗體RoundedFormTest的Load事件處理程序:

 private void RoundedFormTest_Load(object sender, EventArgs e)
{
    // 有效
    Location = new Point(300, 300);
}

在設(shè)計(jì)器中,右鍵窗體無法顯示菜單

這也是一個(gè)很奇怪的問題,原因不知。只能右鍵窗體以外的部分來顯示菜單,查看屬性或代碼等。

代碼實(shí)現(xiàn)

修改部分

  • 添加了設(shè)置背景顏色的屬性、背景漸變方向的屬性、是否可以調(diào)整窗體大小的屬性
[Category("高級(jí)"), DefaultValue(true), Description("窗體是否固定大小,為true時(shí),無法拖動(dòng)邊角調(diào)整窗體大小,默認(rèn)true")]
public bool FixedSize { get; set; } = true;
[Category("高級(jí)"), DefaultValue(typeof(Color), "DarkSlateBlue"), Description("漸變背景開始的顏色,如果BgStartColor和BgEndColor顏色一樣,則無漸變")]
public Color BgStartColor
{
   get => bgStartColor; set
   {
       bgStartColor = value;
       Validate();
   }
}
[Category("高級(jí)"), DefaultValue(typeof(Color), "MediumPurple"), Description("漸變背景結(jié)束的顏色,如果BgStartColor和BgEndColor顏色一樣,則無漸變")]
public Color BgEndColor
{
   get => bgEndColor; set
   {
       bgEndColor = value;
       Validate();
   }
}
[Category("高級(jí)"), DefaultValue(0f), Description("背景顏色的漸變方向,默認(rèn)0度,水平方向漸變")]
public float LinearGradient
{
   get => linearGradient; set
   {
       linearGradient = value;
       Validate();
   }
}
  • 去除了原本通過計(jì)時(shí)器定時(shí)執(zhí)行Layered Windows繪制的實(shí)現(xiàn),改為在OnResize、OnShown方法中繪制

原本的代碼在Load加載后,通過定時(shí)器定時(shí)執(zhí)行Layered Windows的繪制,感覺這樣實(shí)現(xiàn)太費(fèi)性能,且不高效。改為將其繪制放在需要繪制的OnResize、OnShown方法中。

  • 添加拖動(dòng)窗體、創(chuàng)拽調(diào)整窗體大小的代碼

正常的窗體都應(yīng)該支持拖動(dòng)窗體,拖拽調(diào)整窗體大小、標(biāo)題欄等基本功能,此處只添加之前介紹過的拖動(dòng)和拖拽。其他可根據(jù)需要再行修改。

  • 添加RoundRadius屬性,圓角半徑可以根據(jù)需要指定和修改。默認(rèn)圓角大小為35。
[CategoryAttribute("高級(jí)"), DefaultValue(35), Description("圓角半徑的大小")]
public int RoundRadius
{
   set
   {
       roundRadius = value;
       this.Invalidate();
   }
   get
   {
       return roundRadius;
   }
}

全部代碼

全部的代碼200多行,可根據(jù)需要進(jìn)行精簡(jiǎn)。

 public class RoundedForm : Form
 {
     private Color bgStartColor = Color.DarkSlateBlue;
     private Color bgEndColor = Color.MediumPurple;
     private float linearGradient;
     private int roundRadius;//圓角半徑
     // 上面文章列出的屬性代碼,此處不再重復(fù)
     public RoundedForm()
     {
         this.FormBorderStyle = FormBorderStyle.None;
         roundRadius = 35;
     }
     // OnResize、OnShown后繪制Layered Windows
     protected override void OnResize(EventArgs e)
     {
         DrawRoundForm();
         base.OnResize(e);
     }
     protected override void OnShown(EventArgs e)
     {
         DrawRoundForm();
         base.OnShown(e);
     }
     private void DrawRoundForm()
     {
         if (DesignMode) return;
         if (ClientRectangle.Width == 0 || ClientRectangle.Height == 0)
         {
             return;
         }
         using (Bitmap backImage = new Bitmap(this.Width, this.Height))
         {
             using (Graphics graphics = Graphics.FromImage(backImage))
             {
                 Rectangle gradientRectangle = ClientRectangle;
                 using (Brush b = new LinearGradientBrush(gradientRectangle, BgStartColor, BgEndColor, LinearGradient))
                 {
                     graphics.FillRoundRectangle(gradientRectangle, b, 35);
                     foreach (Control ctrl in this.Controls)
                     {
                         using (Bitmap bmp = new Bitmap(ctrl.Width, ctrl.Height, PixelFormat.Format32bppArgb))
                         {
                             ctrl.DrawToBitmap(bmp, ctrl.ClientRectangle); // 結(jié)合OnPaint中的繪制,能完美實(shí)現(xiàn)ctrl圓角的邊角透明底層,原因(猜測(cè)可能是)Bitmap沒有指定顏色,控件之外的部分透明
                             graphics.DrawImage(bmp, ctrl.Location);
                         }
                     }
                     PerPixelAlphaBlend.SetBitmap(backImage, Left, Top, Handle);//不執(zhí)行將無法顯示窗體
                 }
             }
         }
     }
     protected override void OnPaint(PaintEventArgs e)
     {
         base.OnPaint(e);
         if (ClientRectangle.Width == 0 || ClientRectangle.Height == 0)
         {
             return;
         }
         using (Graphics graphics = e.Graphics)
         {
             //Rectangle gradientRectangle = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
             Rectangle gradientRectangle = ClientRectangle;
             using (Brush b = new LinearGradientBrush(gradientRectangle, BgStartColor, BgEndColor, LinearGradient))
             {
                 graphics.FillRoundRectangle(gradientRectangle, b, 35);
             };
         }
     }
     protected override CreateParams CreateParams
     {
         get
         {
             CreateParams cp = base.CreateParams;
             if (!DesignMode) 
                 cp.ExStyle |= 0x00080000;  // Form 添加 WS_EX_LAYERED 擴(kuò)展樣式 
             return cp;
         }
     }
     // 通過重寫 WndProc 實(shí)現(xiàn)拖拽調(diào)整窗體大小、拖拽移動(dòng)窗體
     const int HTLEFT = 10;
     const int HTRIGHT = 11;
     const int HTTOP = 12;
     const int HTTOPLEFT = 13;
     const int HTTOPRIGHT = 14;
     const int HTBOTTOM = 15;
     const int HTBOTTOMLEFT = 0x10;
     const int HTBOTTOMRIGHT = 17;
     protected override void WndProc(ref Message m)
     {
         base.WndProc(ref m);
         if (m.Msg == 0x84)
         {
             if (!FixedSize)
             {
                 // 拖拽調(diào)整窗體大小
                 Point vPoint = new Point((int)m.LParam & 0xFFFF, (int)m.LParam >> 16 & 0xFFFF);
                 vPoint = PointToClient(vPoint);
                 if (vPoint.X <= 5)
                     if (vPoint.Y <= 5)
                         m.Result = (IntPtr)HTTOPLEFT;
                     else if (vPoint.Y >= ClientSize.Height - 5)
                         m.Result = (IntPtr)HTBOTTOMLEFT;
                     else m.Result = (IntPtr)HTLEFT;
                 else if (vPoint.X >= ClientSize.Width - 5)
                     if (vPoint.Y <= 5)
                         m.Result = (IntPtr)HTTOPRIGHT;
                     else if (vPoint.Y >= ClientSize.Height - 5)
                         m.Result = (IntPtr)HTBOTTOMRIGHT;
                     else m.Result = (IntPtr)HTRIGHT;
                 else if (vPoint.Y <= 5)
                     m.Result = (IntPtr)HTTOP;
                 else if (vPoint.Y >= ClientSize.Height - 5)
                     m.Result = (IntPtr)HTBOTTOM;
             }
             // 鼠標(biāo)左鍵按下實(shí)現(xiàn)拖動(dòng)窗口功能
             if (m.Result.ToInt32() == 1)
             {
                 m.Result = new IntPtr(2);
             }
         }
     }
 }
 public static class PerPixelAlphaBlend
 {
     public static void SetBitmap(Bitmap bitmap, int left, int top, IntPtr handle)
     {
         SetBitmap(bitmap, 255, left, top, handle);
     }
     public static void SetBitmap(Bitmap bitmap, byte opacity, int left, int top, IntPtr handle)
     {
         if (bitmap.PixelFormat != PixelFormat.Format32bppArgb)
             throw new ApplicationException("The bitmap must be 32ppp with alpha-channel.");
         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));
             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);
         }
     }
 }
 internal 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);
 }

以上就是Winform 控件優(yōu)化LayeredWindow無鋸齒圓角窗體的詳細(xì)內(nèi)容,更多關(guān)于LayeredWindow無鋸齒圓角窗體的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C# 引入委托的目的是什么

    C# 引入委托的目的是什么

    這篇文章主要介紹了C# 引入委托的目的是什么,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • Unity 使用TexturePacker打包圖集的操作方法

    Unity 使用TexturePacker打包圖集的操作方法

    這篇文章主要介紹了Unity 使用TexturePacker打包圖集的操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08
  • C#實(shí)現(xiàn)圍棋游戲

    C#實(shí)現(xiàn)圍棋游戲

    這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)圍棋游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • c#訪問this關(guān)鍵字和base關(guān)鍵字示例

    c#訪問this關(guān)鍵字和base關(guān)鍵字示例

    this關(guān)鍵字引用類的當(dāng)前實(shí)例。靜態(tài)成員方法中不能使用this關(guān)鍵字,this關(guān)鍵字只能在實(shí)例構(gòu)造函數(shù)、實(shí)例方法或?qū)嵗L問器中使用。base關(guān)鍵字用于從派生類中訪問基類的成員。下面學(xué)習(xí)一下這二個(gè)關(guān)鍵字的使用方法
    2014-01-01
  • C#實(shí)現(xiàn)日期格式轉(zhuǎn)換的公共方法類實(shí)例

    C#實(shí)現(xiàn)日期格式轉(zhuǎn)換的公共方法類實(shí)例

    這篇文章主要介紹了C#實(shí)現(xiàn)日期格式轉(zhuǎn)換的公共方法類,結(jié)合完整實(shí)例形式分析了C#針對(duì)各種常見日期格式的轉(zhuǎn)換方法,涉及C#字符串、日期、時(shí)間相關(guān)操作技巧,需要的朋友可以參考下
    2017-01-01
  • 輕松學(xué)習(xí)C#的密封類

    輕松學(xué)習(xí)C#的密封類

    輕松學(xué)習(xí)C#的密封類,對(duì)C#的密封類感興趣的朋友可以參考本篇文章,幫助大家更靈活的運(yùn)用C#的密封類
    2015-11-11
  • WPF+SkiaSharp實(shí)現(xiàn)自繪投籃小游戲

    WPF+SkiaSharp實(shí)現(xiàn)自繪投籃小游戲

    這篇文章主要介紹了如何利用WPF+SkiaSharp實(shí)現(xiàn)自繪投籃小游戲。此案例主要是針對(duì)光線投影法碰撞檢測(cè)功能的示例,順便做成了一個(gè)小游戲,很簡(jiǎn)單,但是,效果卻很不錯(cuò),感興趣的可以動(dòng)手嘗試一下
    2022-08-08
  • c# 實(shí)現(xiàn)簡(jiǎn)單的串口通訊

    c# 實(shí)現(xiàn)簡(jiǎn)單的串口通訊

    這篇文章主要介紹了c# 如何實(shí)現(xiàn)簡(jiǎn)單的串口通訊,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下
    2021-03-03
  • C#異常處理知識(shí)匯總

    C#異常處理知識(shí)匯總

    這篇文章主要介紹了C#異常處理的知識(shí),文中講解非常詳細(xì),配合代碼幫助大家更好的理解,感興趣的朋友可以參考下
    2020-06-06
  • 再議C#中的裝箱與拆箱的問題詳解

    再議C#中的裝箱與拆箱的問題詳解

    本篇文章再次介紹了C#中的裝箱與拆箱,這次們看下使用泛型和不使用泛型引發(fā)裝箱拆箱的情況
    2013-05-05

最新評(píng)論

峨边| 都昌县| 南通市| 界首市| 普定县| 绥江县| 德令哈市| 射阳县| 遂川县| 烟台市| 仙居县| 绥棱县| 江源县| 五家渠市| 广州市| 海林市| 司法| 县级市| 德令哈市| 罗源县| 渝北区| 葵青区| 栖霞市| 兴山县| 天祝| 缙云县| 新巴尔虎左旗| 宜春市| 奉化市| 宜黄县| 木兰县| 梅河口市| 铜陵市| 白水县| 墨竹工卡县| 常宁市| 留坝县| 汨罗市| 德化县| 延津县| 满洲里市|