原文:UWP Button添加圆角阴影(一)

众所周知,17763之前的UWP控件,大部分是没有圆角属性的;而阴影也只有17763中的ThemeShadow可以直接在xaml中使用,之前的版本只能用DropShadow,用法极其别扭。

本文就给出一个虽然很别扭,但是效果还不错的,比较通用的圆角+阴影的方案。

概念

我们先思考一下,用户感知到的圆角按钮,到底是个什么东西。

任何一个按钮,不外乎Background和Content两部分,用户可以从Content中获取到按钮的信息,而按钮的形状,在没有Border的情况下,用户对Button形状最直观的感受就是Background!

也就是说,我们只要让一个按钮的Background是一个圆角矩形,他在大多数情况下,就是一个圆角按钮!

按照这个思路,我们可以在17763之前的UWP,也就是大多数控件都没有CornerRadius这个属性的环境里,造出许多圆角的控件。

圆角

从Generic.xaml中,把Button的Style复制出一份,删除没有必要的东西,就成了下面的样子:

<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,是这样的:

<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结构应该是这个模样的:

<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里,不然是不会生效的。

完整版是这样的,有改颜色的需求可以撸一下VisualState里的颜色:

<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>

UWP Button添加圆角阴影(一)的更多相关文章

  1. UWP Button添加圆角阴影(三)

    原文:UWP Button添加圆角阴影(三) Composition DropShadow是CompositionAPI中的东西,使用Storyboard设置某个属性,就是频繁的触发put_xxx() ...

  2. UWP Button添加圆角阴影(二)

    原文:UWP Button添加圆角阴影(二) 阴影 对于阴影呢,WindowsCommunityToolkit中已经有封装好的DropShadowPanel啦,只要引用Microsoft.Toolki ...

  3. 为input输入框添加圆角并去除阴影

    <input type="text" name="bianhao" value="" placeholder="请输入商品编 ...

  4. CSS3圆角,阴影,透明

    CSS实现圆角,阴影,透明的方法很多,传统的方法都比较复杂,用CSS3就方便很多了,虽然现在各浏览器对CSS3的支持还不是很好,但不久的将来CSS3就会普及. 1.圆角 CSS3实现圆角有两种方法. ...

  5. HackTwelve 为背景添加圆角边框

    1.概要:     ShapeDrawable是一个为UI控件添加特效的好工具.这个技巧适用于那些可以添加背景的控件 2.添加圆角边框其实就是添加的背景那里不是直接添加图片,而是添加一个XML文件即可 ...

  6. iOS给UIimage添加圆角的两种方式

    众所周知,给图片添加圆角有CALayer的cornerRadius, 比如: 最直接的方法: imgView.layer.cornerRadius1=110;   imgView.clipsToBou ...

  7. iOS 在xib或storyboard里为控件添加圆角、外框和外框颜色

    如果要在xib和storyboard里为控件添加圆角和外框宽度,只要这样做就可以 layer.borderWidth     设置外框宽度属性 layer.cornerRadius    设置圆角属性 ...

  8. iOS 高效添加圆角效果实战讲解

    圆角(RounderCorner)是一种很常见的视图效果,相比于直角,它更加柔和优美,易于接受.但很多人并不清楚如何设置圆角的正确方式和原理.设置圆角会带来一定的性能损耗,如何提高性能是另一个需要重点 ...

  9. iOS在xib或storyboard里为控件添加圆角、外框和外框颜色

    如果要在xib和storyboard里为控件添加圆角和外框宽度,只要这样做就可以: layer.borderWidth 设置外框宽度属性 layer.cornerRadius 设置圆角属性 只要为属性 ...

随机推荐

  1. jquery查找frameset框架内iframe的元素

    老系统还幸存有过时的frameset框架,维护升级工作需要对其内部的iframe中的元素进行相关操作.使用jquery查找子iframe页面内的元素时,总找不到目标元素.后来发现少了contents ...

  2. Sophus链接错误

    错误指示如下: CMakeFiles/run_vo.dir/run_vo.cpp.o: In function `main': run_vo.cpp:(.text.startup+0x1086): u ...

  3. 销售vs技术岗,做技术的方法思考

    销售甚至比技术岗位挣得还多,当然,做技术的比较好的拿到的自然也多. 我在想个问题,技术的天然优势是可以不断地积累,包括写code,写博客,做流程,完善流程,自动化流程,或者把某些工作流程化,自动化,托 ...

  4. 计算器的改良(NOIP2000)

    题目链接:计算器的改良 这道题,不是很难,但代码也短不到哪去. 我们这里决定采取边读入边计算的方法,因为题目没有明确说式子有多长. 我们需要计算什么? 我们需要知道等号两边未知数的系数和常数项即可. ...

  5. Codeforces Round #538 (Div. 2) C 数论 + 求b进制后缀零

    https://codeforces.com/contest/1114/problem/C 题意 给你一个数n(<=1e8),要你求出n!在b进制下的后缀零个数(b<=1e12) 题解 a ...

  6. 移动赋值运算符(c++11)

    1.概念 1)移动赋值运算符是一个重载的赋值运算符,参数为自身类的右值引用,返回值自身类的左值引用,由于不抛出任何异常,用noexcept指定(如果定义在类的外面,那么定义也要用noexcept指定) ...

  7. Day1-python基础-变量常量

    不积跬步无以至千里 补充上一节字符串的内容: 字符串格式化输出: name = input("name>>") print("My name is %s&qu ...

  8. LINQ 语法

    语言集成查询 (LINQ) 是 Visual Studio 2008 和 .NET Framework 3.5 版中一项突破性的创新,它在对象领域和数据领域之间架起了一座桥梁. 传统上,针对数据的查询 ...

  9. 模板引擎(smarty)知识点总结四

    /*   smarty 引入对象 */ require_once 'libs/Smarty.class.php';  require 'MySmarty.class.php';  $msma = ne ...

  10. VHDL基础1

    Description Structure 一个可综合的VHDL描述中一般由3部分组成:LIBRARY declarations.ENTITY.ARCHITECTURE Library(库)用来设计重 ...