Silverlight 下苹果(Mac OS)风格按钮的实现

在 Silverlight 2 beta 2 下,可以通过 Style 和 ControlTemplate 对控件的观感 (look and feel) 进行定制。并且在最新的 Expression Blend 2.5 June 2008 preview 版本中,可以用可视化的方式来进行设计(一些细微的地方仍然要代码调整),这样就方便多了。

本文介绍如何实现一个 Mac 风格的按钮。先看一下效果:

mac_button.jpg

左边一排是普通按钮,右边一排是 Mac 风格的按钮。其中获得焦点的按钮在视觉上用一圈虚线框表示。

因为 Button 控件开放了一个 Template 属性,我们要做的就是创建一个 Style. 比如叫做 MacButton,然后让按钮实例去套用这个 Style 即可:

< Button  Style =" {StaticResource MacButton} " />


而 Style 本质上是用来设置属性值的,Template 也是一个特殊的属性,它是 CcontrolTemplate. 在 Xaml 语法中可以这样写:

    < Setter  Property ="Template" >
    
< Setter.Value >
     
< ControlTemplate  TargetType ="Button" >
        dot.gif
     
</ ControlTemplate >
   
</ Setter.Value >
  
</ Setter >


所有控件的真实内容都在上面的 "...." 中定义即可。

再介绍一点基础知识
==========================
控件契约 (Control Contract)

Silverlight 2 beta 2 内置的每一种控件都有其自身的契约,我们在为控件创建 Style 时必须符合此契约的要求才能生效。

一般包含3个方面:

1) 属性
2) 控件可能用到的 UIElement. (用 TemplatePartAttribute 标记)
3) VisualState 对象 (用 TemplateVisualState 在控件上标记)

这里 VisualState 很有意思,表示控件的状态。并且状态可以分组,同一个组内的状态可以互相迁移。
比如 Button 有两组状态:一组是“普通”、“鼠标悬停”、“按下”、“禁用”;而另一组是“获得焦点”、“失去焦点”。
两组状态可以同时生效,互不影响。

在 ControlTemplate 内,我们可以定义各个状态以及状态间迁移时的动画 (用一个 Storyboard 定义),以及状态之间迁移花费多少时间 (Duration) 等。

Button 控件的契约如下:

[TemplateVisualState(Name  =   " Normal " , GroupName  =   " CommonStates " )]
[TemplateVisualState(Name 
=   " MouseOver " , GroupName  =   " CommonStates " )]
[TemplateVisualState(Name 
=   " Pressed " , GroupName  =   " CommonStates " )]
[TemplateVisualState(Name 
=   " Disabled " , GroupName  =   " CommonStates " )]
[TemplateVisualState(Name 
=   " Unfocused " , GroupName  =   " FocusStates " )]
[TemplateVisualState(Name 
=   " Focused " , GroupName  =   " FocusStates " )]
public   class  Button : Control
{
    
public   static   readonly  DependencyProperty BackgroundProperty;
    
public   static   readonly  DependencyProperty BorderBrushProperty;
    
public   static   readonly  DependencyProperty BorderThicknessProperty;
    
public   static   readonly  DependencyProperty ContentProperty;
    
public   static   readonly  DependencyProperty ContentTemplateProperty;
    
public   static   readonly  DependencyProperty FontFamilyProperty;
    
public   static   readonly  DependencyProperty FontSizeProperty;
    
public   static   readonly  DependencyProperty FontStretchProperty;
    
public   static   readonly  DependencyProperty FontStyleProperty;
    
public   static   readonly  DependencyProperty FontWeightProperty;
    
public   static   readonly  DependencyProperty ForegroundProperty;
    
public   static   readonly  DependencyProperty HorizontalContentAlignmentProperty;
    
public   static   readonly  DependencyProperty PaddingProperty;
    
public   static   readonly  DependencyProperty TextAlignmentProperty;
    
public   static   readonly  DependencyProperty TextDecorationsProperty;
    
public   static   readonly  DependencyProperty TextWrappingProperty;
    
public   static   readonly  DependencyProperty VerticalContentAlignmentProperty;

    
public  Brush Background {  get set ; }
    
public  Brush BorderBrush {  get set ; }
    
public  Thickness BorderThickness {  get set ; }
    
public   object  Content {  get set ; }
    
public  DataTemplate ContentTemplate {  get set ; }
    
public  FontFamily FontFamily {  get set ; }
    
public   double  FontSize {  get set ; }
    
public  FontStretch FontStretch {  get set ; }
    
public  FontStyle FontStyle {  get set ; }
    
public  FontWeight FontWeight {  get set ; }
    
public  Brush Foreground {  get set ; }
    
public  HorizontalAlignment HorizontalContentAlignment {  get set ; }
    
public  Thickness Padding {  get set ; }
    
public  TextAlignment TextAlignment {  get set ; }
    
public  TextDecorationCollection TextDecorations {  get set ; }
    
public  TextWrapping TextWrapping {  get set ; }
    
public  VerticalAlignment VerticalContentAlignment {  get set ; }
}


