作者:Gavin(daisong.michelangelo@gmail.com)
时间: Nov, 2015
封面图片为Gavin原创,请勿未经允许私自引

最近因为工作需要,要做一个桌面浏览器,和大多数浏览器一样,我的这个浏览器也需要有标签栏,效果就像这样:目标效果图
在网上查了很多资料,大多数做法都是自定义Winform中TabControl控件,具体的做法有多种。
首先让我们来见识一下winform原生的TabControl是什么样子的:
怎么样,有没有让你有种思考人生的冲动?
下面我们来分析怎么将它改造成我们想要的模样
TabControl有由一个个TabPage组成,每个TabPage有一个TabItem(也就是页签)
让我们从这个页签开始,它由以下三部分组成:

  1. 标签页左边是一个图标
  2. 中间是网页的Title
  3. 右边是标签的关闭按钮

要实现这三个部分,我们可以重写TabControl.DrawItem 事件,在这之前我们当然要继承Winform 的TabControl 组件:
public partial class TabControl1 : TabControl
然后还要记得将DrawMode 设置成 TabDrawMode.OwnerDrawFixed,这样我们就可以自己画TabItem了,否则你在DrawItem里面干的事可就白干了。
然后,我们就可以重写DrawItem事件,开始画也签了

  1. this.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.MainTab_DrawItem);

首先,我们可以先设置标签页的背景,而且标签在选中和未选中的时候背景是不一样的。

  1. // 获取当前选项卡区域
  2. Rectangle bounds = GetTabRect(e.Index);
  3. Brush br = SystemBrushes.ActiveCaptionText;
  4. if (e.Index == this.SelectedIndex) //当前选中的Tab页,设置不同的样式以示选中
  5. {
  6. e.Graphics.FillRectangle(Brushes.White, bounds); //改变选中选项卡标签的背景色
  7. }
  8. else
  9. {
  10. e.Graphics.FillRectangle(Brushes.DarkRed, bounds); //改变非选中的选项卡标签的背景色
  11. br = SystemBrushes.GrayText; //置灰非选中项
  12. }

注意,一定要先画背景,在画其他的东西,不然后画背景会将之前画的东西擦除掉。
接着我们就可以来画图标了,TabControl 有一个ImageList可以存放图片,可以通过key和index获取到。原生的TabControl已经实现了这个功能,但是我们重写了整个TabItem的绘制方法,所以我们还是要自己实现。

  1. //绘制图标
  2. if (this.ImageList != null)
  3. {
  4. int index = this.TabPages[e.Index].ImageIndex;
  5. string key = this.TabPages[e.Index].ImageKey;
  6. Image icon = new Bitmap(icoSize, icoSize);
  7. if (index > -1)
  8. {
  9. icon = this.ImageList.Images[index];
  10. }
  11. if (!string.IsNullOrEmpty(key))
  12. {
  13. icon = this.ImageList.Images[key];
  14. }
  15. e.Graphics.DrawImage(icon, bounds.X + startX, bounds.Top + 2);
  16. }

好了,我们快要完成了,Graphics有绘制各种形状的方法,参数大多含有坐标,坐标控制就不多说了,MSDN里的方法说的很清楚了。
接下来,我们要画文字了,很简单,TabPages[i].Text就是我们的Title

  1. e.Graphics.DrawString(this.TabPages[i].Text, font, br, bounds.X + startX, bounds.Y + 6);

最后,我们还是一样的方法画出关闭按钮。closeImage可以先导入到资源文件

  1. bounds.Offset(bounds.Width - (CLOSE_SIZE + 5), 2);
  2. e.Graphics.DrawImage(closeImage, bounds.X, bounds.Y);

