作者: 乔克斯
查看: 78452|回复: 67

主题标签Tag

[源码] 【美化TabControl教程】仿苹果Safari浏览器选项卡

  [复制链接]
乔克斯 发表于 2015-1-26 18:18:12 | 显示全部楼层 |阅读模式
查看: 78452|回复: 67
一步一步玩控件:自定义TabControl——从山寨Apple Safari开始
教程:
分析,分析
山寨的灵魂在于分析,首先把刚才拍的高清果照扯过来分解了。



我把他分解成这几个部分:
  • 根据标签不同修改窗体标题
  • 导航标签
  • 标签面板
  • 自动缩放

  • 这篇是#1-1,就专心讨论关于标签的东西,也就是第2、3点。
  • 组件设计
  • 分析了其中的功能,那么就要想想怎么来实现。
  • 从功能来看,这个窗口实际上是由多个子面板切换来实现的,最多他加了点自动缩放。所以从本质来说,还是一个标签切换的窗口。
  • 我最早想到的就是大名鼎鼎却又丑得无以复加的TabControl。

按照标签切换这个思想,TabControl完全可以胜任这次的山寨需求。但是TabControl这么丑,必须要给它整整容才行。想不到我竟然有整容的才华。
下手吧,年轻人!

因为要改动的地方会很多,所以还是完全自己来绘制标签好了。为了完全自定义TabControl,同时方便循环利用,从TabControl派生一个我们自己的标签控件TabControlEx。
[C#] 纯文本查看 复制代码
public class TabControlEx : System.Windows.Forms.TabControl 




这就是我们的TabControlEx,看起来和TabControl没什么两样(那是当然的)。

为了让他看起来不太一样,在构造函数里加上下面的代码。
[C#] 纯文本查看 复制代码
    base.SetStyle(
         ControlStyles.UserPaint |                      // 控件将自行绘制,而不是通过操作系统来绘制
         ControlStyles.OptimizedDoubleBuffer |          // 该控件首先在缓冲区中绘制,而不是直接绘制到屏幕上,这样可以减少闪烁
         ControlStyles.AllPaintingInWmPaint |           // 控件将忽略 WM_ERASEBKGND 窗口消息以减少闪烁
         ControlStyles.ResizeRedraw |                   // 在调整控件大小时重绘控件
         ControlStyles.SupportsTransparentBackColor,    // 控件接受 alpha 组件小于 255 的 BackColor 以模拟透明
         true);                                         // 设置以上值为 true
    base.UpdateStyles();

这段代码的意思就像注释里说的,注意ControlStyles这个枚举是可以按位组合的,所以上面要用「或(|)」来进行连接,这样系统就会完全忽视TabControl这个基类的界面显示,而使用我们自己的方式来呈现UI。

现在TabControlEx看起来是这样的。


啥米?!!OMG!东西哪去了??
嗯,当我第一次玩UserPaint的时候,也被吓了一跳。其实这就是上面我们设置的那句ControlStyles.UserPaint,于是系统就不帮我们画任何东西了。
所以从现在开始,一切都要靠自己了。下面所有的绘制都在OnPaint()方法中绘制。
为了先让我们找到方向,在OnPaint()方法中,我们先把Tab的位置找到,为此我们给每个Tab的边框都画出来。
[C#] 纯文本查看 复制代码
    protected override void OnPaint(PaintEventArgs e)
    {
        for (int i = 0; i < this.TabCount; i++)
        {
            e.Graphics.DrawRectangle(Pens.Red, this.GetTabRect(i));
        }
    }

TabControl.GetTabRect(int)的功能是获得指定index的标签的矩形位置。画完后,我们的TabControlEx看起来不那么迷糊了。

可是,标签的大小还是不对,我们要的不是普通的那种长条,而是闷骚的苹果的瘦高型,要像这样。

嗯,好吧,我们回到构造函数,用下面的语句来设置大小。
[C#] 纯文本查看 复制代码
    this.SizeMode = TabSizeMode.Fixed;  // 大小模式为固定
    this.ItemSize = new Size(44, 55);   // 设定每个标签的尺寸

上面设置44x55其实只是因为苹果原版刚好是这么大,先这么着,后面如果不合适了,回头再来改。现在标签是这样的了。

Apple标签的选中状态是带阴影的,看起来很酷,可是如果我用GDI+来画的话,什么渐变什么变换,烦都烦死了。怎么办呢?
请记住,我们正在山寨。所谓山寨的精神,就是不问方法、不择手段,只要最后「看起来一样」就行了。所以,我决定用上抠图大法,把apple的背景图抠出来。

把这个背景保存为TabBackground.bmp文件,然后添加到项目中,把它做成「嵌入的资源」,就像这样。

然后我们用一个变量来保存背景图。因为这张图随时会用到,所以还是做成全局变量(类级别),在构造函数里读取图片。
[C#] 纯文本查看 复制代码
    Image backImage;
    public TabControlEx()
    {
        // (略)
        backImage = new Bitmap(this.GetType(), "TabButtonBackground.bmp");   // 从资源文件(嵌入到程序集)里读取图片
    }

现在有了图标,加上去看看吧。在OnPaint()里这样写。
[C#] 纯文本查看 复制代码
    if (this.SelectedIndex == i)
    {
        e.Graphics.DrawImage(backImage, this.GetTabRect(i));
    }

只有被选中的标签才会出现这种背景。于是,标签变成这样了。

绘制文字
这会看着还挺单调的,所以我们来加点料。下面来画文字。说起文字,我想你应该注意到了,Safari的标签文字,都是带有阴影的(准确的说是高光)。



所以,在绘制文字时,先用高光色绘制第一遍,再用普通文字色(黑)绘制第二遍。
[C#] 纯文本查看 复制代码
    protected override void OnPaint(PaintEventArgs e)
    {
        for (int i = 0; i < this.TabCount; i++)
        {
            // (略)

            // Calculate text position
            Rectangle bounds = this.GetTabRect(i);
            PointF textPoint = new PointF();
            SizeF textSize = TextRenderer.MeasureText(this.TabPages.Text, this.Font);

            // 注意要加上每个标签的左偏移量X
            textPoint.X
                = bounds.X + (bounds.Width - textSize.Width) / 2;
            textPoint.Y
                = bounds.Bottom - textSize.Height - this.Padding.Y;

            // Draw highlights
            e.Graphics.DrawString(
                this.TabPages.Text,
                this.Font,
                SystemBrushes.ControlLightLight,    // 高光颜色
                textPoint.X,
                textPoint.Y);

            // 绘制正常文字
            textPoint.Y--;
            e.Graphics.DrawString(
                this.TabPages.Text,
                this.Font,
                SystemBrushes.ControlText,    // 正常颜色
                textPoint.X,
                textPoint.Y);

        }
    }



缤纷色彩的源泉:图标
文字也有了,那么接下来就轮到图标了。TabControl是用ImageList控件来存储自己使用的图标的,那么添加一个ImageList,然后加入图标。注意这里都要32x32的图标,所以应该设置ImageList.ImageSize为32x32。
[C#] 纯文本查看 复制代码
    // 绘制图标
    if (this.ImageList != null)
    {
        int index = this.TabPages.ImageIndex;
        string key = this.TabPages.ImageKey;
        Image icon = new Bitmap(1, 1);

        if (index > -1)
        {
            icon = this.ImageList.Images[index];
        }
        if (!string.IsNullOrEmpty(key))
        {
            icon = this.ImageList.Images[key];
        }
        e.Graphics.DrawImage(
            icon,
            bounds.X + (bounds.Width - icon.Width) / 2,
            bounds.Top + this.Padding.Y);
    }


嗯,现在我们的标签看起来像那么回事了,接下来就该难看的红线条退休了。再完善一下,我们的标签就OK了。


同步滚动演示。(上面是山寨,下面是正品,正品的文字开了抗锯齿,山寨没开,这是次要问题)


到此,标签导航部分已经完成,剩下的,就是窗体的自动缩放和同步修改Text功能了。


案例源码下载:


评分

参与人数 4金钱 +6 收起 理由
1182518927 + 2 希望楼主能给我详细的操作流程!O(∩_∩)O.
jobfind + 1 感谢分享,LZ辛苦了~
lp0213 + 2
century5200 + 1 感谢LZ对论坛做出的贡献~

查看全部评分

回复 论坛版权

使用道具 举报

贱贱的贱贱 发表于 2015-1-28 02:15:31 | 显示全部楼层
CSDN野比...控件大神
evilatom 发表于 2015-1-27 09:06:26 | 显示全部楼层
好详细 不错
CastleDrv 发表于 2015-1-27 12:52:22 | 显示全部楼层
支持技术贴
 楼主| 乔克斯 发表于 2015-1-28 10:25:50 | 显示全部楼层

被发现了。
psetpsetpset 发表于 2015-2-5 12:36:05 | 显示全部楼层
学习了。。。
回复

使用道具 举报

caurhy 发表于 2015-4-22 17:19:58 | 显示全部楼层
谢谢大神,学习了,正在编程学习中,感激~~~
aruialy 发表于 2015-4-22 21:17:38 | 显示全部楼层
好东西,感谢楼主分享!
1048138294 发表于 2015-5-7 17:43:51 | 显示全部楼层
很不错的教程..
chh919 发表于 2015-6-3 10:42:01 | 显示全部楼层
试试,感谢感谢。。。
您需要登录后才可以回帖 登录 | 加入CSkin博客

本版积分规则

QQ|申请友链|小黑屋|手机版|Archiver|CSkin ( 粤ICP备13070794号

Powered by Discuz! X3.2  © 2001-2013 Comsenz Inc.  Designed by ARTERY.cn
GMT+8, 2024-5-20 01:35, Processed in 0.641057 second(s), 40 queries , Gzip On.

快速回复 返回顶部 返回列表