对TabControl的简单优化
之前由于忙于赶项目进度而忽视了软件的用户体验,界面挺难看,有一天看见组长优化了某个窗体,让人感觉完全不一样,我也不甘示弱,要把我的程序做顺眼一点才行。我的程序是一个以TabControl为主要容器的窗体,这样的程序窗体在目前广泛使用,谷歌浏览器Chrome,360安全卫士,QQ,鲁大师等。



重点是头部的TabItem的变迁,从文字到图标结合文字和单纯图标,让TabControl以一种比较友好的形式融入到界面中去。先看看控件的效果



为了让新的TabControl能适应三种情况(文字,图标下衬文字,图标),就定义了如下枚举,
public enum TabTypeEnum
{
ImageText,
Text,
Image,
}
同时在新的TabControl类里面定义了对应的属性TabType和私有字段_tabType
private TabTypeEnum _tabType;
public TabTypeEnum TabType
{
get { return _tabType; }
set
{
_tabType = value;
if (TabType != TabTypeEnum.Text)
{
SetStyle(ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.SupportsTransparentBackColor,
true);
base.UpdateStyles(); this.SizeMode = TabSizeMode.Fixed; }
else
{ SizeMode = defaultSizeModel;
this.Size = defaultSize;
}
}
}
在改变Tab的类型时要额外加一些处理逻辑,如果是Tab包含图标的,肯定要对控件的Style进行设置
SetStyle(ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.SupportsTransparentBackColor, true);
base.UpdateStyles();
这里设置的都与重绘控件时有关:双缓冲,改变大小则重绘控件。对于单纯图标还有图标+文字,单纯文字这三种方式的Tab大小会有所不同,这个Tab的大小通过ItemSize设置,这里我默认设置了纯文字则按回它初始值的大小,这个初始值在构造函数里获取;图标+文字和纯图标这两种方式在重绘时计算设置。
描绘控件又是去重写OnPaint方法,这样又用回疏远了很久的GDI+去描绘这个TabItem。这里有三种Tab方式,但着重介绍图标+文字这种方式,Tab选中会有阴影的,阴影可以自己PS一个圆角矩形,我这个是网上抠别人的,这个图片已添加“已有项”的形式添加到项目中,然后生成操作选“嵌入资源”。