关于动画的更多知识参考 Silverlight 2 beta 2 文档。

简单说一下创建这个样式的步骤:

在 ExpressionBlend 2.5 中,首先我们往界面上拖一个 Button. 然后在右键菜单中:
create_style.jpg

选择 "Edit a Copy" 后,就会自动创建一个指定名称的 Style, 其内容是 Button 的默认模板。
因为模板内容非常繁琐,我们如果自己全部手写很困难,所以我们选择在默认模板的基础上修改。

后续的步骤,主要是在模板的控件树中删除掉不必要的内容,并且修改一些画刷等设置,重新定义动画等等。不再一一详述,具体请看代码。
另外这里我发现的一个特别需要注意的问题,就是 Button 默认生成的模板中,其 Unfocused 状态的动画必须删除掉才行:

          < vsm:VisualState  x:Name ="Unfocused" >
          
< Storyboard >
             
<!--  自动生成的模板下这里是有内容的,要删掉! -->
          
</ Storyboard >
         
</ vsm:VisualState >


否则,你会发现套用了该风格的所有按钮都无法失去焦点,页面上会出现若干个按钮同时为高亮状态的滑稽情景

下面是例子的全部代码:

< UserControl
    
xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d
="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc
="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable
="d"
    x:Class
="SilverlightApplication6.Page"
    d:DesignWidth
="640"  d:DesignHeight ="480"  xmlns:vsm ="clr-namespace:System.Windows;assembly=System.Windows" >

    
< UserControl.Resources >
        
< vsm:Style  x:Key ="MacButton"  TargetType ="Button" >
            
< vsm:Setter  Property ="IsEnabled"  Value ="true" />
            
< vsm:Setter  Property ="IsTabStop"  Value ="true" />
            
< vsm:Setter  Property ="Background"  Value ="#FF003255" />
            
< vsm:Setter  Property ="Foreground"  Value ="#FF313131" />
            
< vsm:Setter  Property ="MinWidth"  Value ="5" />
            
< vsm:Setter  Property ="MinHeight"  Value ="5" />
            
< vsm:Setter  Property ="Margin"  Value ="0" />
            
< vsm:Setter  Property ="HorizontalContentAlignment"  Value ="Center" />
            
< vsm:Setter  Property ="VerticalContentAlignment"  Value ="Center" />
            
< vsm:Setter  Property ="Cursor"  Value ="Arrow" />
            
< vsm:Setter  Property ="TextAlignment"  Value ="Left" />
            
< vsm:Setter  Property ="TextWrapping"  Value ="NoWrap" />
            
< vsm:Setter  Property ="FontSize"  Value ="11" />
            
< vsm:Setter  Property ="Template" >
                
< vsm:Setter.Value >
                    
< ControlTemplate  TargetType ="Button" >
                        
< Grid >
                            
< Grid.Resources >
                                
< Color  x:Key ="LinearBevelLightStartColor" > #FFFFFFFF </ Color >
                                
< Color  x:Key ="LinearBevelLightEndColor" > #F4E2E0E0 </ Color >
                                
< Color  x:Key ="LinearBevelDarkStartColor" > #E0E5E5E5 </ Color >
                                
< Color  x:Key ="LinearBevelDarkEndColor" > #B2FFFFFF </ Color >
                                
< Color  x:Key ="MouseOverLinearBevelDarkEndColor" > #7FFC1717 </ Color >
                                
< Color  x:Key ="HoverLinearBevelLightStartColor" > #FCFFFFFF </ Color >
                                
< Color  x:Key ="HoverLinearBevelLightEndColor" > #EAFFFFFF </ Color >
                                
