1、 网上也看过很多做.NET窗体的例子,我只是把网上的这些东西综合了一下,主要有下面这些特点: 1、边框是半透明的,透明度可根据需要自己更改; 2、可以改变窗体的大小,改变后样式不变; 3、窗体的边框是不规则的; 4、重点解决了窗体会出现闪烁的问题,在窗体移动的时候也不会闪烁; 5、使用方便,只要将AlphaFormPanel拖动到一般的窗体上就可以实现换肤; 设计思路说明: 一、.NET下处理一个窗体部分透明我所知道的有两种方法: 1、用一张支持Alhpa通道的图片来处理半透明,这种方式处理出来的效果会很好,甚至可以用一张动态的图片来做背景。相信有人看过那个
2、游动的鱼的程序,鱼的边缘是半透明的,就是用这种方式做的。这种方式整个窗体都是通过UpdateLayeredWindow画出来的,如果要在上面加控件的话,所有的控件都要自己来绘制,显然在具体的项目中用这种方式的话会大大增加开发的难度。有兴趣的人可以看看这个程序: /Files/liutao409/游动的鱼.rar 关键的代码就是根据这种支持Alhpa通道的图片来绘制窗体 [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] public static extern int UpdateLayeredWin
3、dow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize,IntPtr hdcSrc, ref Point pptSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags); public void SetBits(Bitmap bitmap) { if (!haveHandle) return; if(!Bitmap.IsCanonicalPixelFormat(bitmap.PixelFormat)| !Bitmap.IsAlphaPixelFo
4、rmat(bitmap.PixelFormat)) throw new ApplicationException("图片必须是32位带Alhpa通道的图片。"); IntPtr oldBits = IntPtr.Zero; IntPtr screenDC = Win32.GetDC(IntPtr.Zero); IntPtr hBitmap = IntPtr.Zero; IntPtr memDc = Win32.CreateCompatibleDC(screenDC); try
5、 { 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); hB
6、itmap = 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.BlendFla
7、gs = 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(m
8、emDc, oldBits); Win32.DeleteObject(hBitmap); } Win32.ReleaseDC(IntPtr.Zero, screenDC); Win32.DeleteDC(memDc); } } 2、用两个窗体来实现边框半透明后面的窗体用来做边框,前面的窗体放其他的控件,前面的窗体跟随后面的窗体移动和改变大小,这样就很容易的控制后面的窗体半透明显示。这种方式虽然麻烦一点,但是
9、不用像第一种方式那样自己绘制所有的控件,所以还是可以在项目中使用的。我的这个例子用的就是这种方式实现的。 二、窗体边框的处理 我的窗体的边框是用图片来处理的,为了使窗体的边框在改变大小后的样式不改变,上下边框做成了3段式的,中间部分平铺,两端保持不变这样窗体任意缩放后样式都不会改变。如果对GDI+熟悉的话,也可以不用图片来处理,直接绘制渐变填充也可以。我重写的承载图片的PictureBox,让PictureBox将所有的消息都传给父窗体来处理,这样就可以由父窗体统一的处理窗体的缩放和移动了,具体的代码如下: public partial class Picture
10、BoxEX : PictureBox { public delegate void delSetFormSize(int intInfo); public event delSetFormSize evtSetFormSize; #region 属性 private bool _bTopLeft = false; public bool BTopLeft { get { return _bTopLeft; } set { _b
11、TopLeft = value; } } private bool _bTop = false; public bool BTop { get { return _bTop; } set { _bTop = value; } } private bool _bTopRight = false; public bool BTopRight { get { return _bTo
12、pRight; } set { _bTopRight = value; } } private bool _bLeft = false; public bool BLeft { get { return _bLeft; } set { _bLeft = value; } } private bool _bBottomLeft = false; public bool BBottomLeft
13、 { get { return _bBottomLeft; } set { _bBottomLeft = value; } } private bool _bBottom = false; public bool BBottom { get { return _bBottom; } set { _bBottom = value; } } private bool _bRi
14、ght = false; public bool BRight { get { return _bRight; } set { _bRight = value; } } private bool _bBottomRight = false; public bool BBottomRight { get { return _bBottomRight; } set { _bBottomRi
15、ght = value; }
}
#endregion
#region 构造函数
///
16、t int HTTRANSPARENT = -1; 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、 17; protected override void WndProc(ref Message m) { base.WndProc(ref m); switch (m.Msg) { case WM_NCHITTEST: Point vPoint = new Point((int)m.LParam & 0xFFFF, (int)m.LParam >> 16
18、 & 0xFFFF); vPoint = PointToClient(vPoint); int intInfo = -1; if (_bTopLeft) { if (vPoint.X <= 10) { intInfo = HTTOPLEFT;
19、 } } else if (_bLeft) { intInfo = HTLEFT; } else if (_bBottomLeft) { intInfo = HTBOTTOMLEFT; }
20、 else if (_bBottom) { intInfo = HTBOTTOM; } else if (_bBottomRight) { intInfo = HTBOTTOMRIGHT; } else i
21、f (_bRight) { intInfo = HTRIGHT; } else if (_bTopRight) { if (vPoint.X >= ClientSize.Width - 10) { intInfo = HTT
22、OPRIGHT; } } else if (_bTop) { if (vPoint.Y <= 5) { m.Result = (IntPtr)HTTOP; intInfo = HTTOP;
23、 } } if (evtSetFormSize != null && intInfo != -1) { evtSetFormSize(intInfo); } //将消息传给父窗体来处理 m.Result = (IntPtr)HTTRANSPARENT;
24、 break; } } #endregion } 关键代码说明: 一、用到的消息 private const int WM_NCLBUTTONDBLCLK = 0x00A3; private int _intInfo = -1; //消息回传值 private const int HTLEFT = 10; private const int HTRIGHT = 11; private con
25、st int HTTOP = 12; private const int HTTOPLEFT = 13; private const int HTTOPRIGHT = 14; private const int HTBOTTOM = 15; private const int HTBOTTOMLEFT = 0x10; private const int HTBOTTOMRIGHT = 17; private const int WM_NCHITTEST = 0x84;
26、private const int HTCLIENT = 0x01; private const int HTCAPTION = 0x02; 1、改变窗体大小和移动窗体的位置 所有的和边框有关的消息都传到后面的窗体的处理,承载边框的PictureBoxEx 会将传给它的系统消息忽略掉 protected override void WndProc(ref Message m) { if (m.Msg == WM_NCHITTEST) {
27、 this.DefWndProc(ref m); //移动窗体位置 if (m.Result.ToInt32() == HTCLIENT && this.WindowState != FormWindowState.Maximized) { m.Result = new IntPtr(HTCAPTION); } else {
28、 base.WndProc(ref m); } //改变窗体大小 if (ChangeFormSize && _intInfo != -1 && this.WindowState != FormWindowState.Maximized) { m.Result = (IntPtr)_intInfo; } _intInfo = -1;
29、 } //双击鼠标左键的消息 else if (m.Msg == WM_NCLBUTTONDBLCLK) { if (ChangeFormSize) { //相当于单击一次最大化按钮 btnMax_MouseClick(null, EventArgs.Empty); } }
30、 else { base.WndProc(ref m); } } 2、减少窗体闪烁 下面的是比较常见的减少窗体闪烁的方法 private void SetStyles() { SetStyle( ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPa
31、int | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor, true); SetStyle(ControlStyles.Selectable, false); UpdateStyles(); } 还可以设置窗体双缓存在减少闪烁
32、 this.DoubleBuffered = true; 在窗体控件的创建过程中,如果控件过多的话,用下面这个函数来处理控件的创建也可以减少闪烁 public class AvoidControlFlicker { private int _paintFrozen; public void FreezePainting(Control toFreezeControl, bool isToFreeze) { if (null == toFreezeControl)
33、 throw new ArgumentNullException("toFreezeControl"); if (isToFreeze && toFreezeControl.IsHandleCreated && toFreezeControl.Visible) { if (0 == _paintFrozen++) { NativeMethods.SendMessage(toFreezeControl.Handl
34、e, NativeConsts.WM_SETREDRAW, 0, 0); } } if (!isToFreeze) { if (0 == _paintFrozen) return; if (0 == --_paintFrozen) { NativeMethods.SendMessage(toFreezeControl.Handle, Nati
35、veConsts.WM_SETREDRAW, 1, 0);
toFreezeControl.Invalidate(true);
}
}
}
}
3、为了给初学者提供一些帮助,其它一些处理函数也写出来
///
36、"e"> private void formStyle_LocationChanged(object sender, EventArgs e) { if (PForm != null) { //同时改变前面的子窗体的位置 CForm.Location = new Point(this.Location.X + pbLeft.Width, this.Location.Y + panelTop.Height);
37、 CForm.Update();
}
}
///
38、s.Close();
}
//关闭窗体时要同时关闭窗体所在的父窗体
protected override void OnClosing(CancelEventArgs e)
{
e.Cancel = true;
base.OnClosing(e);
Owner.Close();
}
///
39、 /// /// private void btnMax_MouseClick(object sender, EventArgs e) { if (this.WindowState == FormWindowState.Maximized) { this.WindowState = FormWindowSt
40、ate.Normal;
this.Opacity = FormOpacity;
}
else
{
this.WindowState = FormWindowState.Maximized;
this.Opacity = 1;
}
}
///
41、>
///
///
private void btnMin_MouseClick(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Minimized;
}
///
42、> /// /// private void formStyle_SizeChanged(object sender, EventArgs e) { if (PForm != null) { //同时改变子窗体的大小 CForm.Size = new Size(this.Size.Width - pbLeft.Width - pbRight.Width, this.Size.Height - panelTop.Height - panelBottom.Height); } } 最后,提供一下这个控件以及示例代码给大家下载:






