赞
踩
之前学习的 LayeredSkin 看到里面有个异形窗口,比较感兴趣,所以就找一下资料研究一下。不规则窗体学习有一个比较好的例子,叫 GoldFishProject,是一条鱼金鱼在屏幕上游。
不规则窗口示例代码
GoldFishProject 游动金鱼的学习代码
如果没有积分,也可以关注我获取哟~
现学习了两种实现方式:
这种方式实现的不规则窗口很平滑,没有锯齿,可以带半透明的效果,但是不在响应 paint 方法,绘制不了窗体上的控件,效果图:
代码如下:
窗体代码:
public partial class UpdateLayeredWindowForm : Form { bool haveHandle = false;//窗体句柄创建完成 public UpdateLayeredWindowForm() { InitializeComponent(); } private void UpdateLayeredWindowForm_Load(object sender, EventArgs e) { FormBorderStyle = FormBorderStyle.None;//取消窗口边框 SetBits(new Bitmap(BackgroundImage));//设置不规则窗体 FormMovableEvent();//设置拖动窗体移动 } #region 防止窗体闪屏 private void InitializeStyles() { SetStyle( ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor, true); SetStyle(ControlStyles.Selectable, false); UpdateStyles(); } #endregion #region 句柄创建事件 protected override void OnHandleCreated(EventArgs e) { InitializeStyles();//设置窗口样式、双缓冲等 base.OnHandleCreated(e); haveHandle = true; } #endregion #region 设置窗体样式 protected override CreateParams CreateParams { get { CreateParams cParms = base.CreateParams; cParms.ExStyle |= 0x00080000; // WS_EX_LAYERED return cParms; } } #endregion #region 设置不规则窗体 public void SetBits(Bitmap bitmap) { if (!haveHandle) return; if (!Bitmap.IsCanonicalPixelFormat(bitmap.PixelFormat) || !Bitmap.IsAlphaPixelFormat(bitmap.PixelFormat)) throw new ApplicationException("The picture must be 32bit picture with alpha channel."); IntPtr oldBits = IntPtr.Zero; IntPtr screenDC = Win32.GetDC(IntPtr.Zero); IntPtr hBitmap = IntPtr.Zero; IntPtr memDc = Win32.CreateCompatibleDC(screenDC); try { Win32.Point topLoc = new Win32.Point(Left, Top); Win32.Size bitMapSize = new Win32.Size(bitmap.Width, bitmap.Height); Win32.BLENDFUNCTION blendFunc = new Win32.BLENDFUNCTION(); Win32.Point srcLoc = new Win32.Point(0, 0); hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); oldBits = Win32.SelectObject(memDc, hBitmap); blendFunc.BlendOp = Win32.AC_SRC_OVER; blendFunc.SourceConstantAlpha = 255;//这里设置窗体绘制的透明度 blendFunc.AlphaFormat = Win32.AC_SRC_ALPHA; blendFunc.BlendFlags = 0; Win32.UpdateLayeredWindow(Handle, screenDC, ref topLoc, ref bitMapSize, memDc, ref srcLoc, 0, ref blendFunc, Win32.ULW_ALPHA); } finally { if (hBitmap != IntPtr.Zero) { Win32.SelectObject(memDc, oldBits); Win32.DeleteObject(hBitmap); } Win32.ReleaseDC(IntPtr.Zero, screenDC); Win32.DeleteDC(memDc); } } #endregion #region 无标题栏的窗口移动 private Point mouseOffset; //记录鼠标指针的坐标 private bool isMouseDown = false; //记录鼠标按键是否按下 /// <summary> /// 窗体移动监听绑定 /// </summary> private void FormMovableEvent() { //窗体移动 this.MouseDown += new MouseEventHandler(Frm_MouseDown); this.MouseMove += new MouseEventHandler(Frm_MouseMove); this.MouseUp += new MouseEventHandler(Frm_MouseUp); } /// <summary> /// 窗体按下时 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Frm_MouseDown(object sender, MouseEventArgs e) { int xOffset; int yOffset; //点击窗体时,记录鼠标位置,启动移动 if (e.Button == MouseButtons.Left) { xOffset = -e.X; yOffset = -e.Y; mouseOffset = new Point(xOffset, yOffset); isMouseDown = true; } } /// <summary> /// 窗体移动时 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Frm_MouseMove(object sender, MouseEventArgs e) { if (isMouseDown) { //移动的位置计算 Point mousePos = Control.MousePosition; mousePos.Offset(mouseOffset.X, mouseOffset.Y); Location = mousePos; } } /// <summary> /// 窗体按下并释放按钮时 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Frm_MouseUp(object sender, MouseEventArgs e) { // 修改鼠标状态isMouseDown的值 // 确保只有鼠标左键按下并移动时,才移动窗体 if (e.Button == MouseButtons.Left) { //松开鼠标时,停止移动 isMouseDown = false; //Top高度小于0的时候,等于0 if (this.Top < 0) { this.Top = 0; } } } #endregion }
Win32API代码:(从金鱼的例子中引用的代码)
//########################################################################## //★★★★★★★ http://www.cnpopsoft.com ★★★★★★★ //★★ VB & C# source code and articles for free !!! ★★ //★★★★★★★ Davidwu ★★★★★★★ //########################################################################## using System; using System.Runtime.InteropServices; /// <summary> /// Wind32API /// </summary> internal class Win32 { #region 消息 public const int MF_REMOVE = 0x1000; public const int SC_RESTORE = 0xF120; //还原 public const int SC_MOVE = 0xF010; //移动 public const int SC_SIZE = 0xF000; //大小 public const int SC_MINIMIZE = 0xF020; //最小化 public const int SC_MAXIMIZE = 0xF030; //最大化 public const int SC_CLOSE = 0xF060; //关闭 public const int WM_SYSCOMMAND = 0x0112; public const int WM_COMMAND = 0x0111; public const int GW_HWNDFIRST = 0; public const int GW_HWNDLAST = 1; public const int GW_HWNDNEXT = 2; public const int GW_HWNDPREV = 3; public const int GW_OWNER = 4; public const int GW_CHILD = 5; public const int WM_NCCALCSIZE = 0x83; public const int WM_WINDOWPOSCHANGING = 0x46; public const int WM_PAINT = 0xF; public const int WM_CREATE = 0x1; public const int WM_NCCREATE = 0x81; public const int WM_NCPAINT = 0x85; public const int WM_PRINT = 0x317; public const int WM_DESTROY = 0x2; public const int WM_SHOWWINDOW = 0x18; public const int WM_SHARED_MENU = 0x1E2; public const int HC_ACTION = 0; public const int WH_CALLWNDPROC = 4; public const int GWL_WNDPROC = -4; public const int WS_SYSMENU = 0x80000; public const int WS_SIZEBOX = 0x40000; public const int WS_MAXIMIZEBOX = 0x10000; public const int WS_MINIMIZEBOX = 0x20000; #endregion [StructLayout(LayoutKind.Sequential)] public struct Size { public Int32 cx; public Int32 cy; public Size(Int32 x, Int32 y) { cx = x; cy = y; } } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct BLENDFUNCTION { public byte BlendOp; public byte BlendFlags; public byte SourceConstantAlpha; public byte AlphaFormat; } [StructLayout(LayoutKind.Sequential)] public struct Point { public Int32 x; public Int32 y; public Point(Int32 x, Int32 y) { this.x = x; this.y = y; } } public const byte AC_SRC_OVER = 0; public const Int32 ULW_ALPHA = 2; public const byte AC_SRC_ALPHA = 1; [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] public static extern IntPtr CreateCompatibleDC(IntPtr hDC); [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] public static extern IntPtr GetDC(IntPtr hWnd); [DllImport("gdi32.dll", ExactSpelling = true)] public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObj); [DllImport("user32.dll", ExactSpelling = true)] public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] public static extern int DeleteDC(IntPtr hDC); [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] public static extern int DeleteObject(IntPtr hObj); [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] public static extern int UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pptSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags); [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)] public static extern IntPtr ExtCreateRegion(IntPtr lpXform, uint nCount, IntPtr rgnData); [DllImport("user32")] public static extern int SendMessage(IntPtr hwnd, int msg, int wp, int lp); }
这种方式不能实现半透明效果,有锯齿,好处是能显示出控件,贴个效果图感受下:
代码如下:
public partial class GraphicsPathForm : Form { public GraphicsPathForm() { InitializeComponent(); } private void GraphicsPathForm_Load(object sender, EventArgs e) { TopMost = true;//设置为最顶层 FormBorderStyle = FormBorderStyle.None;//取消窗口边框 this.Region = new Region(GetWindowRegion(new Bitmap(BackgroundImage)));//设置不规则窗体 FormMovableEvent();//设置拖动窗体移动 } #region 设置不规则窗体 private GraphicsPath GetWindowRegion(Bitmap bitmap) { Color TempColor; GraphicsPath gp = new GraphicsPath(); if (bitmap == null) return null; for (int nX = 0; nX < bitmap.Width; nX++) { for (int nY = 0; nY < bitmap.Height; nY++) { TempColor = bitmap.GetPixel(nX, nY); //if (TempColor.A != 0)//去掉完全透明区域 if (TempColor.A == 255)//保留完全不透明的区域 { gp.AddRectangle(new Rectangle(nX, nY, 1, 1)); } } } return gp; } #endregion #region 无标题栏的窗口移动 private Point mouseOffset; //记录鼠标指针的坐标 private bool isMouseDown = false; //记录鼠标按键是否按下 /// <summary> /// 窗体移动监听绑定 /// </summary> private void FormMovableEvent() { //窗体移动 this.MouseDown += new MouseEventHandler(Frm_MouseDown); this.MouseMove += new MouseEventHandler(Frm_MouseMove); this.MouseUp += new MouseEventHandler(Frm_MouseUp); } /// <summary> /// 窗体按下时 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Frm_MouseDown(object sender, MouseEventArgs e) { int xOffset; int yOffset; //点击窗体时,记录鼠标位置,启动移动 if (e.Button == MouseButtons.Left) { xOffset = -e.X; yOffset = -e.Y; mouseOffset = new Point(xOffset, yOffset); isMouseDown = true; } } /// <summary> /// 窗体移动时 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Frm_MouseMove(object sender, MouseEventArgs e) { if (isMouseDown) { //移动的位置计算 Point mousePos = Control.MousePosition; mousePos.Offset(mouseOffset.X, mouseOffset.Y); Location = mousePos; } } /// <summary> /// 窗体按下并释放按钮时 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Frm_MouseUp(object sender, MouseEventArgs e) { // 修改鼠标状态isMouseDown的值 // 确保只有鼠标左键按下并移动时,才移动窗体 if (e.Button == MouseButtons.Left) { //松开鼠标时,停止移动 isMouseDown = false; //Top高度小于0的时候,等于0 if (this.Top < 0) { this.Top = 0; } } } #endregion }
像第一种不能添加控件的方法要想实现显示控件的话,下一个关键词就是“双层窗体”,使用两层窗体来实现一个不规则窗体的效果,大致步骤如下:
底层是皮肤层,使用第一种方法;
上层是控件层,使用第二种方法。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。