在项目开发过程中总会遇到一个需求:Loading...正在加载中,这样一个需求作为加载的过渡效果。
控件主要原理以半径X,Y轴进行旋转,按照时钟原理画12个点,这里的RenderTransformOrigin="0.5,0.5"表示以图片的圆心为指定变换原点,每个点旋转角度从0到360度,每次以30度递增。
在控件加载事件中取透明值依赖属性进行时间段递增显示。
先显示loading等待控件效果
WPF UI开发教程自定义loading等待控件
控件样式代码:
<Style TargetType="{x:Type local:LoadingBox}">
<!--<Setter Property="Height" Value="64"/>
<Setter Property="Width" Value="64"/>-->
<Setter Property="RadiusX" Value="4"/>
<Setter Property="RadiusY" Value="4"/>
<Setter Property="Foreground" Value="{DynamicResource Nb.Loading.Foreground}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Name ="loading" Visibility="Visible" Height="{TemplateBinding Height}" Width ="{TemplateBinding Width}" VerticalAlignment="{TemplateBinding VerticalAlignment}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}">
<Grid.Resources>
<DrawingBrush x:Key="brush" Stretch="None" AlignmentX ="Center" AlignmentY="Top">
<DrawingBrush.Drawing>
<GeometryDrawing Brush ="{Binding RelativeSource={RelativeSource AncestorType=nb:LoadingBox},Path=Foreground}">
<GeometryDrawing.Geometry>
<EllipseGeometry RadiusX ="{Binding RelativeSource={RelativeSource AncestorType=nb:LoadingBox},Path=RadiusX}"
RadiusY="{Binding RelativeSource={RelativeSource AncestorType=nb:LoadingBox},Path=RadiusY}"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Grid.Resources>
<Rectangle x:Name="r01" Fill="{StaticResource brush}" Opacity ="{TemplateBinding Opacity}" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle ="0"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="r02" Fill="{StaticResource brush}" Opacity ="{TemplateBinding Opacity}" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle ="30"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="r03" Fill="{StaticResource brush}" Opacity ="{TemplateBinding Opacity}" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle ="60"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="r04" Fill="{StaticResource brush}" Opacity ="{TemplateBinding Opacity}" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle ="90"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="r05" Fill="{StaticResource brush}" Opacity ="{TemplateBinding Opacity}" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle ="120"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="r06" Fill="{StaticResource brush}" Opacity ="{TemplateBinding Opacity}" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle ="150"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="r07" Fill="{StaticResource brush}" Opacity ="{TemplateBinding Opacity}" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle ="180"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="r08" Fill="{StaticResource brush}" Opacity ="{TemplateBinding Opacity}" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle ="210"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="r09" Fill="{StaticResource brush}" Opacity ="{TemplateBinding Opacity}" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle ="240"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="r10" Fill="{StaticResource brush}" Opacity ="{TemplateBinding Opacity}" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle ="270"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="r11" Fill="{StaticResource brush}" Opacity ="{TemplateBinding Opacity}" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle ="300"/>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle x:Name="r12" Fill="{StaticResource brush}" Opacity ="{TemplateBinding Opacity}" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<RotateTransform Angle ="330"/>
</Rectangle.RenderTransform>
</Rectangle>
<Grid.Triggers>
<EventTrigger RoutedEvent ="Grid.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior ="Forever">
<DoubleAnimation Storyboard.TargetName ="r01" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08333" BeginTime ="0:0:0.00000" To="0"/>
<DoubleAnimation Storyboard.TargetName ="r02" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08333" BeginTime ="0:0:0.08333" To="0"/>
<DoubleAnimation Storyboard.TargetName ="r03" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08333" BeginTime ="0:0:0.16666" To="0"/>
<DoubleAnimation Storyboard.TargetName ="r04" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08333" BeginTime ="0:0:0.24999" To="0"/>
<DoubleAnimation Storyboard.TargetName ="r05" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08333" BeginTime ="0:0:0.33332" To="0"/>
<DoubleAnimation Storyboard.TargetName ="r06" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08333" BeginTime ="0:0:0.41665" To="0"/>
<DoubleAnimation Storyboard.TargetName ="r07" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08333" BeginTime ="0:0:0.49998" To="0"/>
<DoubleAnimation Storyboard.TargetName ="r08" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08333" BeginTime ="0:0:0.58331" To="0"/>
<DoubleAnimation Storyboard.TargetName ="r09" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08333" BeginTime ="0:0:0.66664" To="0"/>
<DoubleAnimation Storyboard.TargetName ="r10" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08333" BeginTime ="0:0:0.74997" To="0"/>
<DoubleAnimation Storyboard.TargetName ="r11" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08333" BeginTime ="0:0:0.83330" To="0"/>
<DoubleAnimation Storyboard.TargetName ="r12" Storyboard.TargetProperty="Opacity" AutoReverse="True" Duration="0:0:0.08333" BeginTime ="0:0:0.91663" To="0"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
默认半径RadiusX,RadiusY是4
<Grid.Resources>
<DrawingBrush x:Key="brush" Stretch="None" AlignmentX ="Center" AlignmentY="Top">
<DrawingBrush.Drawing>
<GeometryDrawing Brush ="{Binding RelativeSource={RelativeSource AncestorType=nb:LoadingBox},Path=Foreground}">
<GeometryDrawing.Geometry>
<EllipseGeometry RadiusX ="{Binding RelativeSource={RelativeSource AncestorType=nb:LoadingBox},Path=RadiusX}"
RadiusY="{Binding RelativeSource={RelativeSource AncestorType=nb:LoadingBox},Path=RadiusY}"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Grid.Resources>
重写Grid资源样式,用DrawingBrush来绘制区域,EllipseGeometry 以半径RadiusX,RadiusY矢量图型绘制圆型。
public class LoadingBox : Control
{
static LoadingBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(LoadingBox), new FrameworkPropertyMetadata(typeof(LoadingBox)));
}
public double RadiusX
{
get { return (double)GetValue(RadiusXProperty); }
set { SetValue(RadiusXProperty, value); }
}
// Using a DependencyProperty as the backing store for RadiusX. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RadiusXProperty =
DependencyProperty.Register("RadiusX", typeof(double), typeof(LoadingBox), new PropertyMetadata(3d));
public double RadiusY
{
get { return (double)GetValue(RadiusYProperty); }
set { SetValue(RadiusYProperty, value); }
}
// Using a DependencyProperty as the backing store for RadiusY. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RadiusYProperty =
DependencyProperty.Register("RadiusY", typeof(double), typeof(LoadingBox), new PropertyMetadata(3d));
}
依赖属性定义RadiusX,RadiusY半径坐标。
下面是实例调用代码:
<local:LoadingBox Grid.Column="0" Width="40" Height="40" RadiusX="3" RadiusY="3" Foreground="#000000"/>