控件需求
长时间等待时,防止用户进行其他操作,显示 loading 等待框。
控件最终效果如下所示:
20221107_113333
控件设计方案
控件继承 window 窗口,loading 显示时可以直接阻塞线程,防止用户进行其他操作。
底部为半透明 Border 、中间为12颗米粒(米粒使用Rectangle)、loading文字提示内容。
将12颗米粒放在Canvas 控件中,将Canvas 按30° 不停旋转,即可得到最终的loading动画效果。
控件xaml模板下图所示
<Window x:Class="WPFCustomControl.Controls.RLoading"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFCustomControl.Controls"
mc:Ignorable="d"
Title="RLoading" Height="200" Width="200"
WindowStyle="None" WindowStartupLocation="CenterScreen" Background="Transparent" AllowsTransparency="True" ShowInTaskbar="False"
Loaded="Window_Loaded">
<Window.Resources>
<DrawingBrush x:Key="riceBrush" Stretch="None" AlignmentX="Center" AlignmentY="Top">
<DrawingBrush.Drawing>
<GeometryDrawing Brush="#fff">
<GeometryDrawing.Geometry>
<EllipseGeometry RadiusX="3" RadiusY="17"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
<Style TargetType="Rectangle">
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="100" />
<Setter Property="Fill" Value="{StaticResource riceBrush }" />
<Setter Property="RenderTransformOrigin" Value=".5,.5" />
</Style>
</Window.Resources>
<Grid>
<Grid.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00"
Storyboard.TargetName="canvasRotate"
Storyboard.TargetProperty="Angle">
<DiscreteDoubleKeyFrame KeyTime="00:00:00" Value="0"/>
<DiscreteDoubleKeyFrame KeyTime="00:00:00.08" Value="30"/>
<DiscreteDoubleKeyFrame KeyTime="00:00:00.16" Value="60"/>
<DiscreteDoubleKeyFrame KeyTime="00:00:00.24" Value="90"/>
<DiscreteDoubleKeyFrame KeyTime="00:00:00.32" Value="120"/>
<DiscreteDoubleKeyFrame KeyTime="00:00:00.40" Value="150"/>
<DiscreteDoubleKeyFrame KeyTime="00:00:00.48" Value="180"/>
<DiscreteDoubleKeyFrame KeyTime="00:00:00.56" Value="210"/>
<DiscreteDoubleKeyFrame KeyTime="00:00:00.64" Value="240"/>
<DiscreteDoubleKeyFrame KeyTime="00:00:00.72" Value="270"/>
<DiscreteDoubleKeyFrame KeyTime="00:00:00.80" Value="300"/>
<DiscreteDoubleKeyFrame KeyTime="00:00:00.88" Value="330"/>
<DiscreteDoubleKeyFrame KeyTime="00:00:00.96" Value="360"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
<Border Background="#333" Opacity="0.7" CornerRadius="4" SnapsToDevicePixels="True" />
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Canvas Width="100" Height="100" RenderTransformOrigin=".5,.5">
<Canvas.RenderTransform>
<TransformGroup>
<RotateTransform Angle="0" x:Name="canvasRotate" />
</TransformGroup>
</Canvas.RenderTransform>
<Rectangle/>
<Rectangle Opacity="0.92">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-30"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Opacity="0.84">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-60"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Opacity="0.76" >
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-90"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Opacity="0.68">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-120"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Opacity="0.60">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-150"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Opacity="0.52">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-180"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Opacity="0.44">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-210"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Opacity="0.36" >
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-240"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Opacity="0.28">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-270"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Opacity="0.20">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-300"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Opacity="0.12">
<Rectangle.RenderTransform>
<TransformGroup>
<RotateTransform Angle="-330"/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Canvas>
<Label x:Name="txtTitle" Foreground="White" HorizontalContentAlignment="Center" FontSize="20">加载中</Label>
</StackPanel>
</Grid>
</Window>
控件后台代码如下图所示:
handler 字段为需要长时间执行的代码片段。handler 执行完成后,会自动执行 OnComplate 方法里的关闭窗口。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace WPFCustomControl.Controls {
/// <summary>
/// RLoading.xaml 的交互逻辑
/// </summary>
public partial class RLoading : Window {
Action handler;
private RLoading(string title, Action handler) {
InitializeComponent();
this.txtTitle.Content = title;
this.handler = handler;
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
handler?.BeginInvoke(OnComplate, null);
}
private void OnComplate(IAsyncResult ar) {
this.Dispatcher.Invoke(new Action(() => { this.Close(); }));
}
public static void ShowLoading(string title, Action handler, Window owner) {
var loading = new RLoading(title, handler) { Owner = owner };
loading.ShowDialog();
}
}
}
代码内容使用方式如下:
实例代码使用 Sleep(5000),睡眠5秒钟模拟长时间耗时动作。
private void LoadingClick(object sender, RoutedEventArgs e) {
Controls.RLoading.ShowLoading("正在加载", () => { System.Threading.Thread.Sleep(5000); }, this);
}
有问题或看不懂的地方,欢迎评论去留言。