65.9K
CodeProject 正在变化。 阅读更多。
Home

圆形玻璃 WPF 按钮的样式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.99/5 (111投票s)

2009年1月5日

CPOL

5分钟阅读

viewsIcon

409185

downloadIcon

19924

为 WPF 按钮设置类似 Windows Vista 的玻璃效果样式

glassbuttons/main.jpg

引言

自安装 Windows Vista 以来,我一直很喜欢它圆形的玻璃按钮风格。WPF 的强大之处在于它允许对几乎任何用户界面组件进行样式设置。在 Microsoft Expression Blend 中摸索了一段时间后,我设计了一种样式,我认为它看起来非常接近我想要的风格。所以,我想与大家分享。如前所述,我使用了 Microsoft Expression Blend 来绘制按钮,我还使用了出色的 XAML 编辑器 - Kaxaml - 进行了一些微调。

概述

玻璃按钮样式包含三个层,它们共同构成了玻璃效果,还有一个 ContentPresenter 用于容纳按钮的内容。所有层都堆叠在网格中。还定义了用于鼠标悬停和按下状态的触发器,以增加一些交互性。

我定义了一个带有样式的键控窗口资源,但也可以删除该键以将样式应用于窗口中的所有按钮。

好了,让我们逐层解析,看看这种在 Microsoft 产品中广泛使用的酷炫玻璃外观是如何实现的。

按钮层

背景层

第一层是一个椭圆,它实际上是绘制反射层和折射层的画布。没什么特别的,椭圆的填充颜色绑定到按钮的 Background 属性。

这是 Blend 工作区中带有深蓝色椭圆的截图

glassbuttons/layer1.png

<!-- Background Layer -->
<Ellipse Fill="{TemplateBinding Background}"/>

折射层

第二层模拟了从按钮顶部到底部的光线折射。之所以在反射层之前绘制这一层,是因为反射层必须在按钮椭圆的中间某个位置有一个硬边缘,才能获得闪亮的玻璃外观。这一层实际上是另一个椭圆,但这次,我们将使用从白到透明的径向渐变填充,以模仿光线折射。渐变从底部中间开始,到椭圆的顶部中间结束;但是,为了降低折射高光的强度,渐变从椭圆底部边缘的下方开始。这在截图和下面的 XAML 代码中可以清楚地看到。

glassbuttons/layer2.png

<!-- Refraction Layer -->
<Ellipse x:Name="RefractionLayer">
  <Ellipse.Fill>
  <RadialGradientBrush GradientOrigin="0.496,1.052">
    <RadialGradientBrush.RelativeTransform>
      <TransformGroup>
        <ScaleTransform CenterX="0.5" 
          CenterY="0.5" ScaleX="1.5" ScaleY="1.5"/>
        <TranslateTransform X="0.02" Y="0.3"/>
      </TransformGroup>
    </RadialGradientBrush.RelativeTransform>
    <GradientStop Offset="1" Color="#00000000"/>
    <GradientStop Offset="0.4" Color="#FFFFFFFF"/>
    </RadialGradientBrush>
  </Ellipse.Fill>
</Ellipse>

反射层

第三层是光线反射层,这实际上是效果中最难的部分。问题在于,无法使用任何标准形状来绘制反射。因此,我决定使用 Path 来绘制反射区域。当然,也可以手动绘制 Path,但老实说,这并不是什么令人愉快的事情(除非您是艺术家并且拥有漂亮的数字化仪)。无论如何,我在 MS Blend 中又绘制了一个椭圆并将其转换为 Path,然后我只是摆弄 Path 的 Bezier 点以获得最终的平滑路径。Path 的好处是,您可以像对待 Ellipse 或 Rectangle 等预定义形状图元一样,将渐变应用于复杂的 Path 对象。因此,为了获得闪亮的反射效果,我们需要一个从路径底部(按钮中间某处)到顶部边缘的从透明到白色的径向渐变填充。我想如果我是艺术家,我就可以正式化径向渐变的渐变停止点。但我不是艺术家,所以我一直在 Blend 中调整设置,直到获得满意的外观。而且,由于我们的按钮布局在网格中,我们应该设置 VerticalAlignment="Top",这样反射区域就不会出现在按钮的中心。