< Color  x:Key ="HoverLinearBevelDarkStartColor" > #D8FFFFFF </ Color >
                                
< Color  x:Key ="HoverLinearBevelDarkEndColor" > #4CFFFFFF </ Color >
                                
< Color  x:Key ="CurvedBevelFillStartColor" > #B3FFFFFF </ Color >
                                
< Color  x:Key ="CurvedBevelFillEndColor" > #3CFFFFFF </ Color >
                                
< SolidColorBrush  x:Key ="BorderBrush"  Color ="#FF5E5E5E" />
                                
< SolidColorBrush  x:Key ="AccentBrush"  Color ="#FF000000" />
                                
< SolidColorBrush  x:Key ="DisabledBrush"  Color ="#A5FFFFFF" />
                                
< LinearGradientBrush  x:Key ="FocusedStrokeBrush"  EndPoint ="0.5,1"  StartPoint ="0.5,0" >
                                    
< GradientStop  Color ="#B2FFFFFF"  Offset ="0" />
                                    
< GradientStop  Color ="#51FFFFFF"  Offset ="1" />
                                    
< GradientStop  Color ="#66FFFFFF"  Offset ="0.325" />
                                    
< GradientStop  Color ="#1EFFFFFF"  Offset ="0.325" />
                                
</ LinearGradientBrush >
                            
</ Grid.Resources >
                            
< vsm:VisualStateManager.VisualStateGroups >
                                
< vsm:VisualStateGroup  x:Name ="CommonStates" >
                                    
< vsm:VisualStateGroup.Transitions >
                                        
< vsm:VisualTransition  Duration ="00:00:00.2000000"  To ="MouseOver" />
                                        
< vsm:VisualTransition  Duration ="0:0:0.1"  To ="Pressed" />
                                        
< vsm:VisualTransition  Duration ="00:00:00.2000000"  From ="Normal"  To ="MouseOver" />
                                    
</ vsm:VisualStateGroup.Transitions >
                                    
< vsm:VisualState  x:Name ="Normal" >
                                        
< Storyboard />
                                    
</ vsm:VisualState >
                                    
< vsm:VisualState  x:Name ="MouseOver" >
                                        
< Storyboard >
                                            
< ColorAnimationUsingKeyFrames 
                                                
Duration ="0"  
                                                Storyboard.TargetName
="BackgroundGradient"  
                                                Storyboard.TargetProperty
="(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)" >
                                                
< SplineColorKeyFrame  KeyTime ="0"  Value ="#c8d5ed"   />
                                            
</ ColorAnimationUsingKeyFrames >
                                            
< ColorAnimationUsingKeyFrames 
                                                
Duration ="0"  
                                                Storyboard.TargetName
="BackgroundGradient"  
                                                Storyboard.TargetProperty
="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" >
                                                
< SplineColorKeyFrame  KeyTime ="0"  Value ="#97c2ee"   />
                                            
</ ColorAnimationUsingKeyFrames >
                                            
< ColorAnimationUsingKeyFrames 
                                                
Duration ="0"  
                                                Storyboard.TargetName
="BackgroundGradient"  
                                                Storyboard.TargetProperty
="(Shape.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" >
                                                
< SplineColorKeyFrame  KeyTime ="0"  Value ="#6eadee"   />
                                            
</ ColorAnimationUsingKeyFrames >
                                            
< ColorAnimationUsingKeyFrames 
                                                
Duration ="0"  
                                                Storyboard.TargetName
="BackgroundGradient"  
                                                Storyboard.TargetProperty
="(Shape.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)" >
                                                
< SplineColorKeyFrame  KeyTime ="0"  Value ="#aff9ff"   />
                                            
</ ColorAnimationUsingKeyFrames >
                                        
</ Storyboard >
                                    
</ vsm:VisualState >
                                    
< vsm:VisualState  x:Name ="Pressed" >
                                        
< Storyboard >
                                            
< DoubleAnimationUsingKeyFrames  Duration ="0"  Storyboard.TargetName ="BackgroundGradient"  Storyboard.TargetProperty ="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Offset)" >
                                                
< SplineDoubleKeyFrame  KeyTime ="0"  Value =".2" />
                                            
</ DoubleAnimationUsingKeyFrames >
                                            
< ColorAnimationUsingKeyFrames 
                                                
Duration ="0"  
                                                Storyboard.TargetName
="BackgroundGradient"  
                                                Storyboard.TargetProperty
