之前由于忙于赶项目进度而忽视了软件的用户体验,界面挺难看,有一天看见组长优化了某个窗体,让人感觉完全不一样,我也不甘示弱,要把我的程序做顺眼一点才行。我的程序是一个以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 面向对象编程基础] (22) 事件

    [.net 面向对象编程基础] (22)  事件 事件(Event)是学习.net面向对象编程很重要的一部分,在学习事件之前,我们实际上已经在很多地方使用了事件,比如控件的click事件等,这些都是. ...

  2. 深入理解MVVM模式中Silverlight的Trigger、Action和Behavior及Silverlight的继承机制

    接触Silverlight已经有两三个月了,开始一直感觉他和Winform很相似,拖拖控件就行了,所以一直把经历放在了研究后台和服务器交互和性能优化上面,很少去仔细研究Silverlight的页面.前 ...

  3. FusionCharts简单教程(七)-----使用FusionCharts实现下钻功能

          前面介绍的FusionCharts都是对FusionCharts的基本属性进行操作,下面几篇博文就FusionCharts的高级特性做一个介绍,包括:添加下钻链接.使用Style样式定制图 ...

  4. HTTP学习笔记(五)

    目前,市场上流行有很多web服务器软件,每种服务器都有自己的特点.我们在开发的过程中,经常要和它们打交道,所以了解它们的工作原理也是很重要的. 几款比较流行的服务器 它们会做些什么? 第三篇中有这样的 ...

  5. CocoaPods 深入使用

    在 CocoaPods 使用中介绍了基本的使用 写项目的时候想用到 SQLite.swift第三方库,但是问题来了 pod search SQLite.swift  //执行这条语句,搜索不到结果 但 ...

  6. js常用函数

    日期时间函数(需要用变量调用): var c=new Date; c.getDate(); document.write(c) //获取当前时间 var c=new Date(); c.getTime ...

  7. [常见问题]解决创建servlet 找不到webservlet包.

    今天在创建一个springmvc项目的时候发现 使用的HttpServletRequest不起作用, 提示需要映入 jar文件, 于是便有了今天的这个问题: 百度了下才发现 项目需要导入Runtime ...

  8. Atitit 开发2d游戏的技术选型attilax总结

    Atitit 开发2d游戏的技术选型attilax总结 1.1. 跨平台跨平台:一定要使用跨平台的gui技术,目前最好的就是h5(canvas,webgl,dom) +js了..1 1.2. 游戏前后 ...

  9. 用scrollTop制作一个自动滚动公告栏

    我们在浏览网页时,经常会看到会一些滚动的栏目,比如向上滚动的公告.新闻等.其实他们的制作都不难,只要学了基础的html.css.javascript就可以做出来,用JavaScript的scrollT ...

  10. Android SDK 百度云盘分享链接

    SDK百度云盘地址: 链接: http://pan.baidu.com/s/1skSCplF 密码: drq4 使用说明: 这是Android开发所需的sdk,下载并解压后,将解压出的整个文件夹复制或 ...