一步一步玩控件:TabControl——从制作山寨Safari窗体开始
2012-05-18 06:20
344 查看
绘制标签图标
嗯,现在我们的标签看起来像那么回事了,接下来就该难看的红线条退休了。再完善一下,我们的标签就OK了。
同步滚动演示。(上面是山寨,下面是正品,正品的文字开了抗锯齿,山寨没开,这是次要问题)
到此,标签导航部分已经完成,剩下的,就是窗体的自动缩放和同步修改Text功能了。
(下集预报:实现自动收缩的窗体。敬请期待)
© 野比 2012 版权所有
现在继续昨天的山寨。昨天我们分析得到了4条需要山寨的部分,如下。
根据标签不同修改窗体标题
导航标签
标签面板
自动缩放
通过昨天的努力,我们已经搞定了第2、3条,所以,今天的任务,就只剩下两条
根据标签不同修改窗体标题
导航标签
标签面板
自动缩放
修改窗体标题
我们参考下图,
我们制作的TabControlEx是作为它所在窗体的子控件存在的,为了获得包含TabControlEx的窗体(的引用),可以调用TabControlEx的FindForm()方法(从Control继承)。FindForm()可以获取容纳该控件的顶层窗体,在我们的例子里,就是我们的山寨Safari窗体。
为了在TabControlEx刚刚加入父控件的时候(也就是窗体初始化的时候)就能够顺利「劫持」到窗体的引用,并修改它的标题(否则显示Tab0的时候会发现窗体的标题还未改变),我们重写一下TabControlEx的ParentChanged事件,在获取到父窗体之后顺便把第一个标签Text赋给窗体做标题。
这样,我们就可以在启动时就修改父窗体标题了(以及后面修改窗体大小)。我们最终的目的是每次切换标签时都改变父窗体标题,现在我们拿到了窗体的引用,只需要重写TabControlEx的Selected事件。
下面是完成之后的效果
自动调整窗体大小
完成了杂项工作,现在要进入今天的重点:自动调整大小。在开始之前,先来回顾一下这个闷骚的功能。
下面来好好分析一下到底发生了什么事。
注意,大家发现右下角那个问号没有?根据观察,那个问号始终是保持在窗体右下角的,这就好办了,直接Anchor到Right和Bottom就行。因此下面的分析中直接无视它了。
从本质上来看,因为切换的标签内容高度不同,所以窗体高度也发生了改变。但不管怎么变,窗体的底部到最下面一个控件的距离Δ没有变化,参考分析图。
所以,动画就是在|H1-H2|这段距离内发生的,Δ据截图估算,约为20px(把它写成常量)。
因为窗体有可能变大或变小,另外,值得注意的是,Safari是在窗体动画完成,调整大小到位以后,才显示新标签的控件,这样做可以显得很有动感,而且留下了足够的时间加载控件。所以,动画应该在标签的Selecting事件里解决,而显示控件留到Selected事件。
下面来分析大小调整的算法。
© 野比 2012 版权所有
山寨算法:从不追求精确还原
通过慢镜头分析,可以看到在相同时间差内窗体大小的运动距离是不同的,窗体大小不是匀速改变的。前半段是加速收缩或膨胀,最后是减速到位。
尽管现在还写不出来,为了不让算法影响设计进度,先把算法写在单独的方法里(更好的应该是写成委托,直接传递方法,但你认为一个山寨货有必要吗)。
既然这样,那么算法的问题我们稍后再来讨论,
算法尝试(本节待修改)
目前流行的加减速函数有很多,最简单的从1次函数(匀速)、2次函数(匀加速)到3、4甚至5次函数都有人在用。这类指数型的加速函数使用简单方便,用得很多。下面是在Mahematica里绘制的几种函数曲线,从上倒下分别为:g=10的自由落体函数,y=x^2,y=x^3,y=x^4和y=x直线(注意:为了让大家看清函数细节,x和y轴不是1:1的)。
看起来要实现又加速又减速还真是麻烦,看来只有去掉减速了。反正山寨嘛,只要「看起来像」就行了。没办法,我们是搞山寨的,手艺当然不行了,所以到底用那种,还真的不知道。山寨大法告诉我们,不知道的东西,「试,就对了」。那么就选3个版本的getHeight()来试试。
动画(本节待修改)
现在研究怎样让窗体动起来。由于动画过程较长,将近1秒,那么我们实现的时候应当尽量以不影响主线程为前提。除了动不动就多线程这种有点大炮打蚊子太2的方法外,我们还可以用系统自带的定时器(System.Windows.Forms.Timer)。在每个Timer.Tick事件里挪一步,合起来就成了动画。
现在我们可以填写刚才分析的Selecting和Selected事件了。
(未完待续)
// 绘制图标 if (this.ImageList != null) { int index = this.TabPages[i].ImageIndex; string key = this.TabPages[i].ImageKey; Image icon = new Bitmap(1, 1); if (index > -1) { icon = this.ImageList.Images[index]; } // ImageKey优先级较高 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功能了。
(下集预报:实现自动收缩的窗体。敬请期待)
© 野比 2012 版权所有
现在继续昨天的山寨。昨天我们分析得到了4条需要山寨的部分,如下。
根据标签不同修改窗体标题
导航标签
标签面板
自动缩放
通过昨天的努力,我们已经搞定了第2、3条,所以,今天的任务,就只剩下两条
根据标签不同修改窗体标题
导航标签
标签面板
自动缩放
修改窗体标题
我们参考下图,
我们制作的TabControlEx是作为它所在窗体的子控件存在的,为了获得包含TabControlEx的窗体(的引用),可以调用TabControlEx的FindForm()方法(从Control继承)。FindForm()可以获取容纳该控件的顶层窗体,在我们的例子里,就是我们的山寨Safari窗体。
为了在TabControlEx刚刚加入父控件的时候(也就是窗体初始化的时候)就能够顺利「劫持」到窗体的引用,并修改它的标题(否则显示Tab0的时候会发现窗体的标题还未改变),我们重写一下TabControlEx的ParentChanged事件,在获取到父窗体之后顺便把第一个标签Text赋给窗体做标题。
// 对父窗体的引用 Form owner; protected override void OnParentChanged(EventArgs e) { // 如果没有劫持到,则搜索 if (owner == null) owner = this.FindForm(); owner.Text = this.TabPages[0].Text; }
这样,我们就可以在启动时就修改父窗体标题了(以及后面修改窗体大小)。我们最终的目的是每次切换标签时都改变父窗体标题,现在我们拿到了窗体的引用,只需要重写TabControlEx的Selected事件。
protected override void OnSelected(TabControlEventArgs e) { parent.Text = e.TabPage.Text; }
下面是完成之后的效果
自动调整窗体大小
完成了杂项工作,现在要进入今天的重点:自动调整大小。在开始之前,先来回顾一下这个闷骚的功能。
下面来好好分析一下到底发生了什么事。
注意,大家发现右下角那个问号没有?根据观察,那个问号始终是保持在窗体右下角的,这就好办了,直接Anchor到Right和Bottom就行。因此下面的分析中直接无视它了。
从本质上来看,因为切换的标签内容高度不同,所以窗体高度也发生了改变。但不管怎么变,窗体的底部到最下面一个控件的距离Δ没有变化,参考分析图。
所以,动画就是在|H1-H2|这段距离内发生的,Δ据截图估算,约为20px(把它写成常量)。
const int FORM_DELTA = 20;
因为窗体有可能变大或变小,另外,值得注意的是,Safari是在窗体动画完成,调整大小到位以后,才显示新标签的控件,这样做可以显得很有动感,而且留下了足够的时间加载控件。所以,动画应该在标签的Selecting事件里解决,而显示控件留到Selected事件。
下面来分析大小调整的算法。
© 野比 2012 版权所有
山寨算法:从不追求精确还原
通过慢镜头分析,可以看到在相同时间差内窗体大小的运动距离是不同的,窗体大小不是匀速改变的。前半段是加速收缩或膨胀,最后是减速到位。
尽管现在还写不出来,为了不让算法影响设计进度,先把算法写在单独的方法里(更好的应该是写成委托,直接传递方法,但你认为一个山寨货有必要吗)。
private double getHeight(double time) { // (Not implemented yet) }
既然这样,那么算法的问题我们稍后再来讨论,
算法尝试(本节待修改)
目前流行的加减速函数有很多,最简单的从1次函数(匀速)、2次函数(匀加速)到3、4甚至5次函数都有人在用。这类指数型的加速函数使用简单方便,用得很多。下面是在Mahematica里绘制的几种函数曲线,从上倒下分别为:g=10的自由落体函数,y=x^2,y=x^3,y=x^4和y=x直线(注意:为了让大家看清函数细节,x和y轴不是1:1的)。
看起来要实现又加速又减速还真是麻烦,看来只有去掉减速了。反正山寨嘛,只要「看起来像」就行了。没办法,我们是搞山寨的,手艺当然不行了,所以到底用那种,还真的不知道。山寨大法告诉我们,不知道的东西,「试,就对了」。那么就选3个版本的getHeight()来试试。
动画(本节待修改)
现在研究怎样让窗体动起来。由于动画过程较长,将近1秒,那么我们实现的时候应当尽量以不影响主线程为前提。除了动不动就多线程这种有点大炮打蚊子太2的方法外,我们还可以用系统自带的定时器(System.Windows.Forms.Timer)。在每个Timer.Tick事件里挪一步,合起来就成了动画。
// Δ常量 int FORM_DELTA = 20; // 动画用Timer Timer timer; // 经历时间计数器 int elapsed = 0; // 构造函数 public TabControlEx() { // (略) // 初始化Timer timer = new Timer(); timer.Interval = 100; timer.Enabled = false; timer.Tick += new EventHandler(timer_Tick); } // Timer tickle void timer_Tick(object sender, EventArgs e) { if (parent == null) return; elapsed++; parent.Height = getHeight(elapsed, FORM_DELTA); }
现在我们可以填写刚才分析的Selecting和Selected事件了。
(未完待续)
相关文章推荐
- [转]一步一步玩控件:自定义TabControl——从山寨Safari开始
- 关于TabControl 控件,窗体自动适应,大小的属性AutoScaleModo=Inherit
- C#利用tabControl控件实现多窗体嵌入及关闭
- C#利用tabControl控件实现多窗体嵌入及关闭
- C#使用tabcontrol控件可操作多窗体嵌入以及双击窗口名称可关闭
- Web版的Tabcontrol控件的制作过程
- C#利用tabcontrol控件实现多窗体嵌入及关闭
- TabControl控件的最佳实践,可以把一个窗体和用户控件添加进来
- DevExpress 使用 XtraTabbedMdiManager 控件以 Tab样式加载 Mdi窗体并合并 RibbonControl 解决方案
- DevExpress 使用 XtraTabbedMdiManager 控件以 Tab样式加载 Mdi窗体并合并 RibbonControl 解决方案
- C#利用tabControl控件实现多窗体嵌入及关闭
- C#利用tabControl控件实现多窗体嵌入及关闭
- DevExpress 使用 XtraTabbedMdiManager 控件以 Tab样式加载 Mdi窗体并合并 RibbonControl 解决方案
- C#将子窗体嵌入到tabControl控件的tabpage中
- Delphi封装Mdi窗体到Dll并使用插件管理,tabControl制作多页面
- WPF之TabControl控件用法
- VS2013/MFC编程入门之三十一(常用控件:标签控件Tab Control )
- 一步一步学List Control控件的用法(第三步)----设置风格
- VC++ Tab Control控件的使用
- VS2010/MFC编程入门之三十三(常用控件:标签控件Tab Control 下)