文章来源:Matt Berseth
翻译:candylight
过去的两周里,我读了一些关于 Silverlight 2的资料,也通读了ScottGu的系列教程, 查看了deep zoom的demo (这里和这里),甚至还浏览了一些样例控件主题的XAML文件,当然,我自己并没有全部编译一遍。因此,我会针对Silverlight样式和模板特性一文放上一些小例子,如创建Button 控件的皮肤,让它们看起来类似我的Vista中IE7按钮效果。我知道在某种程度上说,这可能算是一种退步(Silverlight默认的Button已经很漂亮),但我还是不想让我的第一例子就这样流产了~
下面网格给出了我的Button控件的四种不同状态,如果你仔细看看按钮背景,你还能看见有一种不错的玻璃效果,就像我们过去用HTML语言来调用图片效果一样。除了这个闪光点,Button的其他部分采用的是Silverlight中最基本的对象--Rectangle(一个是背景,一个是玻璃效果遮罩,另一个是焦点虚线框)。除了一堆Rectangle,我的自定义Button皮肤还能设置在鼠标经过、点击和禁用时的动画效果。下面是我的自定义Button在不同状态下的图片:
预效果预览地址 | 下载地址 (需安装有VS 2008和Silverlight 2 Beta 1 Tools)
自定义Button控件外观
在我们定制Button皮肤前,我们需要先了解Button的外观定制要点—这样我们才能知道怎么做。
使用内嵌样式
跟许多ASP.NET控件类似,Button外观可以通过修改代码、改变按钮的属性来自定义。因此,如果你想要一个蓝色的按钮,只需要设定Background属性的值设为Blue。
使用样式元素
对于大多数UI(用户界面)开发者来说,内嵌样式看起来非常自然,但是它们却很难维护,也容易很快混乱,因此我们需要把这些内嵌设置放到程序的Resource集里封装起来,以供再次使用—就像CSS。为此,我添加了一个Style对象到程序的节点Resource集中,在Stryle对象里,我设定Background属性为Blue,如下所示:
App.xaml
Page.xaml
你会觉得这种方式跟使用CSS比较相似。要替代内嵌样式,你只需要指定要改变的样式对象的资源就可以了。
在保持原有行为的情况下,完全替换掉控件视觉外观
现在开始进行真正比较酷的部分。用Silverlight和WPF你可以定制Button控件的任何特征,ScottGu的“使用control templates”教程已经给出了一些有趣的例子,充分展示了这个特性的灵活性,如下面这个Button中嵌入完整功能Calenda控件的例子(图片来自他的博客):
现在开始我们在Button模板中加入一些图形和控件,Button控件支持hook特性,这样我们就可以指定我们的按钮在下列四种情况时,看起来是什么样子:{通常状态Normal State, 鼠标经过状态MouseOver State, 鼠标按下状态Pressed State, 禁用状态Disabled State},另外还有按钮当前获得焦点的状态。MSDN上的这一段描可以帮助我们里解这点。
当你创建一个新的控件模板时,你重新定义了可视化结构和可视化行为。具体细节的控件代码你可以参考控件的ControlTemplate。举例来说,代码调用了控件一个存放在模板中的方法,也就是你必须了解模板和代码是如何相互关联的。这种联系称为Control Contract,也就是控件可视化部分与逻辑部分的一个关系。- MSDN
我理解的是—控件代码需要知道模板中对象的一些细节,这样才能保证模板提供的功能正确。如果你想在鼠标经过按钮时,改变背景颜色由红到蓝,你必须让Button知道。当我的控件得到焦点时,Button能显示点状矩形框。了解了这些自定义的要点内容后,我们可以参考MSDN上Style and Templates页面,在那里的ControlTemple中,我看到许多对象包括StoryBoard对象和Button控件的描述。如果你创建一个Button模板,想在MoverOver状态下,按钮具有动画效果的话—请你在模板的MouseOver状态中,添加StoryBoard对象。下面是设定Button控件公共界面的对象详细描述:
另外的这些内容在文档中称作Template Parts(模板部分),如果你找到MSDN上Button控件部分,你可以注意到这些项作为Button类文档的一部分。我认为,如果你不理解一个控件定义的模板部分的话,你是不能深入开发一个控件皮肤的。
创建自定义IE按钮皮肤
有了一定的背景知识后,我们现在开始创建一个IE按钮皮肤
第一步:添加RootElement,背景Rectangle和ContenPresenter
如果我们回头看看上面的表格,Button的ControlTemplate看起来在控件模板中有两个对象:一个名叫RootElement,另一个叫做FocusVisualElement。RootElement是一个包含控件中所有可视化对象的容器,因此我们从这里开始。我已经在我的Button上使用了一个Grid对象来进行对象布局,Grid中包含的第一个子对象是一个填充浅灰色的Rectangle对象(支持原生圆角效果)。然后添加了一个ContentPresenter用来处理Button的内容部分的效果。我已经使用了标记扩展语言绑定控件的属性到ContentPresenter上,(我现在仍然在研究ContentPresenter是什么,当我了解的差不多了时,我会用一篇文章写清楚的)
如果只有单个矩形块的话,按钮将是如下效果:
第二部:叠放半透明Rectangle添加玻璃效果
然后,我现在添加另一个Rectangle到模板中,它位于原来的背景Rectangle上面。第二个Rectangle负责提供不错的玻璃效果。为了这个效果,我在第二个Rectangle中加入了一些代码(除非我弄错了,Z-Order是对象添加时的顺序,因为先添加的是背景Rectangle,它的Z-Order就比较小)。我们并没有添加一张变透明的image,而是给Rectangle填充了渐变的笔刷(Gradient bursh)--顶端亮,底端较暗。
当第二个Rectangle添加到模板中了后,按钮显示如下:
第三步:添加FocusVisualElment Rectangle
下面,我添加了另一个Rectangle到模板中,用来在按钮获得焦点时,在按钮边框内侧,显示一个点状矩形框。另外,我是在开始那两个Rectangle后添加Rectangle的,因此它具有最高的Z-Order。当按钮获得焦点时,一个白色的点状边框将会应用在控件上。我没有看到文档中的特别说明,但是给按钮皮肤化的所有例子都将FocusVisualElement 首先置为Visibility,即Coppapsed state。我没有去验证这一点,但我相信当Button侦测到焦点时,它会更新Visibility值为Visible,让点状Rectangle显示出来。
第四步:在所有图形上添加一个Rectangle
最后,为了制作按钮的禁用效果,在模板的最后(它具有最高的Z-order),我们还需要添加另一个Rectangle。首先将Opacity值置为0,让其隐藏,当按钮进入Disabled State状态时,用StoryBoard更新Opacity值为1,将其显示做其他所有的Rectangle之上。
第五步:使用SotryBoard来制作状态转换动画
最后一步用来制作我们IE皮肤的动画效果,即前面提到过的四种按钮状态的动画。这里又一个简要的介绍,说明一下Button在转化状态时需要进行什么事项。
MouseOver State
Pressed State
- 改变背景Rectangle颜色为蓝色
- 将渐变Rectangle的Opacity设为0.7 (这将会使大部分蓝色显示出来)
- 加黑边框
Normal State
Disabled State
需要注意的是,当按钮在不同状态下改变的时候,我希望更新几个内嵌Rectangle的属性。为此,我在我的模板中添加了4个StoryBoard节点- 每个不同的Button状态对应一个不同的节点。MouseOver的StoryBoard是最简单的一个- 它当鼠标在RootElement上方时激活,该StoryBoard采用ColorAnimation对象改变背景Rectangle填充笔刷的颜色属性,由灰到蓝。下面是该StoryBoard的代码:
需要注意下列事项:
- StoryBoard的Key值是MouseOver State。Button控件通过名称来寻找这些项,因此你需要像上面那样输入一个名称。
- 我的动画效果的Duration设定为0:0:0 – 也就是动画立即开始,如果你想让它慢慢变蓝,你可以设定一个较小的值。例如0:0:0.5可以让按钮在1/2秒内从灰变成蓝色。
- ColorAnimation用来指定一个名叫Brush的对象,如果你回去看第一步,那是用来填充背景Rectangle的笔刷的名字。
剩余的动画采用同样的方式,TargetName属性指向需要动画效果的项。通过改变他们某一个属性值(如Opacity,StrokeThickness,Color等),让其具有动画效果。
最后完成所有工作时,我已经成功的将新皮肤移到了程序XAML文件的Resources部分中,就是这个皮肤让我的Button变成可爱的新IE皮肤模样…
当所有这些都编译运行后,看起来是下面这个样子的:
最后,我折叠了所有用来制作自定义样式的对象和资源代码,因此你能看到他们放在一起的时候是什么感觉。完整的皮肤大概有150行代码,不过考虑到它提供的灵活性,还算不错的。我使用Silverlight的时间还不长,但是我已经感觉到控件皮肤化将是我感兴趣的一个主题。
终于写完了,谢谢。