="(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)" >
                                                
< SplineColorKeyFrame  KeyTime ="0"  Value ="#bac5e8"   />
                                            
</ ColorAnimationUsingKeyFrames >
                                            
< ColorAnimationUsingKeyFrames 
                                                
Duration ="0"  
                                                Storyboard.TargetName
="BackgroundGradient"  
                                                Storyboard.TargetProperty
="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" >
                                                
< SplineColorKeyFrame  KeyTime ="0"  Value ="#7bb2e9"   />
                                            
</ ColorAnimationUsingKeyFrames >
                                            
< ColorAnimationUsingKeyFrames 
                                                
Duration ="0"  
                                                Storyboard.TargetName
="BackgroundGradient"  
                                                Storyboard.TargetProperty
="(Shape.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" >
                                                
< SplineColorKeyFrame  KeyTime ="0"  Value ="#4d9ae7"   />
                                            
</ ColorAnimationUsingKeyFrames >
                                            
< ColorAnimationUsingKeyFrames 
                                                
Duration ="0"  
                                                Storyboard.TargetName
="BackgroundGradient"  
                                                Storyboard.TargetProperty
="(Shape.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)" >
                                                
< SplineColorKeyFrame  KeyTime ="0"  Value ="#85eaff"   />
                                            
</ ColorAnimationUsingKeyFrames >
                                        
</ Storyboard >
                                    
</ vsm:VisualState >
                                    
< vsm:VisualState  x:Name ="Disabled" >
                                        
< Storyboard >
                                            
< DoubleAnimationUsingKeyFrames  Duration ="0"  Storyboard.TargetName ="DisabledVisual"  Storyboard.TargetProperty ="Opacity" >
                                                
< SplineDoubleKeyFrame  KeyTime ="0"  Value ="1" />
                                            
</ DoubleAnimationUsingKeyFrames >
                                        
</ Storyboard >
                                    
</ vsm:VisualState >
                                
</ vsm:VisualStateGroup >
                                
< vsm:VisualStateGroup  x:Name ="FocusStates" >
                                    
< vsm:VisualState  x:Name ="Focused" >
                                        
< Storyboard >
                                            
< ObjectAnimationUsingKeyFrames  Duration ="0"  Storyboard.TargetName ="FocusVisual"  Storyboard.TargetProperty ="Visibility" >
                                                
< DiscreteObjectKeyFrame  KeyTime ="0" >
                                                    
< DiscreteObjectKeyFrame.Value >
                                                        
< vsm:Visibility > Visible </ vsm:Visibility >
                                                    
</ DiscreteObjectKeyFrame.Value >
                                                
</ DiscreteObjectKeyFrame >
                                            
</ ObjectAnimationUsingKeyFrames >
                                        
</ Storyboard >
                                    
</ vsm:VisualState >
                                    
< vsm:VisualState  x:Name ="Unfocused" >
                                        
< Storyboard >
                                        
</ Storyboard >
                                    
</ vsm:VisualState >
                                
</ vsm:VisualStateGroup >
                            
</ vsm:VisualStateManager.VisualStateGroups >
                            
< Rectangle  x:Name ="Background"  Fill =" {TemplateBinding Background} "  RadiusX ="11"  RadiusY ="11" />
                            
< Rectangle  x:Name ="BackgroundGradient"  Stroke =" {StaticResource BorderBrush} "  StrokeThickness ="1"  RadiusX ="11"  RadiusY ="11"  Margin ="-1,-1,-1,-1" >
                                
< Rectangle.Fill >
                                    
< LinearGradientBrush  EndPoint ="0.7,1"  StartPoint ="0.7,0" >
                                        
< GradientStop  Color =" {StaticResource LinearBevelLightStartColor} "  Offset ="0" />
                                        
< GradientStop  Color =" {StaticResource LinearBevelLightEndColor} "  Offset ="0.326" />
                                        
< GradientStop  Color =" {StaticResource LinearBevelDarkStartColor} "  Offset ="0.344" />
                                        
< GradientStop  Color ="#FFFFFFFF"  Offset ="0.786" />
                                    
</ LinearGradientBrush >
                                
</ Rectangle.Fill >
                            
</ Rectangle >
                            
< Grid  x:Name ="FocusVisual"  Visibility ="Collapsed" >
                                