glassbuttons/layer3.png

<!-- Reflection Layer -->
<Path x:Name="ReflectionLayer" VerticalAlignment="Top" Stretch="Fill">
  <Path.RenderTransform>
    <ScaleTransform ScaleY="0.5" />
  </Path.RenderTransform>
  <Path.Data>
    <PathGeometry>
      <PathFigure IsClosed="True" StartPoint="98.999,45.499">
        <BezierSegment Point1="98.999,54.170" Point2="89.046,52.258" 
           Point3="85.502,51.029"/>
        <BezierSegment IsSmoothJoin="True" Point1="75.860,47.685" 
           Point2="69.111,45.196" Point3="50.167,45.196"/>
        <BezierSegment Point1="30.805,45.196" Point2="20.173,47.741" 
           Point3="10.665,51.363"/>
        <BezierSegment IsSmoothJoin="True" Point1="7.469,52.580" 
           Point2="1.000,53.252" Point3="1.000,44.999"/>
        <BezierSegment Point1="1.000,39.510" Point2="0.884,39.227" 
           Point3="2.519,34.286"/>
        <BezierSegment IsSmoothJoin="True" Point1="9.106,14.370" 
           Point2="27.875,0" Point3="50,0"/>
        <BezierSegment Point1="72.198,0" Point2="91.018,14.466" 
           Point3="97.546,34.485"/>
        <BezierSegment IsSmoothJoin="True" Point1="99.139,39.369" 
           Point2="98.999,40.084" Point3="98.999,45.499"/>
      </PathFigure>
    </PathGeometry>
  </Path.Data>
  <Path.Fill>
    <RadialGradientBrush GradientOrigin="0.498,0.526">
      <RadialGradientBrush.RelativeTransform>
        <TransformGroup>
          <ScaleTransform CenterX="0.5" 
            CenterY="0.5" ScaleX="1" ScaleY="1.997"/>
          <TranslateTransform X="0" Y="0.5"/>
        </TransformGroup>
      </RadialGradientBrush.RelativeTransform>
      <GradientStop Offset="1" Color="#FFFFFFFF"/>
      <GradientStop Offset="0.85" Color="#92FFFFFF"/>
      <GradientStop Offset="0" Color="#00000000"/>
    </RadialGradientBrush>
  </Path.Fill>
</Path>

最后,我在按钮中心添加了一个 ContentPresenter。一些实验表明,将内容区域向下移动 1 像素会使按钮看起来更漂亮,因此也为 ContentPresenter 应用了边距(请记住,由于内容区域在网格中居中,顶部的 2px 边距实际上是将其向下移动了一个像素)。

<!-- ContentPresenter -->
<ContentPresenter Margin="0,2,0,0" 
  HorizontalAlignment="Center" VerticalAlignment="Center"/>

好了,这是 Blend 工作区中玻璃按钮的最终外观

glassbuttons/final.png

添加一些交互性

现在,让我们为按钮添加一些交互性。

鼠标悬停效果

为了获得鼠标悬停效果,我们需要增加光源的强度。为此,让我们为 IsMouseOver 事件定义一个触发器,复制并粘贴反射层和折射层的渐变设置,并微调渐变 alpha 值和偏移量以突出按钮。对于折射层,我只是将径向渐变的源向上移动了一点;对于反射层,我将渐变停止点改为不那么透明的白色。

