之前由于忙于赶项目进度而忽视了软件的用户体验,界面挺难看,有一天看见组长优化了某个窗体,让人感觉完全不一样,我也不甘示弱,要把我的程序做顺眼一点才行。我的程序是一个以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的简单优化的更多相关文章

  1. 一次千万级别的SQL查询简单优化体验

    背景:从两张有关联的表查询数据,A表数据量1400万,B表数据量8000万.A与B通过ID逻辑关联,没有实际的外键.B表是后来扩展出来的. 问题:根据某个ID查询时超时,运行时跑不出结果. 原因:使用 ...

  2. 双数组trie树的基本构造及简单优化

    一 基本构造 Trie树是搜索树的一种,来自英文单词"Retrieval"的简写,可以建立有效的数据检索组织结构,是中文匹配分词算法中词典的一种常见实现.它本质上是一个确定的有限状 ...

  3. [mysql] 2进制安装和简单优化

    ##################################mysql 2进制安装和简单优化################################################## ...

  4. 封装ajax,让调用变得简单优化

    思考一下: 通常我们在使用ajax来发送接口请求时,每一次都会调用ajax固定的元素,比如data.url.method.success.error等.那么我们想一下能不能先把ajax封装起来,在每次 ...

  5. linux简单优化

    1.简单优化 #关闭firewalld,selinux,NetworkManager systemctl(管理服务的命令) stop(关服务) firewalld (服务名称,d是demo的意思) s ...

  6. mysql的简单优化【简单易学】

    1.选取最适用的字段属性: 表字段尽量设小,不要给数据库增加没必要的空间:如:值为'01'.'02',给char(2)即可: 2.使用连接(JOIN)来代替子查询(Sub-Queries): 使用jo ...

  7. mysql简单优化思路

    mysql简单优化思路 作为开发人员,数据库知识掌握的可能不是很深入,但是一些基本的技能还是要有时间学习一下的.作为一个数据库菜鸟,厚着脸皮来总结一下 mysql 的基本的不能再基本的优化方法. 为了 ...

  8. mysql之优化器、执行计划、简单优化

    mysql之优化器.执行计划.简单优化 2018-12-12 15:11 烟雨楼人 阅读(794) 评论(0) 编辑 收藏 引用连接: https://blog.csdn.net/DrDanger/a ...

  9. 【jQuery基础学习】11 jQuery性能简单优化

    关于性能优化 合适的选择器 $("#id")会直接调用底层方法,所以这是最快的.如果这样不能直接找到,也可以用find方法继续查找 $("p")标签选择器也是直 ...

随机推荐

  1. [.net 面向对象编程基础] (7) 基础中的基础——流程控制语句

    [.net 面向对象编程基础] (7) 基础中的基础——流程控制语句 本来没有这一节的内容,后来考虑到既然是一个系列文章,那么就尽可能写的详细一些,本节参考了网上朋友所写的例子,为的是让更多小伙伴学习 ...

  2. 教你如何完美保存Html编辑器编辑过的文本到Word中

    有时候在网页上面编辑了一段文字,有图片,想保存一份到word文档里面,但是复制粘贴以后发现格式并没有保存下来,今天就来教大家如何完整的保存Html编辑器编辑过的文字(可以包含图片,但是图片必须是绝对路 ...

  3. 使用mybatis访问sql server

    原创文章转载请注明出处:@协思, http://zeeman.cnblogs.com mybatis作为一种半自动化的ORM工具,可以提供更大的灵活性,逐渐受到社区的欢迎. 官方下载地址是:https ...

  4. jQuery实现放大镜效果

    1.1.1 摘要 相信大家都见过或使用过放大镜效果,甚至实现过该效果,它一般应用于放大查看商品图片,一些电商网站(例如:凡客,京东商城,阿里巴巴等)都有类似的图片查看效果. 在接下来的博文中,我们将向 ...

  5. 基于Task的异步模式--全面介绍

    今天是国庆长假第一天,也是今天十月的开始.每到这个时候都是看海的季节-一个看"人海"的季节.反正我是不想在这样一个尴尬期出去放松自己,于是不如在家写写博客,长点本领呢.今天就来给大 ...

  6. Linux用户切换

    1,查看当前user: whoami 2,普通用户切换到root,会要求输入密码: su 3,root切换到普通用户: su - username

  7. Duplicate id @+id/imageView, already defined earlier in this layout,android

    原文地址http://www.thinksaas.cn/topics/0/448/448554.html 其實這個訊息也是可以解掉的,當然最簡單的解法就是你不要使用相同的id就好了.不過萬一你是幫別人 ...

  8. 合法提交Html标签(2)

    提交合法的HTML标签(2) 上面用到了一个Inherits属性,它用来设置页面与后台代码中相关联的类.我们打开CodeFile属性所指的文件,会找到该属性所指的类名.但是这里仅仅存放的是用户定义的事 ...

  9. 基于Oracle的SQL优化(社区万众期待 数据库优化扛鼎巨著)

    基于Oracle的SQL优化(社区万众期待数据库优化扛鼎巨著) 崔华 编   ISBN 978-7-121-21758-6 2014年1月出版 定价:128.00元 856页 16开 编辑推荐 本土O ...

  10. rabbitmq消息队列——"topic型交换器"

    在之前的章节中我们改进了我们的日志系统,我们使用direct型交换器代替了只能盲目广播消息的fanout型交换器,这使得我们可以有选择性地接收日志. 尽管使用direct型交换器改进了我们的日志系统, ...