好了,页签的外观我们已经大工告成了,接下来我们要响应关闭按钮来关闭页签。
方法是监听鼠标点击事件,如果点击区域是在我们画关闭按钮的区域呢,我们就冲tabPages中将这个Page移除。

  1. //计算关闭区域
  2. Rectangle bounds = this.GetTabRect(this.SelectedIndex);
  3. bounds.Offset(bounds.Width - (CLOSE_SIZE + 5), 2);
  4. bounds.Width = CLOSE_SIZE;
  5. bounds.Height = CLOSE_SIZE;
  6. //如果鼠标在区域内就关闭选项卡
  7. bool isClose = x > bounds.X && x < bounds.Right
  8. && y > bounds.Y && y < bounds.Bottom;
  9. if (isClose == true)
  10. {
  11. int index = this.SelectedIndex;
  12. TabPage tab = this.SelectedTab;
  13. this.TabPages.Remove(tab);
  14. tab.Dispose();
  15. }

好了,看来我们已经完美地做出了一个美观的TabItem了,如果你没有别的需要的话,这样的组件就可以使用了。
但如我之前所说,实现方法有多种,因为需求总是在变,所以我们还是需要一种更全面的方法。
如果我需要改变整个TabControl 的背景色透明要怎么办呢?
仅仅重写DrawTabItem是远远不够的。

好了,下面进入今天的主题,怎么重写TabControl的OnPaint()方法来实现我们之前所实现的功能。
首先,我们当然要继承TabControl,还要设置几个属性

  1. this.SetStyle(
  2. ControlStyles.UserPaint | // 控件将自行绘制,而不是通过操作系统来绘制
  3. ControlStyles.OptimizedDoubleBuffer | // 该控件首先在缓冲区中绘制,而不是直接绘制到屏幕上,这样可以减少闪烁
  4. ControlStyles.AllPaintingInWmPaint | // 控件将忽略 WM_ERASEBKGND 窗口消息以减少闪烁
  5. ControlStyles.ResizeRedraw | // 在调整控件大小时重绘控件
  6. ControlStyles.SupportsTransparentBackColor, // 控件接受 alpha 组件小于 255 的 BackColor 以模拟透明
  7. true); // 设置以上值为 true
  8. this.UpdateStyles();

最终要的一个就是要设置ControlStyles.UserPaint 为True,不然你就思考人生吧!但是设置了这个属性我们就要告别过去,从一张白纸开始了,当然这也意味着我们就可以尽情发挥了。
首先,我们就可以重写OnPaint方法了,这里我们绘制背景就很方便了,只要画一张图片就好了。

  1. protected override void OnPaint(PaintEventArgs e)
  2. {
  3. base.OnPaint(e);
  4. Rectangle rec = this.ClientRectangle; // 获取TabControl主控件的工作区域
  5. Image backImage = Properties.Resources.bgImage; //获取背景图片,我的背景图片在项目资源文件中。
  6. e.Graphics.DrawImage(backImage, 0, 0, this.Width, this.Height); //绘制主控件的背景
  7. //绘制标签样式
  8. for (int i = 0; i < this.TabPages.Count; i++)
  9. {
  10. //....这里和绘制上面的标签页类似
  11. }
  12. }

好了,先写到这吧,一个简单的自定义TabControl就实现了。
参考资料:

一步一步玩控件:自定义TabControl——从山寨Safari开始

