在WPF(Windows Presentation Foundation)中,自定义控件的创建可以极大地提升应用程序的用户体验。本文将介绍如何创建一个具有日间和夜间模式切换功能的自定义控件,即ThemeSwitch按钮。这个控件可以通过NuGet包下载,并且可以在GitHub上找到相应的源代码和演示项目。
ThemeSwitch按钮是一个基于WPF ToggleButton的CustomControl。它作为一个多目标框架的免费库通过NuGet包分发,确保了与所有WPF版本的兼容性。它具有三种动画类型:ValueItem(DoubleAnimation)、ThickItem(ThicknessAnimation)和ColorItem(ColorAnimation)。
最初,这个按钮的灵感来自于一个YouTube视频,该视频展示了一个在Figma中设计的日间到夜间切换按钮。决定在WPF中复制这个概念,这是专长领域。最终的结果令人印象深刻,很高兴与他人分享。这个过程也给了机会传授编码技巧和建议给那些对WPF开发感兴趣或正在学习WPF开发的人。
通过软件工具的分析,发现这个按钮是一个从WPF ToggleButton派生的自定义变体。它包括代表日间(太阳/云朵)和夜间(月亮/繁星夜空)的元素。此外,加入了自定义动画以增强日间到夜间的过渡效果。
绘制特殊形状有多种方法,比如使用预先编辑的SVG图像或在Blend中使用Path工具。然而,在这个项目中,展示了如何在WPF项目中直接绘制所需的形状,而不需要任何外部设计软件。
对于日间模式,太阳可以使用椭圆形表示。云朵虽然看起来复杂,但实际上创建起来相当简单。为了实时查看绘图效果,在MainWindow中操作。绘制一个适当大小的圆形,并将其复制大约八次。这些圆形可以使用鼠标或箭头键定位,以实现类似云朵的外观。Visual Studio根据这些形状的位置生成相应的边距值,然后将其纳入App.xaml中。通过添加不同程度的透明度来创建云朵效果。
<Grid Width="60" Height="30">
<Ellipse Width="17" Height="17" Fill="#FFFFFF" Margin="26,1,16,12" />
<Ellipse Width="17" Height="17" Fill="#FFFFFF" Margin="38,1,5,12" />
<Ellipse Width="17" Height="17" Fill="#FFFFFF" Margin="10,12,33,1" />
<Ellipse Width="17" Height="17" Fill="#FFFFFF" Margin="23,12,20,1" />
<Ellipse Width="17" Height="17" Fill="#FFFFFF" Margin="32,13,11,0" />
<Ellipse Width="17" Height="17" Fill="#FFFFFF" Margin="43,8,0,5" />
<Ellipse Width="17" Height="17" Fill="#FFFFFF" Margin="16,3,27,10" />
<Ellipse Width="17" Height="17" Fill="#FFFFFF" Margin="5,2,38,11" />
<Ellipse Width="17" Height="17" Fill="#FFFFFF" Margin="0,8,43,5" />
</Grid>
夜间主题中的星空使用相同的方法创建。将星空的基本元素定义为矩形,通过调整透明度来实现亮度效果。将基本透明度设置为55,对于更亮的星星,简单地移除透明度。
<Rectangle Width="1" Height="1" Fill="#55FFFFFF" Margin="60,35,19,14" />
<Rectangle Width="1" Height="1" Fill="#FFFFFF" Margin="36,6,43,43" />
为了生动地展示按钮的切换效果,为每个元素添加了动画。这些动画是通过jamesnet.WPF Nuget包实现的,该包简化了WPF中的动画集成。
元素的移动是通过jamesnet.WPF中的ThickItem动画实现的(类似于WPF中的ThicknessAnimation)。选择"Margin"作为"Property",使用Cubieaseinout添加更自然的动画效果,将持续时间设置为0.5秒,并使用边距值来确定移动的终点。
<james:ThickItem TargetName="cloud1" Property="Margin" Mode="CubicEaseInOut" Duration="0:0:0.5" To="-70 20 0 0" />
从太阳到月亮或从日间到夜间元素的颜色过渡是通过jamesnet.WPF中的ColorItem动画实现的(类似于WPF中的ColorAnimation)。一般属性设置类似于ThickItem,但在Property语法上有轻微变化:
<james:ColorItem TargetName="ellipse" Property="(Fill).Color" Mode="CubicEaseInOut" Duration="0:0:0.5" To="#E5B91A" />
<james:ColorItem TargetName="border" Property="(Background).Color" Mode="CubicEaseInOut" Duration="0:0:0.5" To="#191C25" />
对于夜间模式中的星系元素,它最初不需要显示,使用jamesnet.WPF中的ValueItem动画(等同于WPF中的DoubleAnimation)。将Property设置为Opacity,To设置为0,表示最初是不可见的状态。
<james:ValueItem TargetName="galaxy" Property="Opacity" Mode="CubicEaseInOut" Duration="0:0:0.5" To="0" />
按照上述代码,实现了交替元素移动的效果。然而,注意到云朵超出了切换按钮的范围并保持可见。为了解决这个问题,为项目设置了一个限定区域,限制所有元素只在定义区域内显示。
这是通过Clip属性实现的。使用Geometry,特别是RectangleGeometry,定义了一个裁剪形状,将项目的大小设置为120x50。还添加了一个25的圆角值,确保所有元素只在定义的Clip区域内显示。
<Setter Property="Clip">
<Setter.Value>
<RectangleGeometry Rect="0,0,120,50">
<RectangleGeometry.RadiusX>25</RectangleGeometry.RadiusX>
<RectangleGeometry.RadiusY>25</RectangleGeometry.RadiusY>
</RectangleGeometry>
</Setter.Value>
</Setter>