原文:UWP Button添加圆角阴影(一)
众所周知,17763以前的UWP控件,大部分是没有圆角属性的;而阴影也只有17763中的ThemeShadow能够直接在xaml中使用,以前的版本只能用DropShadow,用法极其别扭。
本文就给出一个虽然很别扭,可是效果还不错的,比较通用的圆角+阴影的方案。html
咱们先思考一下,用户感知到的圆角按钮,究竟是个什么东西。
任何一个按钮,不外乎Background和Content两部分,用户能够从Content中获取到按钮的信息,而按钮的形状,在没有Border的状况下,用户对Button形状最直观的感觉就是Background!
也就是说,咱们只要让一个按钮的Background是一个圆角矩形,他在大多数状况下,就是一个圆角按钮!
按照这个思路,咱们能够在17763以前的UWP,也就是大多数控件都没有CornerRadius这个属性的环境里,造出许多圆角的控件。布局
从Generic.xaml中,把Button的Style复制出一份,删除没有必要的东西,就成了下面的样子:spa
<Style TargetType="Button"> <Setter Property="Background" Value="{ThemeResource ButtonBackground}" /> <Setter Property="Foreground" Value="{ThemeResource ButtonForeground}" /> <Setter Property="BorderBrush" Value="{ThemeResource ButtonBorderBrush}" /> <Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" /> <Setter Property="Padding" Value="8,4,8,4" /> <Setter Property="HorizontalAlignment" Value="Left" /> <Setter Property="VerticalAlignment" Value="Center" /> <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" /> <Setter Property="FontWeight" Value="Normal" /> <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" /> <Setter Property="UseSystemFocusVisuals" Value="True" /> <Setter Property="FocusVisualMargin" Value="-3" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid x:Name="RootGrid" Background="{TemplateBinding Background}"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal" /> <VisualState x:Name="PointerOver"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundPointerOver}" /> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBorderBrushPointerOver}" /> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonForegroundPointerOver}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Pressed"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundPressed}" /> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBorderBrushPressed}" /> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonForegroundPressed}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Disabled"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="RootGrid" Storyboard.TargetProperty="Background"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundDisabled}" /> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBorderBrushDisabled}" /> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonForegroundDisabled}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <ContentPresenter x:Name="ContentPresenter" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Content="{TemplateBinding Content}" ContentTransitions="{TemplateBinding ContentTransitions}" ContentTemplate="{TemplateBinding ContentTemplate}" Padding="{TemplateBinding Padding}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" AutomationProperties.AccessibilityView="Raw" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
其实大部份内容不重要,咱们先来分析一下这个结构,删掉全部布局以外的属性,就剩下很单纯的Grid装着一个ContentPresenter,是这样的:code
<Style TargetType="Button" x:Key="CornerRadiusShadowButtonStyle"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid x:Name="RootGrid"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal" /> <VisualState x:Name="PointerOver"/> <VisualState x:Name="Pressed"/> <VisualState x:Name="Disabled"/> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <ContentPresenter x:Name="ContentPresenter"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
圆角的属性呢,只有在Border,Rectangle中有,固然你要是足够闲,也能够撸Path...
结果很明显了对吧,这就是圆角的两个实现方式,ContentPresenter外面套Border,或者后面放Rectangle当Background。
Border的方式呢,优缺点都很明显。
优势是Border的CornerRadius能够分别设置四个角的半径,并且能够设置给Button设置Border相关属性的时候,让Border的相关属性按照咱们定义的Border的形状去绘制。
缺点呢,就是在PC端,Border设置CornerRadius后,会Clip掉内容超出Border的部分(因为17763以前的UWP没有GeometryClip,因此这个圆角Clip也算是个特性...),其实咱们能够利用这个特性作圆形头像,圆形播放器啥的...
而Rectangle呢,虽然不能分别设置四个角的半径,可是能够分别设置X的半径和Y的半径...提及来感受好诡异...
出我的喜爱,还有我不喜欢画Border的风格,就选Rectangle的解决方案了。修改完成以后的Style结构应该是这个模样的:orm
<Style TargetType="Button" x:Key="CornerRadiusShadowButtonStyle"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid x:Name="RootGrid"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal" /> <VisualState x:Name="PointerOver"/> <VisualState x:Name="Pressed"/> <VisualState x:Name="Disabled"/> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Rectangle x:Name="Background" /> <ContentPresenter x:Name="ContentPresenter"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>
注意事项:VisualStateManager必须放到控件根元素内,好比Page的VisualStateManager就必须放到Page内第一个元素好比Grid或者StackPanel里,否则是不会生效的。
xml
完整版是这样的,有改颜色的需求能够撸一下VisualState里的颜色:htm
<Style TargetType="Button" x:Key="CornerRadiusShadowButtonStyle"> <Setter Property="Background" Value="{ThemeResource ButtonBackground}"/> <Setter Property="Foreground" Value="{ThemeResource ButtonForeground}"/> <Setter Property="BorderBrush" Value="Transparent" /> <Setter Property="BorderThickness" Value="0" /> <Setter Property="Padding" Value="20,10,20,10" /> <Setter Property="HorizontalAlignment" Value="Left" /> <Setter Property="VerticalAlignment" Value="Center" /> <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" /> <Setter Property="FontWeight" Value="Normal" /> <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" /> <Setter Property="UseSystemFocusVisuals" Value="True" /> <Setter Property="FocusVisualMargin" Value="-3" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Grid x:Name="RootGrid"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal" /> <VisualState x:Name="PointerOver"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Background" Storyboard.TargetProperty="Fill"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundPointerOver}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Pressed"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Background" Storyboard.TargetProperty="Fill"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundPressed}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Disabled"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Background" Storyboard.TargetProperty="Fill"> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundDisabled}" /> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Rectangle x:Name="Background" Fill="{TemplateBinding Background}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" RadiusX="5" RadiusY="5" /> <ContentPresenter x:Name="ContentPresenter" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Content="{TemplateBinding Content}" ContentTransitions="{TemplateBinding ContentTransitions}" ContentTemplate="{TemplateBinding ContentTemplate}" Padding="{TemplateBinding Padding}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" AutomationProperties.AccessibilityView="Raw" /> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style>