最近要做个软件正在做技术准备,由于WINFORM生成的窗体很丑陋,一个好的软件除了功能性很重要外,UI的体验也是不容忽视的。习惯性的在网上搜素了下,换肤控件也有好几款,但是有些用起来不是很好用,好点的也要花很多银子哦,而且毕竟是别人写的,心里总不是个滋味,所以决定自己尝试着写写看,花了一个晚上终于做出来了个DEMO,貌似还不错,贴图如下(图片是直接是用的暴风影音的,寒自己一个。。)

下面和大家分享下。

首先分析下皮肤的制作原理,我的理解是把整个窗体(去边框后)划分为9个区域(如果有更复杂的界面,可以划分更多),有图有真相:

然后准备皮肤素材,切图,我的切图如下:

接着可以开工了:

1.初始化图片资源变量

  protected int formMinX = 0;//最小化按钮的X坐标protected int formMaxX = 0;//最大化按钮的X坐标protected int formCloseX = 0;//关闭按钮的X坐标protected int formTitleMarginLeft = 0;//标题栏的左边界protected int formTitleMarginRight = 0;//标题栏的右边界Image imgTopLeft = (Image)Resources.topleft;//窗体顶部左上角图片Image imgTopRight = (Image)Resources.topright;//窗体顶部右上角图片Image imgTopMiddle = (Image)Resources.topstretch;//窗体顶部中间图片Image imgBottomLeft = (Image)Resources.bottomLeft;//窗体底部左下角图片Image imgBottonRight = (Image)Resources.bottomRight;//窗体底部右下角图片Image imgBottonmMiddle = (Image)Resources.bottomstretch;//窗体底部中间图片Image imgMiddleLeft = (Image)Resources.LeftDrag_Mid;//窗体中部左边框图片Image imgMiddleRight = (Image)Resources.RightDrag_Mid;//窗体中部右边框图片Image imgFormMin = (Image)Resources.skin_btn_min;//最小化按钮Image imgFormMax = (Image)Resources.skin_btn_max;//最大化按钮Image imgFormClose = (Image)Resources.skin_btn_close;//关闭按钮Image imgFormRestore = (Image)Resources.skin_btn_restore;//还原按钮

2.重写OnPaint事件。代码直接贴上来(比较简单,就是计算图片要绘制到窗体的坐标,然后把图片绘到窗体上)

        protected override void OnPaint(PaintEventArgs e){base.OnPaint(e);this.BackColor = Color.Black;//绘制皮肤Graphics g = e.Graphics;//绘制窗体顶部左上角图片g.DrawImage(imgTopLeft, 0, 0, imgTopLeft.Width, imgTopLeft.Height);int topRightX = e.ClipRectangle.Width - imgTopRight.Width;//绘制窗体顶部右上角图片g.DrawImage(imgTopRight, topRightX, 0, imgTopRight.Width, imgTopRight.Height);int topMiddleWidth= e.ClipRectangle.Width - (imgTopLeft.Width + imgTopRight.Width) + 4;//绘制窗体顶部中间图片(标题栏)formTitleMarginLeft = imgTopLeft.Width;formTitleMarginRight = topRightX;g.DrawImage(imgTopMiddle, imgTopLeft.Width, 0, topMiddleWidth, imgTopMiddle.Height);//绘制窗体底部左下角图片g.DrawImage(imgBottomLeft, 0, e.ClipRectangle.Height - imgBottomLeft.Height, imgBottomLeft.Width, imgBottomLeft.Height);//绘制窗体底部右下角图片g.DrawImage(imgBottonRight, e.ClipRectangle.Width - imgBottomLeft.Width, e.ClipRectangle.Height - imgBottonRight.Height, imgBottonRight.Width, imgBottonRight.Height);//绘制窗体底部中间图片g.DrawImage(imgBottonmMiddle, imgBottomLeft.Width, e.ClipRectangle.Height - imgBottonmMiddle.Height, e.ClipRectangle.Width - (imgBottomLeft.Width + imgBottomLeft.Width) + 4, imgBottonmMiddle.Height);//画左右边框g.DrawImage(imgMiddleLeft, 0, imgTopLeft.Height, imgMiddleLeft.Width, e.ClipRectangle.Height - (imgTopLeft.Height + imgBottomLeft.Height));g.DrawImage(imgMiddleRight, e.ClipRectangle.Width - imgMiddleRight.Width, imgTopRight.Height, imgMiddleRight.Width, e.ClipRectangle.Height - (imgTopLeft.Height + imgBottomLeft.Height));//画右上角按钮(最小化,最大化,关闭)formMinX = topRightX;g.DrawImage(imgFormMin, topRightX, 0, imgFormMin.Width, imgFormMin.Height);if (this.WindowState == FormWindowState.Maximized){imgFormMax = imgFormRestore;}elseimgFormMax = (Image)Resources.skin_btn_max;formMaxX = topRightX + imgFormMin.Width;g.DrawImage(imgFormMax, topRightX + imgFormMin.Width, 0, imgFormMax.Width, imgFormMax.Height);formCloseX = topRightX + imgFormMax.Width + imgFormMin.Width;g.DrawImage(imgFormClose, topRightX + imgFormMax.Width + imgFormMin.Width, 0, imgFormClose.Width, imgFormClose.Height);}

3.当窗体大小发生变化的时候同样要重绘,所以重写OnSizeChanged的事件

         protected override void OnSizeChanged(EventArgs e){base.OnSizeChanged(e);Invalidate();//强制窗体重绘}

OK,这样就完成了皮肤的绘制。接下来我们解决的问题有:

1.如何用鼠标拖拽改变无边框的大小

2.如何用鼠标移动无边框窗体

3.如何让绘制的最小化,最大化,关闭按钮执行操作(响应事件)

对于第1个问题拖拽改变无边框的大小,可以重写消息处理函数WndProc(除了这个我找不到其它好的方法了,如果哪个朋友知道请告诉我一声)

        const int WM_NCHITTEST = 0x0084;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);switch (m.Msg){case WM_NCHITTEST: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;break;}}

