用C#制作 个性化窗体 winform 界面.docx
《用C#制作 个性化窗体 winform 界面.docx》由会员分享,可在线阅读,更多相关《用C#制作 个性化窗体 winform 界面.docx(30页珍藏版)》请在冰豆网上搜索。
用C#制作个性化窗体winform界面
引言:
谁都希望自己的应用程序能让人留下一个深刻的印象,让自己的程序窗体有一件与众不同的"外衣"是一个好办法。
试想:
在一大堆的普通窗口中突然跳出一个很酷的界面,一定能让人眼睛一亮进而产生兴趣的。
在VB,VC中如何定制可伸缩个性化窗口早就不是什么秘密了,已经有了大量相关的文章进行介绍,无非都是如何调用系统API之类的方法,但是在.Net中调用API却相对比较麻烦,所以使用.Net制作个性化窗体的文章也有一些,一般都是使用透明背景加图片的方式,所以不能移动或者不能任意放大缩小窗体。
那有没有不需要调用系统API的方法来实现可伸缩的个性化窗体的办法呢?
当然有,.NetFramework提供了一套非常强大的系统类库,我们下面就要做一个使用"纯".Net打造的可伸缩个性化窗体。
我们需要将窗体所有的"皮肤"全部换成我们自己定义的,包括标题栏,边框和系统按纽等,所以我们首先需要定做一套自己的皮肤图形文件。
因为窗体是可伸缩的,所以我们不能简单的取一整幅图片来作为窗体皮肤,而是根据需要先将图片切割为不同的部分,一般来说,有以下图示几大部分(红线为切割线):
根据方位,将图片各部分命名为:
Bottom_Left,Bottom_Middle,Bottom_Right,Middle_Left,Middle_Right,Top_Left,Top_Middle,Top_Right,SysButton_Min,SysButton_Max,SysButton_Close,SysButton_Restore等。
注意,有些图片是可以伸缩的地方,比如Middle_Left,Bottom_Middle等处的图片可以只是一小块,以后需要进行重复贴图。
而有些固定大小的图片,比如Bottom_Left,Top_Left等以后只用贴一次,实际应用的时候要注意区分。
采用以上原则,你便可以制作皮肤图片,图示如下:
然后可以将这些图片放到ImageList控件或资源文件中供程序调用。
(关于如何制作资源文件请参考:
VisualC#资源文件编程--创建资源文件)
接下来,我们使用VisualStudio.Net新建一个Windows应用程序的项目,在窗体的属性设置中,将窗体的FormBorderStyle属性设置为None(无边框样式),如下图所示:
定义一个资源管理器:
privateResourceManagerrm;
然后使用以下的方法在Form的构造函数中将图片取出来(资源文件名为Skin.resources):
rm=newResourceManager("SkinWindow.Skin",Assembly.GetExecutingAssembly());
Bottom_Left=(Bitmap)rm.GetObject("Bottom_Left");
…(其它的图片也按照此方法取)
重载Form的OnPaint事件:
Graphicsg=e.Graphics;
//手工画窗体的各个部分
DrawMiddle_Left(e.Graphics);//画左边框
DrawBottom_Middle(e.Graphics);//画下边框
DrawMiddle_Right(e.Graphics);//画右边框
DrawBottom_Left(e.Graphics);//画左下角
DrawBottom_Right(e.Graphics);//画右下角
DrawTop_Left(e.Graphics);//画标题栏左边
DrawTop_Right(e.Graphics);//画标题栏右边
DrawTop_Middle(e.Graphics);//画标题栏中间
DrawSys_Button(e.Graphics);//画系统按纽
以下是上述画皮肤方法的具体实现部分,我只举一个画左边框的代码示例,其它的部分请读者举一返三:
privatevoidDrawMiddle_Left(Graphicsg)
{
Brushbrush=newTextureBrush(Middle_Left,newRectangle(0,0,
Middle_Left.Width,Middle_Left.Height));
g.FillRectangle(brush,0,TITLE_WIDTH,Middle_Left.Width,
Height-Bottom_Middle.Height-TITLE_WIDTH);
}
衣服穿上了,现在我们的程序就有了不同的外观:
看上去已经很酷了,不过只是花架子,因为边框,标题栏,系统按纽都是我们自己画上去的假的边框,标题栏和系统按纽,所以这个窗体既不能移动也不能自由的放大缩小,点关闭都没用。
以前我们写程序从来都不需要关心这个的,这些都是窗体的基本功能呀?
没有从来都没有想到这个竟然还会是个问题吧?
怎么办呢?
答案就是我们自己来做,不过会比较麻烦,因为取消掉了边框,所以Windows不会帮你发出系统事件,你捕捉不到系统发生了什么事情的话,就没有办法写下响应代码,所以我们要自己检测鼠标的坐标,并根据鼠标的动作,自己来发出事件消息,然后进行响应。
首先我们先定义出一些响应事件的代码,我定义了一个抽象的基类MouseAction,用来表示所有的鼠标事件,它有一个抽象方法Action:
publicabstractclassMouseAction
{
publicabstractvoidAction(intScreenX,intScreenY,System.Windows.Forms.Formform);
}
然后再来定义出它的各个派生类来表示出具体每个鼠标事件响应的代码。
下面是一个向右拉伸窗口事件的代码响应:
publicclassMouseDrag:
MouseAction
{
privateintx,y;
publicMouseDrag(inthitX,inthitY)
{
x=hitX;
y=hitY;
}
publicoverridevoidAction(intScreenX,intScreenY,System.Windows.Forms.Formform)
{
form.Location=newPoint(ScreenX-x,ScreenY-y);
}
}
接下来我们开始编写发出事件的代码,先定义几个变量:
privateintLEFT=5,RIGHT=5,BOTTOM=5,TOP=5,TITLE_WIDTH=45;//边框和标题栏的大小
privateintx=0,y=0;//保存鼠标的临时坐标
privateMouseActionmouse;//鼠标的事件响应对象
然后在Form的MouseDown事件中记录下鼠标的当前坐标:
x=e.X;
y=e.Y;
附:
e为System.Windows.Forms.MouseEventArgs
然后再根据鼠标的坐标定义出事件响应对象:
//鼠标点击左上边框
if((e.X<=LEFT+10&&e.Y<=TOP)||(e.Y<=TOP+10&&e.X<=LEFT))
...{
mouse=newMouseSizeTopLeft(Location.X,Location.Y,Width,Height);
return;
}
//当然有的事件也可以直接响应:
//鼠标点击系统关闭按纽
if(e.X>Width-20&&e.Y>6&&e.X...{
Close();
return;
}
//大部分的事件响应实际上是在MouseMove事件中完成的:
privatevoidForm_MouseMove(objectsender,System.Windows.Forms.MouseEventArgse)
...{
this.Parent.Cursor=CheckCursorType(e.X,e.Y);//改变鼠标的指针形状
if(mouse!
=null)
...{
mouse.Action(Control.MousePosition.X,Control.MousePosition.Y,this);//执行时间响应
//注意坐标是Control.MousePosition这个静态变量给出的,它的值为鼠标在桌面上的全局坐标
}
}
//给出每个不同部位的鼠标的指针形状:
privateCursorCheckCursorType(intX,intY)
...{
if(((X<=LEFT+10&&Y<=TOP)||(Y<=TOP+10&&X<=LEFT))
||((X>=Width-RIGHT-10&&Y>=Height-BOTTOM)
||(Y>=Height-BOTTOM-10&&X>=Width-RIGHT)))
...{
returnCursors.SizeNWSE;
}
elseif(((Y<=TOP+10&&X>=Width-RIGHT)
||(Y<=TOP&&X>=Width-RIGHT-10))
||((X<=LEFT&&Y>=Height-BOTTOM-10)
||(Y>=Height-BOTTOM&&X<=LEFT+10)))
...{
returnCursors.SizeNESW;
}
elseif(X>=Width-RIGHT||X<=LEFT)
...{
returnCursors.SizeWE;
}
elseif(Y>=Height-BOTTOM||Y<=TOP)
...{
returnCursors.SizeNS;
}
else
...{
returnCursors.Arrow;
}
}
//最后在MouseUp事件中将mouse变量释放掉:
privatevoidForm_MouseUp(objectsender,System.Windows.Forms.MouseEventArgse)
...{
mouse=null;
}
//为了更加逼真,还可以加上标题栏的双击最大化或者还原的事件:
privatevoidForm_DoubleClick(objectsender,System.EventArgse)
...{
if(y>TOP&&y ...{
if(WindowState==FormWindowState.Normal)
...{
WindowState=FormWindowState.Maximized;
SysButton=SysButton_Restore;
Invalidate();
}
elseif(WindowState==FormWindowState.Maximized)
...{
WindowState=FormWindowState.Normal;
SysButton=SysButton_Max;
Invalidate();
}
}
}
防止窗体被缩小成一个点,最好给窗口的MinimumSize赋上一个适当的值,例如200,200。
总结:
现在编译你的程序,运行试试,你的窗体已经拥有正常窗体所拥有的全部功能,并且还具有与众不同的外观,不明就里的人一下子还猜不出来你是怎么弄的,好了,乘别人还不知道,赶快拿出去炫耀一下吧:
)。
我原本打算这样做,可是在实施过程中,好歹也需要绘制改变大小的虚框,随着mouseMove不断更新虚框,我是用controlPaint在屏幕上绘制的,但是更新的时候也有一定闪烁,可是看一般的文件夹,根本就没有闪烁,可否告知这是为什么
∙对我有用[0]
∙丢个板砖[0]
∙引用
∙举报
∙管理
∙TOP
精华推荐:
搜索后的关键字高亮显示(急)100分
∙
∙hbxtlhx
∙(平民百姓)
∙
∙等 级:
∙更多勋章
#6楼得分:
0回复于:
2008-03-2713:
55:
48
对你的窗体在构造函数中添加如下的语句就不会闪动了:
this.SetStyle(ControlStyles.OptimizedDoubleBuffer|ControlStyles.UserPaint|ControlStyles.AllPaintingInWmPaint|ControlStyles.ResizeRedraw,true);
但这时需要你对窗体进行绘制。
∙对我有用[0]
∙丢个板砖[0]
∙引用
∙举报
∙管理
∙TOP
∙
∙hbxtlhx
∙(平民百姓)
∙
∙等 级:
∙更多勋章
#7楼得分:
0回复于:
2008-03-2714:
04:
25
参考一下下面的代码,这是一个无边的窗体且在上面有绘制:
C#code
publicpartialclassNoBorder:
Form
{
internalstaticintWM_NCHITTEST=0x84;//移动鼠标,按住或释放鼠标时发生的系统消息
internalstaticIntPtrHTCLIENT=(IntPtr)0x1;//工作区
internalstaticIntPtrHTSYSMENU=(IntPtr)3;//系统菜单
internalstaticIntPtrHTCAPTION=(IntPtr)0x2;//标题栏
privateintm_BorderWidth=2;
privateintm_CaptionHeight=22;
publicNoBorder()
{
this.SetStyle(ControlStyles.OptimizedDoubleBuffer|ControlStyles.UserPaint|ControlStyles.AllPaintingInWmPaint|ControlStyles.ResizeRedraw,true);
InitializeComponent();
this.Padding=newPadding(this.m_BorderWidth,this.m_BorderWidth+this.m_CaptionHeight,this.m_BorderWidth,this.m_BorderWidth);
}
protectedoverrideCreateParamsCreateParams
{
get
{
intWS_SYSMENU=0x00080000;
intWS_MAXIMIZEBOX=0x10000;
intWS_MINIMIZEBOX=0x20000;
intWS_THICKFRAME=0x40000;
CreateParamscp=base.CreateParams;
cp.Style|=WS_SYSMENU|0x20000|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_THICKFRAME;
returncp;
}
}
///
///重写以处理模拟鼠标系统处理
///
///
protectedoverridevoidWndProc(refMessagem)
{
if(m.Msg==WM_NCHITTEST)
{
base.WndProc(refm);
if(DesignMode)
{
return;
}
if(m.Result==HTCLIENT)
{
m.HWnd=this.Handle;
Rectanglerect=this.Bounds;
Pointcp=Cursor.Position;
if(cp.Y>rect.Top+m_BorderWidth&&cp.Y<=rect.Top+m_CaptionHeight+m_BorderWidth&&cp.X>rect.Left+m_BorderWidth&&cp.X{
if((cp.X<=rect.Left+m_BorderWidth+m_CaptionHeight))
{
m.Result=HTSYSMENU;//模拟系统菜单,双击可以关闭窗体
}
else
{
m.Result=HTCAPTION;//模拟标题栏,移动或双击可以最大或最小化窗体
}
}
}
return;
}
base.WndProc(refm);
}
///
///绘制窗体
///
///
protectedoverridevoidOnPaint(PaintEventArgse)
{
base.OnPaint(e);
RectanglecaptionRect=Rectangle.FromLTRB(this.m_BorderWidth,this.m_BorderWidth,this.ClientRectangle.Right-this.m_BorderWidth,this.m_CaptionHeight);
RectangleclientRect=Rectangle.FromLTRB(this.m_BorderWidth,this.m_BorderWidth+this.m_CaptionHeight,this.ClientRectangle.Right-this.m_BorderWidth,this.ClientRectangle.Bottom-this.m_BorderWidth);
//绘制整个窗体
e.Graphics.FillRectangle(SystemBrushes.InactiveBorder,this.ClientRectangle);
//绘制标题栏
e.Graphics.FillRectangle(SystemBrushes.ActiveCaption,captionRect);
//绘制窗体的图标
if(this.Icon!
=null)
{
e.Graphics.DrawIcon(this.Icon,Rectangle.FromLTRB(this.m_BorderWidth,this.m_BorderWidth,this.m_CaptionHeight,this.m_CaptionHeight));
}
//绘制窗体客户区
e.Graphics.FillRectangle(SystemBrushes.Window,clientRect);
stringstrHello="欢迎访问我的CSDN博客:
\n
TextRenderer.DrawText(e.Graphics,strHello,this.Font,clientRect,Color.Red,Color.White,TextFormatFlags.VerticalCenter);
}
///
///重写以处理鼠标按钮准备调整窗体大小
///
///
protectedoverridevoidOnMouseDown(MouseEventArgse)
{
base.OnMouseDown(e);
MState.SetData(this.Bounds,Cursor.Position);
}
///
///重写以处理鼠标抬起事件重置状态
///
///
protectedoverridevoidOnMouseUp(MouseEventArgse)
{
base.OnMouseUp(e);
MState.ResetState();
}
///