< Rectangle  Margin ="-2,1,-2,-2"  Stroke =" {StaticResource AccentBrush} "  StrokeThickness ="1"  StrokeDashArray ="1.5 1.5"  RadiusX ="3"  RadiusY ="3"   />
                            
</ Grid >
                            
< ContentPresenter  Margin ="4,5,4,4"  HorizontalContentAlignment =" {TemplateBinding HorizontalContentAlignment} "  Padding =" {TemplateBinding Padding} "  VerticalContentAlignment =" {TemplateBinding VerticalContentAlignment} "  Content =" {TemplateBinding Content} "  ContentTemplate =" {TemplateBinding ContentTemplate} "  TextAlignment =" {TemplateBinding TextAlignment} "  TextDecorations =" {TemplateBinding TextDecorations} "  TextWrapping =" {TemplateBinding TextWrapping} " />
                            
< Rectangle  x:Name ="DisabledVisual"  IsHitTestVisible ="false"  Opacity ="0"  Fill =" {StaticResource DisabledBrush} "  RadiusX ="4"  RadiusY ="4" />
                        
</ Grid >
                    
</ ControlTemplate >
                
</ vsm:Setter.Value >
            
</ vsm:Setter >
        
</ vsm:Style >
    
</ UserControl.Resources >

    
< Canvas  x:Name ="LayoutRoot"  Background ="White" >
        
< Button  x:Name ="Button1"  TabIndex ="0"  Height ="23"  HorizontalAlignment ="Left"  Margin ="0,0,0,0"  VerticalAlignment ="Top"  Width ="63"  Content ="Button1"  Canvas.Top ="30"  Canvas.Left ="44" />
        
< Button  x:Name ="Button2"  TabIndex ="1"  Height ="37"  HorizontalAlignment ="Left"  Margin ="0,0,0,0"  VerticalAlignment ="Top"  Width ="120"  Content ="Button2"  Canvas.Top ="68.5"  Canvas.Left ="43" />
        
< Button  x:Name ="Button3"  TabIndex ="2"  HorizontalAlignment ="Left"  Margin ="0,0,0,0"  VerticalAlignment ="Top"  Content ="Button3"  Width ="195"  Height ="65"  Canvas.Top ="122"  Canvas.Left ="43" />
        
< Button  x:Name ="Button4"  TabIndex ="3"  HorizontalAlignment ="Left"  Margin ="0,0,0,0"  VerticalAlignment ="Stretch"  Content ="Button4"  IsEnabled ="False"  Width ="195"  Canvas.Top ="209"  Canvas.Left ="43"  Height ="65" />
        
< Button  x:Name ="Button5"  TabIndex ="4"  Height ="23"  HorizontalAlignment ="Right"  Margin ="0,0,0,0"  VerticalAlignment ="Top"  Content ="Button1"  Width ="63"  Canvas.Top ="30"  Canvas.Left ="269"  Style =" {StaticResource MacButton} " />
        
< Button  x:Name ="Button6"  TabIndex ="5"  Height ="37"  HorizontalAlignment ="Right"  Margin ="0,0,0,0"  VerticalAlignment ="Top"  Content ="Button2"  Width ="120"  Canvas.Top ="68.5"  Canvas.Left ="269"  Style =" {StaticResource MacButton} " />
        
< Button  x:Name ="Button7"  TabIndex ="6"   HorizontalAlignment ="Stretch"  Margin ="0,0,0,0"  VerticalAlignment ="Top"  Content ="Button3"  Height ="65"  Canvas.Top ="122"  Canvas.Left ="269"  Width ="195"  Style =" {StaticResource MacButton} " />
        
< Button  x:Name ="Button8"  TabIndex ="7"  HorizontalAlignment ="Stretch"  Margin ="0,0,0,0"  VerticalAlignment ="Stretch"  Content ="Button4"  IsEnabled ="False"  Canvas.Top ="209"  Canvas.Left ="269"  Width ="195"  Height ="65"  Style =" {StaticResource MacButton} " />
    
</ Canvas >
</ UserControl >



关于 Expression Blend 的使用教程,这里有几个参考:

http://blog.joycode.com/scottgu/archive/2008/06/09/115138.aspx
http://timheuer.com/blog/archive/2008/06/04/skinning-silverlight-controls-made-easier.aspx
http://timheuer.com/blog/archive/2008/06/04/silverlight-introduces-visual-state-manager-vsm.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值