Winform 自定义TabControl实现浏览器标签的更多相关文章

  1. [转]一步一步玩控件:自定义TabControl——从山寨Safari开始

    作者:野比 (conmajia@gmail.com) 时间:May, 2012 封面图片为野比原创,请勿未经允许私自引用 #1-1 嗯,各位,又是我,生物钟颠倒的家伙. 今天我要山寨的是大名鼎鼎的Ap ...

  2. 一步一步玩控件:自定义TabControl——从山寨Safari开始

    作者:野比 (conmajia@gmail.com) 时间:May, 2012 封面图片为野比原创,请勿未经允许私自引用 #1-1 嗯,各位,又是我,生物钟颠倒的家伙. 今天我要山寨的是大名鼎鼎的Ap ...

  3. WPF自定义TabControl样式

    WPF自定义TabControl,TabControl美化 XAML代码: <TabControl x:Class="SunCreate.Common.Controls.TabCont ...

  4. DevExpress navBarControl 和 xtraTabbedMdiManager实现浏览器标签页效果

    一:navBarControl 属性设置 工具箱中的Navigation & Layout选项卡下找到NavBarControl,拖到窗体中 工具箱中添加2个imageCollection.分 ...

  5. [转] WinForm自定义函数FindControl实现按名称查找控件

    原文地址 WinForm自定义函数FindControl实现按名称查找控件 本文所述实例实现WinForm自定义函数FindControl实现按名称查找控件的功能,在C#程序开发中有一定的实用价值. ...

  6. js基础--浏览器标签页隐藏或显示状态 visibility详解

    欢迎访问我的个人博客:http://www.xiaolongwu.cn 前言 在工作中我们可能会遇到这样的需求,当浏览器切换到别的标签页或着最小化时,我们需要暂停页面上正在播放的视频或者音乐,这个需求 ...

  7. selenium WebDriver 对浏览器标签页的切换

    关于selenium WebDriver 对浏览器标签页的切换,现在的市面上最新的浏览器,当点击一个链接打开一个新的页面都是在浏览器中打开一个标签页,而selenium只能对窗口进行切换的方法,只能操 ...

  8. Django 自定义 过滤器和模板标签

    代码布局(自定义的代码,放在哪里) 二种方式:1. 某个app特有的 -app 目录下,templatetags 文件夹     ** 必需是这个名称的包(目录中有__init__.py文件) -再到 ...

  9. Django框架之【自定义模板过滤器与标签】

    本文在我的微信公众号的链接:https://mp.weixin.qq.com/s?__biz=MzU5NTU5MjcwNw==&mid=2247483674&idx=1&sn= ...

随机推荐

  1. 5分钟理解Centos7防火墙firewalld

    版权声明:本内容为原创内容,转载请声明出处. 原文地址:http://www.excelib.com/article/287/show firewalld简介 Centos7中默认将原来的防火墙ipt ...

  2. hdu1596 find the safest road - floyd

    2017-08-04 14:42:56 writer:pprp 题意: Problem Description XX星球有很多城市,每个城市之间有一条或多条飞行通道,但是并不是所有的路都是很安全的,每 ...

  3. Spring -- spring 中使用jdbc, c3p0连接池, dao概念

    1. 示例代码 CustomerDao.java  ,dao接口 public interface CustomerDao { public void insertCustomer(Customer ...

  4. 用java实现单链表

    对于一个单链表来说,要求有最基本的数据节点以及一些重要的方法. 方法应该有增删改查.定位.输出.获取链表长度.排序.链表读入.链表输出.下面是我用java写的单链表 public class List ...

  5. spring mvc:文本框

    采用:<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> ...

  6. 卸载MicrosoftBAF(删除C:\CommonFramework\instdata.dat)

    发现有个可疑文件夹 C:\CommonFramework ,而且还会不停的删除创建 C:\CommonFramework\instdata.dat 最后被发现这是必应的一个框架程序在捣鬼,在微软论坛里 ...

  7. socket长连接和短链接区别

    短连接 连接->传输数据->关闭连接 HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束后就中断连接.短连接是指SOCKET连接后发送后接收完数据后马上断 ...

  8. day5-configparser模块

    一.概述 在软件开发过程中,很多时候需要处理配置文件的读取解析和改写,在python中专门处理配置文件的模块就是configpaser了.顾名思义,configpaser就是配置解析器,可用来对符合格 ...

  9. Ubuntu窗口大小调节方法

    Description: 在Vmware Workstation 11上安装了Ubuntu 10.0,画面显示如下所示: Ubuntu系统的屏幕太小.调整方法:调节显示器分辨率即可,下图是将分辨率调节 ...

  10. get running task , process and service

    public class MyActivityManager extends ExpandableListActivity { private static final String NAME = & ...