Winform 自定义TabControl实现浏览器标签
2015-11-23 19:55
295 查看
作者:Gavin(daisong.michelangelo@gmail.com)
时间: Nov, 2015
封面图片为Gavin原创,请勿未经允许私自引
最近因为工作需要,要做一个桌面浏览器,和大多数浏览器一样,我的这个浏览器也需要有标签栏,效果就像这样:目标效果图
在网上查了很多资料,大多数做法都是自定义Winform中TabControl控件,具体的做法有多种。
首先让我们来见识一下winform原生的TabControl是什么样子的:
怎么样,有没有让你有种思考人生的冲动?
下面我们来分析怎么将它改造成我们想要的模样
TabControl有由一个个TabPage组成,每个TabPage有一个TabItem(也就是页签)
让我们从这个页签开始,它由以下三部分组成:
标签页左边是一个图标
中间是网页的Title
右边是标签的关闭按钮
要实现这三个部分,我们可以重写TabControl.DrawItem 事件,在这之前我们当然要继承Winform 的TabControl 组件:
public partial class TabControl1 : TabControl
然后还要记得将DrawMode 设置成 TabDrawMode.OwnerDrawFixed,这样我们就可以自己画TabItem了,否则你在DrawItem里面干的事可就白干了。
然后,我们就可以重写DrawItem事件,开始画也签了
this.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.MainTab_DrawItem);
首先,我们可以先设置标签页的背景,而且标签在选中和未选中的时候背景是不一样的。
// 获取当前选项卡区域 Rectangle bounds = GetTabRect(e.Index); Brush br = SystemBrushes.ActiveCaptionText; if (e.Index == this.SelectedIndex) //当前选中的Tab页,设置不同的样式以示选中 { e.Graphics.FillRectangle(Brushes.White, bounds); //改变选中选项卡标签的背景色 } else { e.Graphics.FillRectangle(Brushes.DarkRed, bounds); //改变非选中的选项卡标签的背景色 br = SystemBrushes.GrayText; //置灰非选中项 }
注意,一定要先画背景,在画其他的东西,不然后画背景会将之前画的东西擦除掉。
接着我们就可以来画图标了,TabControl 有一个ImageList可以存放图片,可以通过key和index获取到。原生的TabControl已经实现了这个功能,但是我们重写了整个TabItem的绘制方法,所以我们还是要自己实现。
//绘制图标 if (this.ImageList != null) { int index = this.TabPages[e.Index].ImageIndex; string key = this.TabPages[e.Index].ImageKey; Image icon = new Bitmap(icoSize, icoSize); if (index > -1) { icon = this.ImageList.Images[index]; } if (!string.IsNullOrEmpty(key)) { icon = this.ImageList.Images[key]; } e.Graphics.DrawImage(icon, bounds.X + startX, bounds.Top + 2); }
好了,我们快要完成了,Graphics有绘制各种形状的方法,参数大多含有坐标,坐标控制就不多说了,MSDN里的方法说的很清楚了。
接下来,我们要画文字了,很简单,TabPages[i].Text就是我们的Title
e.Graphics.DrawString(this.TabPages[i].Text, font, br, bounds.X + startX, bounds.Y + 6);
最后,我们还是一样的方法画出关闭按钮。closeImage可以先导入到资源文件
bounds.Offset(bounds.Width - (CLOSE_SIZE + 5), 2); e.Graphics.DrawImage(closeImage, bounds.X, bounds.Y);
好了,页签的外观我们已经大工告成了,接下来我们要响应关闭按钮来关闭页签。
方法是监听鼠标点击事件,如果点击区域是在我们画关闭按钮的区域呢,我们就冲tabPages中将这个Page移除。
//计算关闭区域 Rectangle bounds = this.GetTabRect(this.SelectedIndex); bounds.Offset(bounds.Width - (CLOSE_SIZE + 5), 2); bounds.Width = CLOSE_SIZE; bounds.Height = CLOSE_SIZE; //如果鼠标在区域内就关闭选项卡 bool isClose = x > bounds.X && x < bounds.Right && y > bounds.Y && y < bounds.Bottom; if (isClose == true) { int index = this.SelectedIndex; TabPage tab = this.SelectedTab; this.TabPages.Remove(tab); tab.Dispose(); }
好了,看来我们已经完美地做出了一个美观的TabItem了,如果你没有别的需要的话,这样的组件就可以使用了。
但如我之前所说,实现方法有多种,因为需求总是在变,所以我们还是需要一种更全面的方法。
如果我需要改变整个TabControl 的背景色透明要怎么办呢?
仅仅重写DrawTabItem是远远不够的。
好了,下面进入今天的主题,怎么重写TabControl的OnPaint()方法来实现我们之前所实现的功能。
首先,我们当然要继承TabControl,还要设置几个属性
this.SetStyle( ControlStyles.UserPaint | // 控件将自行绘制,而不是通过操作系统来绘制 ControlStyles.OptimizedDoubleBuffer | // 该控件首先在缓冲区中绘制,而不是直接绘制到屏幕上,这样可以减少闪烁 ControlStyles.AllPaintingInWmPaint | // 控件将忽略 WM_ERASEBKGND 窗口消息以减少闪烁 ControlStyles.ResizeRedraw | // 在调整控件大小时重绘控件 ControlStyles.SupportsTransparentBackColor, // 控件接受 alpha 组件小于 255 的 BackColor 以模拟透明 true); // 设置以上值为 true this.UpdateStyles();
最终要的一个就是要设置ControlStyles.UserPaint 为True,不然你就思考人生吧!但是设置了这个属性我们就要告别过去,从一张白纸开始了,当然这也意味着我们就可以尽情发挥了。
首先,我们就可以重写OnPaint方法了,这里我们绘制背景就很方便了,只要画一张图片就好了。
protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); Rectangle rec = this.ClientRectangle; // 获取TabControl主控件的工作区域 Image backImage = Properties.Resources.bgImage; //获取背景图片,我的背景图片在项目资源文件中。 e.Graphics.DrawImage(backImage, 0, 0, this.Width, this.Height); //绘制主控件的背景 //绘制标签样式 for (int i = 0; i < this.TabPages.Count; i++) { //....这里和绘制上面的标签页类似 } }
好了,先写到这吧,一个简单的自定义TabControl就实现了。
参考资料:
相关文章推荐
- java持久层框架mybatis如何防止sql注入
- OpenJudge_P7941 不重复地输出数
- C-7.指针
- HttpServletRequest与HttpServletResponse概要
- UVA 11584 Partitioning by Palindromes
- 【bzoj3942】 [Usaco2015 Feb]Censoring KMP
- Access、Hybrid和Trunk三种模式的理解
- 错排数
- 深入理解JDBC的超时设置
- LoadRunner中负载时间Duration与迭代次数Iteration的联系
- SharedSdk 分享
- mysql问题之infomation_schema
- [转]Bluetooth LE Credit-Based Flow Control for L2CAP Connection-Oriented Channels
- Project Euler 82:Path sum: three ways 路径和:3个方向
- 如何使用reg命令修改注册表
- DBCP、C3P0、Proxool 、 BoneCP开源连接池的比较
- ajax-瀑布流效果
- Golang学习笔记:包制作
- codeforces 581A Vasya the Hipster
- 数据结构之队列_Queue