<Trigger Property="IsMouseOver" Value="True">
  <Setter TargetName="RefractionLayer" Property="Fill">
    <Setter.Value>
      <RadialGradientBrush GradientOrigin="0.496,1.052">
        <RadialGradientBrush.RelativeTransform>
          <TransformGroup>
            <ScaleTransform CenterX="0.5" CenterY="0.5" 
               ScaleX="1.5" ScaleY="1.5"/>
            <TranslateTransform X="0.02" Y="0.3"/>
          </TransformGroup>
        </RadialGradientBrush.RelativeTransform>
      <GradientStop Offset="1" Color="#00000000"/>
      <GradientStop Offset="0.45" Color="#FFFFFFFF"/>
      </RadialGradientBrush>
    </Setter.Value>
  </Setter>
  <Setter TargetName="ReflectionLayer" Property="Fill">
    <Setter.Value>
      <RadialGradientBrush GradientOrigin="0.498,0.526">
        <RadialGradientBrush.RelativeTransform>
          <TransformGroup>
            <ScaleTransform CenterX="0.5" CenterY="0.5" 
               ScaleX="1" ScaleY="1.997"/>
            <TranslateTransform X="0" Y="0.5"/>
          </TransformGroup>
        </RadialGradientBrush.RelativeTransform>
        <GradientStop Offset="1" Color="#FFFFFFFF"/>
        <GradientStop Offset="0.85" Color="#BBFFFFFF"/>
        <GradientStop Offset="0" Color="#00000000"/>
      </RadialGradientBrush>
    </Setter.Value>
  </Setter>
</Trigger>

鼠标按下效果

对于 IsPressed 事件,我们需要减少高光。所以,我们需要将反射层光源(径向渐变起点)稍微向下移动,对于反射层,我们需要使渐变停止点更透明,以调暗落在我们按钮上的光线。

<Trigger Property="IsPressed" Value="True">
  <Setter TargetName="RefractionLayer" Property="Fill">
    <Setter.Value>
      <RadialGradientBrush GradientOrigin="0.496,1.052">
        <RadialGradientBrush.RelativeTransform>
          <TransformGroup>
            <ScaleTransform CenterX="0.5" CenterY="0.5" 
               ScaleX="1.5" ScaleY="1.5"/>
            <TranslateTransform X="0.02" Y="0.3"/>
          </TransformGroup>
        </RadialGradientBrush.RelativeTransform>
        <GradientStop Offset="1" Color="#00000000"/>
        <GradientStop Offset="0.3" Color="#FFFFFFFF"/>
      </RadialGradientBrush>
    </Setter.Value>
  </Setter>
  <Setter TargetName="ReflectionLayer" Property="Fill">
    <Setter.Value>
      <RadialGradientBrush GradientOrigin="0.498,0.526">
        <RadialGradientBrush.RelativeTransform>
          <TransformGroup>
            <ScaleTransform CenterX="0.5" CenterY="0.5" 
               ScaleX="1" ScaleY="1.997"/>
            <TranslateTransform X="0" Y="0.5"/>
          </TransformGroup>
        </RadialGradientBrush.RelativeTransform>
        <GradientStop Offset="1" Color="#CCFFFFFF"/>
        <GradientStop Offset="0.85" Color="#66FFFFFF"/>
        <GradientStop Offset="0" Color="#00000000"/>
      </RadialGradientBrush>
    </Setter.Value>
  </Setter>
</Trigger>

同样,渐变停止点的数值是通过实验确定的,我无法给出任何精确的数值来获得玻璃外观。

Using the Code

要使用此样式,只需将 GlassButton.xaml 文件中定义的样式资源与您的 Window 或 Page 资源合并,并将按钮的样式设置为 {StaticResource GlassButton}。要设置按钮的颜色,请使用按钮的 Background 属性。

<Window x:Class="GlassButton.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Glass Buttons" Height="228" Width="272">

  <Window.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Resources\GlassButton.xaml"/>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Window.Resources>
  
  <Grid> 
    <Button Style="{StaticResource GlassButton}" Width="50" 
       Height="50" Background="#FF660707" Margin="10"/> 
  </Grid>
</Window>

这里有一个演示(您可以在文章附带的源代码中找到它),其中包含一些带有图标的按钮,以及一个漂亮的背景(借用了 Windows Vista 的壁纸)。

glassbuttons/main.jpg

历史

  • 2009 年 1 月 5 日:初始版本
© . All rights reserved.