第2个问题鼠标移动无边框窗体网上一般有三种方法(详见:[转]C#无边框窗体移动的三种方法)

其中有两种是用WINDOWS消息机制来完成,但是我发现如果用消息机制来处理会造成鼠标的双击或者单击事件不能使用,这一点让我很纠结,所以就采用了最原始的处理方式。

        private Point mouseOffset; //记录鼠标指针的坐标private bool isMouseDown = false; //记录鼠标按键是否按下private void Main_MouseDown(object sender, MouseEventArgs e){if (e.Button == MouseButtons.Left){mouseOffset = new Point(-e.X, -e.Y);isMouseDown = true;}}

        private void Main_MouseUp(object sender, MouseEventArgs e){if (e.Button == MouseButtons.Left){isMouseDown = false;}}private void Main_MouseMove(object sender, MouseEventArgs e){if (isMouseDown){Point mousePos = Control.MousePosition;mousePos.Offset(mouseOffset.X, mouseOffset.Y);Location = mousePos;}}
第3个问题我的思路是处理鼠标单击事件(这就是为什么我放弃了用消息处理机制来解决问题2),根据鼠标的坐标位置判断是在点击哪个图片来触发对应的事件

        private void Main_MouseClick(object sender, MouseEventArgs e){//判断鼠标是否点的是右上角按钮区域if (e.X >= formMinX && e.X <= formMinX + Resources.skin_btn_min.Width && e.Y <= imgFormMin.Height){this.WindowState = FormWindowState.Minimized;}if (e.X >= formMaxX && e.X <= formMaxX + Resources.skin_btn_max.Width && e.Y <= imgFormMax.Height){if (this.WindowState != FormWindowState.Maximized)this.WindowState = FormWindowState.Maximized;elsethis.WindowState = FormWindowState.Normal;}if (e.X >= formCloseX && e.X <= formCloseX + Resources.skin_btn_close.Width && e.Y <= imgFormClose.Height){Application.Exit();}}

然后最后的问题就是解决双击“标题栏”来最大化或者还原窗体了,我的思路是在绘制窗体的时候,就记录标题栏的边界坐标值,然后在双击事件中,根据鼠标的坐标位置来判断触发最大化(还原)事件。

        private void Main_MouseDoubleClick(object sender, MouseEventArgs e){if (e.X >= formTitleMarginLeft && e.X <= formTitleMarginRight && e.Y <= imgTopMiddle.Height){if (this.WindowState != FormWindowState.Maximized)this.WindowState = FormWindowState.Maximized;elsethis.WindowState = FormWindowState.Normal;}}

OK,整个皮肤的制作基本就完成了,乍一看貌似基本功能都实现了,但是如果想实现动态换肤,还是很麻烦的,下篇文章我会给出个动态换肤的解决方案和实现源码。

就这样了,欢迎拍砖。(源码下载)

转载于:https://www.cnblogs.com/haiyabtx/archive/2012/09/21/2697190.html

C# winform 自定义皮肤制作相关推荐

  1. WINFORM自定义皮肤制作(上)

    转自:http://www.cnblogs.com/coding1016/archive/2010/01/22/1653777.html 最近要做个软件正在做技术准备,由于WINFORM生成的窗体很丑 ...

  2. flex中自定义皮肤的按钮制作

    今天聊一下FLEX中自定义皮肤的按钮的制作.首先这是一个按钮皮肤文件: <?xml version="1.0" encoding="utf-8"?> ...

  3. 如何制作.Text Blog自定义皮肤

    如何制作.Text Blog自定义皮肤 昨天在CSDN上发表了一片关于制作.Text Blog自定义皮肤的文章,对于刚接触.TextBlog的爱好者可以参阅 察看.(老手想必都会了,呵呵,小生在此耍耍 ...

  4. C# WINFORM 自定义窗体 皮肤[转]

    跟我学做c#皮肤美化(一) --概述与导航 每每看着QQ,360等等那些软件漂亮的外衣时,你是不是总是在想我的软件什么时候才能穿上这么漂亮的外衣呢?不过现在当你看到这篇文章的时候不必再发出这样的疑问了 ...

  5. 人类一败涂地做图教程_人类一败涂地皮肤怎么弄 人类一败涂地皮肤制作教程...

    原标题:人类一败涂地皮肤怎么弄 人类一败涂地皮肤制作教程 人类一败涂地皮肤怎么弄?皮肤可以在人类一败涂地自定义中设置,我们可以更换各种不同的造型,今天小辰给大家介绍一下人类一败涂地皮肤获取方法和更换教 ...

  6. 《M8围棋谱》自定义皮肤设计指南

    <M8围棋谱>自定义皮肤设计指南 版本:1.0 地址:http://docs.google.com/View?id=dhmvxcsd_3dxpmm6nz 作者:liigo,2009年10月 ...

  7. flex 皮肤制作教程

    FLEX3.0组件可以通过CSS来控制样式,这里的CSS和平时编写网页时所谈及的CSS是有区别的,可以说是为FLEX量身定制的样式表,借助ActionScript可以实现强大的 显示 效果,下面的示例 ...

  8. C#窗体皮肤制作(二):创建窗体库项目以及最小化、最大化、关闭按钮的实现

    很高兴有朋友关注这篇博客,同时也十分抱歉让关注的朋友久等了,隔上一篇博客也有3个月没有更新,主要是由于3月份辞职,4月份初离职到期离开了北京高德,来到了上海张江.目前新工作也处于熟悉当中,希望大家能体 ...

  9. 软件皮肤制作工具(skinbuilder) v1.1官方版下载

    软件皮肤制作工具(skinbuilder)是一款软件皮肤制作工具,是为mircrosoft_visual_studio最易用界面增强net(winform)组件包.有了这个软件,你即可大大的减少时间来 ...

最新文章

  1. contentProvider中有关query方法的使用
  2. 杭电1260java实现
  3. Apache+Tomcat中支持“UTF-8”编码的中文地址
  4. UVALive 7324 ASCII Addition (模拟)
  5. Linux关于文件的权限笔记
  6. 滴滴公众评议会第十一期:司机也可以评价乘客 你支持吗?
  7. collectionutils包_基于springframework的集合处理工具类CollectionUtils对常见对象查找包含转换操作...
  8. 智能实验室-全能优化(Guardio) 4.3.0.730
  9. PMP考试参加培训一定能过吗?
  10. 开发那点事(五)vue开发移动端app案例
  11. centos7 Redis多机多节点集群部署
  12. cadence SPB17.4 - 更换已有原理图的标题栏
  13. Gungho重点工作事项督办督查跟踪管理方案
  14. H5端根据指定地址显示地图并且可调起三大主流地图软件(腾讯、百度、高德)
  15. 高德地图红绿灯读秒是怎么实现的?(一)
  16. 概率论—随机事件与随机事件的概率
  17. Linux文件管理初探---学习文件管理,我想你必须要知道的目录
  18. 【世界观】硅谷最重要文件:Netflix126页PPT完全汉化版
  19. 提高记忆力的 10 种记忆技巧和工具
  20. 出国旅游入乡随俗 看看各国怎么付小费

热门文章

  1. php树莓派魔镜,用树莓派和显示器制作一面“魔镜”
  2. Java两种设计模式_23种设计模式(11)java策略模式
  3. linux 进程 释放内存,Linux 释放内存方法和原理
  4. python脚本:判断字符是否为中文
  5. Max retries exceeded with url 解决方案
  6. 《YOLO算法笔记》(草稿)
  7. 【C++ grammar】对象和类(创建对象、对象拷贝、分离声明与实现)
  8. c语言 函数的参数传递示例_restder()函数,带有C ++中的示例
  9. 基于图像处理的数码印花喷墨墨滴形状规范的研究(Python+OpenCV+Mysql)
  10. linux下mac风格菜单栏,ubuntu 8.04 安装mac风格菜单