主頁 > .NET開發 > 淺談Winform控制元件開發(一):使用GDI+美化基礎視窗

淺談Winform控制元件開發(一):使用GDI+美化基礎視窗

2021-02-06 06:06:38 .NET開發

  •  寫在前面:
      • 本系列隨筆將作為我對于winform控制元件開發的心得總結,方便對一些讀者在GDI+、winform等技術方面進行一個入門級的講解,拋磚引玉,
      • 別問為什么不用WPF,為什么不用QT,問就是懶,不想學,
      • 本專案所有代碼均開源在https://github.com/muxiang/PowerControl
      • 效果預覽:(gif,3.4MB)

  • 本系列第一篇內容將僅包含對于Winform基礎視窗也就是System.Windows.Forms.Form的美化,后續將對一些常用控制元件如Button、ComboBox、CheckBox、TextBox等進行修改,并提供一些其他如Loading遮罩層等常見控制元件,
  • 對于基礎視窗的美化,首要的任務就是先把基礎標題欄干掉,這個程序中會涉及一些Windows訊息機制,
  • 首先,我們新建一個類XForm,派生自System.Windows.Forms.Form,
    1 /// <summary>
    2 /// 表示組成應用程式的用戶界面的視窗或對話框,
    3 /// </summary>
    4 [ToolboxItem(false)]
    5 public class XForm : Form
    6 ...

     隨后,我們定義一些常量

     1 /// <summary>
     2 /// 標題欄高度
     3 /// </summary>
     4 public const int TitleBarHeight = 30;
     5 
     6 // 邊框寬度
     7 private const int BorderWidth = 4;
     8 // 標題欄圖示大小
     9 private const int IconSize = 16;
    10 // 標題欄按鈕大小
    11 private const int ButtonWidth = 30;
    12 private const int ButtonHeight = 30;

    覆寫基類屬性FormBorderStyle使base.FormBorderStyle保持None,覆寫基類屬性Padding回傳或設定正確的內邊距

     1 /// <summary>
     2 /// 獲取或設定表單的邊框樣式,
     3 /// </summary>
     4 [Browsable(true)]
     5 [Category("Appearance")]
     6 [Description("獲取或設定表單的邊框樣式,")]
     7 [DefaultValue(FormBorderStyle.Sizable)]
     8 public new FormBorderStyle FormBorderStyle
     9 {
    10     get => _formBorderStyle;
    11     set
    12     {
    13         _formBorderStyle = value;
    14         UpdateStyles();
    15         DrawTitleBar();
    16     }
    17 }
    18 
    19 /// <summary>
    20 /// 獲取或設定表單的內邊距,
    21 /// </summary>
    22 [Browsable(true)]
    23 [Category("Appearance")]
    24 [Description("獲取或設定表單的內邊距,")]
    25 public new Padding Padding
    26 {
    27     get => new Padding(base.Padding.Left, base.Padding.Top, base.Padding.Right, base.Padding.Bottom - TitleBarHeight);
    28     set => base.Padding = new Padding(value.Left, value.Top, value.Right, value.Bottom + TitleBarHeight);
    29 }

    ※最后一步也是最關鍵的一步:重新定義視窗客戶區邊界,重寫WndProc并處理WM_NCCALCSIZE訊息,

     1 protected override void WndProc(ref Message m)
     2 {
     3     switch (m.Msg)
     4     {
     5             case WM_NCCALCSIZE:
     6             {
     7                 // 自定義客戶區
     8                 if (m.WParam != IntPtr.Zero && _formBorderStyle != FormBorderStyle.None)
     9                 {
    10                     NCCALCSIZE_PARAMS @params = (NCCALCSIZE_PARAMS)
    11                         Marshal.PtrToStructure(m.LParam, typeof(NCCALCSIZE_PARAMS));
    12                     @params.rgrc[0].Top += TitleBarHeight;
    13                     @params.rgrc[0].Bottom += TitleBarHeight;
    14                     Marshal.StructureToPtr(@params, m.LParam, false);
    15                     m.Result = (IntPtr)(WVR_ALIGNTOP | WVR_ALIGNBOTTOM | WVR_REDRAW);
    16                 }
    17 
    18                 base.WndProc(ref m);
    19                 break;
    20             }
    21 ……
    相關常量以及P/Invoke相關方法已在我的庫中定義,詳見MSDN,也可從http://pinvoke.net/查詢,
    同樣在WndProc中處理WM_NCPAINT訊息
    1 case WM_NCPAINT:
    2 {
    3     DrawTitleBar();
    4     m.Result = (IntPtr)1;
    5     break;
    6 }

     DrawTitleBar()方法定義如下:

     1 /// <summary>
     2 /// 繪制標題欄
     3 /// </summary>
     4 private void DrawTitleBar()
     5 {
     6     if (_formBorderStyle == FormBorderStyle.None)
     7         return;
     8 
     9     DrawTitleBackgroundTextIcon();
    10     CreateButtonImages();
    11     DrawTitleButtons();
    12 }

    首先使用線性漸變畫刷繪制標題欄背景、圖示、標題文字:

     1 /// <summary>
     2 /// 繪制標題欄背景、文字、圖示
     3 /// </summary>
     4 private void DrawTitleBackgroundTextIcon()
     5 {
     6     IntPtr hdc = GetWindowDC(Handle);
     7     Graphics g = Graphics.FromHdc(hdc);
     8 
     9     // 標題欄背景
    10     using (Brush brsTitleBar = new LinearGradientBrush(TitleBarRectangle,
    11         _titleBarStartColor, _titleBarEndColor, LinearGradientMode.Horizontal))
    12         g.FillRectangle(brsTitleBar, TitleBarRectangle);
    13 
    14     // 標題欄圖示
    15     if (ShowIcon)
    16         g.DrawIcon(Icon, new Rectangle(
    17             BorderWidth, TitleBarRectangle.Top + (TitleBarRectangle.Height - IconSize) / 2,
    18             IconSize, IconSize));
    19 
    20     // 標題文本
    21     const int txtX = BorderWidth + IconSize;
    22     SizeF szText = g.MeasureString(Text, SystemFonts.CaptionFont, Width, StringFormat.GenericDefault);
    23     using Brush brsText = new SolidBrush(_titleBarForeColor);
    24     g.DrawString(Text,
    25         SystemFonts.CaptionFont,
    26         brsText,
    27         new RectangleF(txtX,
    28             TitleBarRectangle.Top + (TitleBarRectangle.Bottom - szText.Height) / 2,
    29             Width - BorderWidth * 2,
    30             TitleBarHeight),
    31         StringFormat.GenericDefault);
    32 
    33     g.Dispose();
    34     ReleaseDC(Handle, hdc);
    35 }

    隨后繪制標題欄按鈕,猶豫篇幅限制,在此不多贅述,詳見原始碼中CreateButtonImages()與DrawTitleButtons(),

    至此,表面作業基本做完了,但這個視窗還不像個視窗,因為最小化、最大化、關閉以及調整視窗大小都不好用,

    為什么?因為還有很多作業要做,首先,同樣在WndProc中處理WM_NCHITTEST訊息,通過m.Result指定當前滑鼠位置位于標題欄、最小化按鈕、最大化按鈕、關閉按鈕或上下左右邊框

     1 case WM_NCHITTEST:
     2     {
     3         base.WndProc(ref m);
     4 
     5         Point pt = PointToClient(new Point((int)m.LParam & 0xFFFF, (int)m.LParam >> 16 & 0xFFFF));
     6 
     7         _userSizedOrMoved = true;
     8 
     9         switch (_formBorderStyle)
    10         {
    11             case FormBorderStyle.None:
    12                 break;
    13             case FormBorderStyle.FixedSingle:
    14             case FormBorderStyle.Fixed3D:
    15             case FormBorderStyle.FixedDialog:
    16             case FormBorderStyle.FixedToolWindow:
    17                 if (pt.Y < 0)
    18                 {
    19                     _userSizedOrMoved = false;
    20                     m.Result = (IntPtr)HTCAPTION;
    21                 }
    22 
    23                 if (CorrectToLogical(CloseButtonRectangle).Contains(pt))
    24                     m.Result = (IntPtr)HTCLOSE;
    25                 if (CorrectToLogical(MaximizeButtonRectangle).Contains(pt))
    26                     m.Result = (IntPtr)HTMAXBUTTON;
    27                 if (CorrectToLogical(MinimizeButtonRectangle).Contains(pt))
    28                     m.Result = (IntPtr)HTMINBUTTON;
    29 
    30                 break;
    31             case FormBorderStyle.Sizable:
    32             case FormBorderStyle.SizableToolWindow:
    33                 if (pt.Y < 0)
    34                 {
    35                     _userSizedOrMoved = false;
    36                     m.Result = (IntPtr)HTCAPTION;
    37                 }
    38 
    39                 if (CorrectToLogical(CloseButtonRectangle).Contains(pt))
    40                     m.Result = (IntPtr)HTCLOSE;
    41                 if (CorrectToLogical(MaximizeButtonRectangle).Contains(pt))
    42                     m.Result = (IntPtr)HTMAXBUTTON;
    43                 if (CorrectToLogical(MinimizeButtonRectangle).Contains(pt))
    44                     m.Result = (IntPtr)HTMINBUTTON;
    45 
    46                 if (WindowState == FormWindowState.Maximized)
    47                     break;
    48 
    49                 bool bTop = pt.Y <= -TitleBarHeight + BorderWidth;
    50                 bool bBottom = pt.Y >= Height - TitleBarHeight - BorderWidth;
    51                 bool bLeft = pt.X <= BorderWidth;
    52                 bool bRight = pt.X >= Width - BorderWidth;
    53 
    54                 if (bLeft)
    55                 {
    56                     _userSizedOrMoved = true;
    57                     if (bTop)
    58                         m.Result = (IntPtr)HTTOPLEFT;
    59                     else if (bBottom)
    60                         m.Result = (IntPtr)HTBOTTOMLEFT;
    61                     else
    62                         m.Result = (IntPtr)HTLEFT;
    63                 }
    64                 else if (bRight)
    65                 {
    66                     _userSizedOrMoved = true;
    67                     if (bTop)
    68                         m.Result = (IntPtr)HTTOPRIGHT;
    69                     else if (bBottom)
    70                         m.Result = (IntPtr)HTBOTTOMRIGHT;
    71                     else
    72                         m.Result = (IntPtr)HTRIGHT;
    73                 }
    74                 else if (bTop)
    75                 {
    76                     _userSizedOrMoved = true;
    77                     m.Result = (IntPtr)HTTOP;
    78                 }
    79                 else if (bBottom)
    80                 {
    81                     _userSizedOrMoved = true;
    82                     m.Result = (IntPtr)HTBOTTOM;
    83                 }
    84                 break;
    85             default:
    86                 throw new ArgumentOutOfRangeException();
    87         }
    88         break;
    89     }

     隨后以同樣的方式處理WM_NCLBUTTONDBLCLK、WM_NCLBUTTONDOWN、WM_NCLBUTTONUP、WM_NCMOUSEMOVE等訊息,進行標題欄按鈕等元素重繪,不多贅述,

    現在視窗進行正常的單擊、雙擊、調整尺寸,我們在最后為視窗添加陰影

    首先定義一個可以承載32位位圖的分層視窗(Layered Window)來負責主視窗陰影的呈現,詳見原始碼中XFormShadow類,此處僅列出用于創建分層視窗的核心代碼:

     1 private void UpdateBmp(Bitmap bmp)
     2 {
     3     if (!IsHandleCreated) return;
     4 
     5     if (!Image.IsCanonicalPixelFormat(bmp.PixelFormat) || !Image.IsAlphaPixelFormat(bmp.PixelFormat))
     6         throw new ArgumentException(@"位圖格式不正確", nameof(bmp));
     7 
     8     IntPtr oldBits = IntPtr.Zero;
     9     IntPtr screenDC = GetDC(IntPtr.Zero);
    10     IntPtr hBmp = IntPtr.Zero;
    11     IntPtr memDc = CreateCompatibleDC(screenDC);
    12 
    13     try
    14     {
    15         POINT formLocation = new POINT(Left, Top);
    16         SIZE bitmapSize = new SIZE(bmp.Width, bmp.Height);
    17         BLENDFUNCTION blendFunc = new BLENDFUNCTION(
    18             AC_SRC_OVER,
    19             0,
    20             255,
    21             AC_SRC_ALPHA);
    22 
    23         POINT srcLoc = new POINT(0, 0);
    24 
    25         hBmp = bmp.GetHbitmap(Color.FromArgb(0));
    26         oldBits = SelectObject(memDc, hBmp);
    27 
    28         UpdateLayeredWindow(
    29             Handle,
    30             screenDC,
    31             ref formLocation,
    32             ref bitmapSize,
    33             memDc,
    34             ref srcLoc,
    35             0,
    36             ref blendFunc,
    37             ULW_ALPHA);
    38     }
    39     finally
    40     {
    41         if (hBmp != IntPtr.Zero)
    42         {
    43             SelectObject(memDc, oldBits);
    44             DeleteObject(hBmp);
    45         }
    46 
    47         ReleaseDC(IntPtr.Zero, screenDC);
    48         DeleteDC(memDc);
    49     }
    50 }

    最后通過路徑漸變畫刷創建陰影位圖,通過位圖構建分層視窗,并與主視窗建立父子關系:

     1 /// <summary>
     2 /// 構建陰影
     3 /// </summary>
     4 private void BuildShadow()
     5 {
     6     lock (this)
     7     {
     8         _buildingShadow = true;
     9 
    10         if (_shadow != null && !_shadow.IsDisposed && !_shadow.Disposing)
    11         {
    12             // 解除父子視窗關系
    13             SetWindowLong(
    14                 Handle,
    15                 GWL_HWNDPARENT,
    16                 0);
    17 
    18             _shadow.Dispose();
    19         }
    20 
    21         Bitmap bmpBackground = new Bitmap(Width + BorderWidth * 4, Height + BorderWidth * 4);
    22 
    23         GraphicsPath gp = new GraphicsPath();
    24         gp.AddRectangle(new Rectangle(0, 0, bmpBackground.Width, bmpBackground.Height));
    25 
    26         using (Graphics g = Graphics.FromImage(bmpBackground))
    27         using (PathGradientBrush brs = new PathGradientBrush(gp))
    28         {
    29             g.CompositingMode = CompositingMode.SourceCopy;
    30             g.InterpolationMode = InterpolationMode.HighQualityBicubic;
    31             g.PixelOffsetMode = PixelOffsetMode.HighQuality;
    32             g.SmoothingMode = SmoothingMode.AntiAlias;
    33 
    34             // 中心顏色
    35             brs.CenterColor = Color.FromArgb(100, Color.Black);
    36             // 指定從實際陰影邊界到視窗邊框邊界的漸變
    37             brs.FocusScales = new PointF(1 - BorderWidth * 4F / Width, 1 - BorderWidth * 4F / Height);
    38             // 邊框環繞顏色
    39             brs.SurroundColors = new[] { Color.FromArgb(0, 0, 0, 0) };
    40             // 掏空視窗實際區域
    41             gp.AddRectangle(new Rectangle(BorderWidth * 2, BorderWidth * 2, Width, Height));
    42             g.FillPath(brs, gp);
    43         }
    44 
    45         gp.Dispose();
    46 
    47         _shadow = new XFormShadow(bmpBackground);
    48 
    49         _buildingShadow = false;
    50 
    51         AlignShadow();
    52         _shadow.Show();
    53 
    54         // 設定父子視窗關系
    55         SetWindowLong(
    56             Handle,
    57             GWL_HWNDPARENT,
    58             _shadow.Handle.ToInt32());
    59 
    60         Activate();
    61     }//end of lock(this)
    62 }

    感謝大家能讀到這里,代碼中如有錯誤,或存在其它建議,歡迎在評論區或Github指正,

    如果覺得本文對你有幫助,還請點個推薦或Github上點個星星,謝謝大家,

轉載請注明原作者,謝謝,

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/256998.html

標籤:WinForm

上一篇:C#腳本引擎RulesEngine

下一篇:c# chart 圖形顯示值字體大小修改

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more