然后在构造函数里面以下面的形式获取阴影图片资源
backImage = new Bitmap(this.GetType(), "select_background.jpg");
在绘图时,先绘阴影,再绘文字,最后绘图标。
获取当前Tab的矩形主要通过TabControl的GetTabRect(int index)方法,通过判断当前的Tab是不是被选中的,来决定绘不绘制阴影
if (this.SelectedIndex == i)
{
e.Graphics.DrawImage(backImage, this.GetTabRect(i));
}
然后根据Tab文字的Size来决定TabSize,
if (this.ItemSize.Width < (textSize.Width + this.Padding.X * ))
this.ItemSize =
new System.Drawing.Size((int)textSize.Width + this.Padding.X * ,
this.ItemSize.Height);
if (this.ItemSize.Height < (int)textSize.Height + ImageList.ImageSize.Height + this.Padding.Y * )
new System.Drawing.Size(this.ItemSize.Width,
(int)textSize.Height + ImageList.ImageSize.Height + this.Padding.Y * );
然后按照文字的Size还有Tab矩形的位置大小计算出文字的位置,描绘出文字
textPoint.X
= bounds.X + (bounds.Width - textSize.Width) / ;
textPoint.Y
= bounds.Bottom - textSize.Height - this.Padding.Y;
e.Graphics.DrawString(
this.TabPages[i].Text,
this.Font,
SystemBrushes.ControlText,
textPoint.X,
textPoint.Y);
最后描绘图标也是结合了图标的Size和Tab的位置与大小来决定图标所在的位置,
e.Graphics.DrawImage(
icon,
bounds.X + (bounds.Width - icon.Width) / ,
bounds.Top + this.Padding.Y);
加上了这行代码,能让描绘出来的文字少点锯齿
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
Tab的描绘就完成了,其余两种Tab只是省去了文字部分或者图标部分的描绘而已,两部分的代码都会在最后列举整个控件源码时顺带列举出来。
这个控件很大程度上参考了CSDN网友的源码,原本的博文一下子找不出来,要是哪位园友知道的顺带告诉我,我作为参考链接附在文中,谢谢!
class ImageTabControl:TabControl
{
public enum TabTypeEnum
{
ImageText,
Text,
Image,
} Image backImage;
Size defaultSize;
TabSizeMode defaultSizeModel; public ImageTabControl()
{
defaultSize = this.ItemSize;
defaultSizeModel = this.SizeMode; this.TabType = TabTypeEnum.ImageText;
backImage = new Bitmap(this.GetType(), "select_background.jpg");
} private TabTypeEnum _tabType;
public TabTypeEnum TabType
{
get { return _tabType; }
set
{
_tabType = value;
if (TabType != TabTypeEnum.Text)
{
SetStyle(ControlStyles.UserPaint | // 控件将自行绘制,而不是通过操作系统来绘制
ControlStyles.OptimizedDoubleBuffer | // 该控件首先在缓冲区中绘制,而不是直接绘制到屏幕上,这样可以减少闪烁
ControlStyles.AllPaintingInWmPaint | // 控件将忽略 WM_ERASEBKGND 窗口消息以减少闪烁
ControlStyles.ResizeRedraw | // 在调整控件大小时重绘控件
ControlStyles.SupportsTransparentBackColor, // 控件接受 alpha 组件小于 255 的 BackColor 以模拟透明
true);
base.UpdateStyles(); this.SizeMode = TabSizeMode.Fixed;
}
else
{ SizeMode = defaultSizeModel;
this.Size = defaultSize;
}
}
} protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e); if (TabType == TabTypeEnum.ImageText)
DrawImageTextItem(e);
else if (TabType == TabTypeEnum.Image)
DrawImageItem(e);
else if (TabType == TabTypeEnum.Text)
DrawTextItem(e);
} protected virtual void DrawImageTextItem(PaintEventArgs e)
{
for (int i = ; i < this.TabCount; i++)
{
//e.Graphics.DrawRectangle(Pens.Red, this.GetTabRect(i));
if (this.SelectedIndex == i)
{
e.Graphics.DrawImage(backImage, this.GetTabRect(i));
} // Calculate text position
Rectangle bounds = this.GetTabRect(i);
PointF textPoint = new PointF();
SizeF textSize = TextRenderer.MeasureText(this.TabPages[i].Text, this.Font); if (this.ItemSize.Width < (textSize.Width + this.Padding.X * ))
this.ItemSize =
new System.Drawing.Size((int)textSize.Width + this.Padding.X * ,
this.ItemSize.Height);
if (this.ItemSize.Height < (int)textSize.Height + ImageList.ImageSize.Height + this.Padding.Y * )
new System.Drawing.Size(this.ItemSize.Width,
(int)textSize.Height + ImageList.ImageSize.Height + this.Padding.Y * ); // 注意要加上每个标签的左偏移量X
textPoint.X
= bounds.X + (bounds.Width - textSize.Width) / ;
textPoint.Y
= bounds.Bottom - textSize.Height - this.Padding.Y; // Draw highlights
e.Graphics.DrawString(
this.TabPages[i].Text,
this.Font,
SystemBrushes.ControlLightLight, // 高光颜色
textPoint.X,
textPoint.Y); // 绘制正常文字
textPoint.Y--;
e.Graphics.DrawString(
this.TabPages[i].Text,
this.Font,
SystemBrushes.ControlText, // 正常颜色
textPoint.X,
textPoint.Y); if (this.ImageList != null)
{
int index = this.TabPages[i].ImageIndex;
string key = this.TabPages[i].ImageKey;
Image icon = new Bitmap(, ); if (index > -)
{
icon = this.ImageList.Images[index];
}
if (!string.IsNullOrEmpty(key))
{
icon = this.ImageList.Images[key];
}
e.Graphics.DrawImage(
icon,
bounds.X + (bounds.Width - icon.Width) / ,
bounds.Top + this.Padding.Y);
}
} e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
} protected virtual void DrawImageItem(PaintEventArgs e)
{
for (int i = ; i < this.TabPages.Count; i++)
{
if (i == this.SelectedIndex)
{
e.Graphics.DrawImage(backImage, this.GetTabRect(i));
} RectangleF itemRec = this.GetTabRect(i); if (ImageList != null)
{
int imageIndex=this.TabPages[i].ImageIndex;
string imageKey=this.TabPages[i].ImageKey;
Image ico=new Bitmap(,);
if (imageIndex >= )
ico = this.ImageList.Images[i];
if (!string.IsNullOrEmpty(imageKey))
ico = this.ImageList.Images[imageKey]; if (this.ItemSize.Height < ImageList.ImageSize.Height + this.Padding.Y * )
this.ItemSize = new System.Drawing.Size(this.ItemSize.Width,
ImageList.ImageSize.Height + this.Padding.Y * );
if (this.ItemSize.Width < ImageList.ImageSize.Width + this.Padding.X * )
this.ItemSize = new System.Drawing.Size(ImageList.ImageSize.Width + this.Padding.X * ,
this.ItemSize.Height); e.Graphics.DrawImage(ico, itemRec.X + (itemRec.Width - ico.Width) / , itemRec.Y + this.Padding.Y);
}
}
} protected virtual void DrawTextItem(PaintEventArgs e)
{
for (int i = ; i < this.TabCount; i++)
{
//e.Graphics.DrawRectangle(Pens.Red, this.GetTabRect(i));
if (this.SelectedIndex == i)
{
e.Graphics.DrawImage(backImage, this.GetTabRect(i));
} // Calculate text position
Rectangle bounds = this.GetTabRect(i);
PointF textPoint = new PointF();
SizeF textSize = TextRenderer.MeasureText(this.TabPages[i].Text, this.Font); // 注意要加上每个标签的左偏移量X
textPoint.X
= bounds.X + (bounds.Width - textSize.Width) / ;
textPoint.Y
= bounds.Y+(bounds.Height-textSize.Height)/; // Draw highlights
e.Graphics.DrawString(
this.TabPages[i].Text,
this.Font,
SystemBrushes.ControlLightLight, // 高光颜色
textPoint.X,
textPoint.Y); // 绘制正常文字
textPoint.Y--;
e.Graphics.DrawString(
this.TabPages[i].Text,
this.Font,
SystemBrushes.ControlText, // 正常颜色
textPoint.X,
textPoint.Y); }
}
}
ImageTabControl
对TabControl的简单优化的更多相关文章
- 一次千万级别的SQL查询简单优化体验
背景:从两张有关联的表查询数据,A表数据量1400万,B表数据量8000万.A与B通过ID逻辑关联,没有实际的外键.B表是后来扩展出来的. 问题:根据某个ID查询时超时,运行时跑不出结果. 原因:使用 ...
- 双数组trie树的基本构造及简单优化
一 基本构造 Trie树是搜索树的一种,来自英文单词"Retrieval"的简写,可以建立有效的数据检索组织结构,是中文匹配分词算法中词典的一种常见实现.它本质上是一个确定的有限状 ...
- [mysql] 2进制安装和简单优化
##################################mysql 2进制安装和简单优化################################################## ...
- 封装ajax,让调用变得简单优化
思考一下: 通常我们在使用ajax来发送接口请求时,每一次都会调用ajax固定的元素,比如data.url.method.success.error等.那么我们想一下能不能先把ajax封装起来,在每次 ...
- linux简单优化
1.简单优化 #关闭firewalld,selinux,NetworkManager systemctl(管理服务的命令) stop(关服务) firewalld (服务名称,d是demo的意思) s ...
- mysql的简单优化【简单易学】
1.选取最适用的字段属性: 表字段尽量设小,不要给数据库增加没必要的空间:如:值为'01'.'02',给char(2)即可: 2.使用连接(JOIN)来代替子查询(Sub-Queries): 使用jo ...
- mysql简单优化思路
mysql简单优化思路 作为开发人员,数据库知识掌握的可能不是很深入,但是一些基本的技能还是要有时间学习一下的.作为一个数据库菜鸟,厚着脸皮来总结一下 mysql 的基本的不能再基本的优化方法. 为了 ...
- mysql之优化器、执行计划、简单优化
mysql之优化器.执行计划.简单优化 2018-12-12 15:11 烟雨楼人 阅读(794) 评论(0) 编辑 收藏 引用连接: https://blog.csdn.net/DrDanger/a ...
- 【jQuery基础学习】11 jQuery性能简单优化
关于性能优化 合适的选择器 $("#id")会直接调用底层方法,所以这是最快的.如果这样不能直接找到,也可以用find方法继续查找 $("p")标签选择器也是直 ...
随机推荐
- [.net 面向对象编程基础] (17) 数组与集合
[.net 面向对象编程基础] (17) 数组与集合 学习了前面的C#三大特性,及接口,抽象类这些相对抽象的东西以后,是不是有点很累的感觉.具体的东西总是容易理解,因此我们在介绍前面抽象概念的时候,总 ...
- [.net 面向对象编程基础] (21) 委托
[.net 面向对象编程基础] (20) 委托 上节在讲到LINQ的匿名方法中说到了委托,不过比较简单,没了解清楚没关系,这节中会详细说明委托. 1. 什么是委托? 学习委托,我想说,学会了就感觉简 ...
- 【面试必备】javascript操作DOM元素
前言 时间过的真快,不知不觉就到年底了.问问自己,这一年你对自己的工作满意吗? 评价标准是什么呢?当然是马云的那两条准则了:钱给到了吗?干的爽吗?如果答案都是no,那么,你准备好跳槽了吗? 为了应对年 ...
- 一个简单的通用Makefile实现
一个简单的通用Makefile实现 Makefile是Linux下程序开发的自动化编译工具,一个好的Makefile应该准确的识别编译目标与源文件的依赖关系,并且有着高效的编译效率,即每次重新ma ...
- Ubuntu下解决adb devices:???????????? no permissions的方法
之前一直都是在windows下进行开发的,但是这次由于一个小模块用的东西只能在linux下运行,所以就发生了这么一系列的问题环境:虚拟机Vmware下的Ubuntu12.10事件:连接android手 ...
- 自制C#版3DS文件的解析器并用SharpGL显示3DS模型
自制C#版3DS文件的解析器并用SharpGL显示3DS模型 我已经重写了3ds解析器,详情在此(http://www.cnblogs.com/bitzhuwei/p/CSharpGL-2-parse ...
- ASP.NET集成模式下的管道事件
- SpringMVC自定义注入controller变量
springmvc config the controller parameter injection 问题描述 在SpringMVC中默认可以注入Model,ModelAndView,@Reques ...
- 移动开发框架剖析(一) Hammer专业的手势控制
一直在写jQuery的源码教程,都没时间研究别的框架了.Hammer是我项目御用的一个手势库,早期1.x版本的swipe事件的响应不灵敏的问题而改过源码,2.x就已经更正过来,而且源码的结构也整个翻新 ...
- Ubuntu 14.04上安装caffe
本来实在windows 10上尝试安装caffe,装了一天没装上,放弃; 改在windows上装ubuntu的双系统,装了一个下午,不小心windows的系统盘被锁死了,也不会unlock?只好含泪卸 ...