
老板加薪!看我做的 WPF Loading !!!
控件名:RingLoading
作者:WPFDevelopersOrg
原文链接: https://github.com/WPFDevelopersOrg/WPFDevelopers.Minimal
.NET40;Visual Studio 2022;Grid -> Ellipse 、Border 分别给它们指定不同的Angle从左侧开始 -135 225 54,做永久 Angle 动画;From -135 到 -495;From 225 到 -585;From -54 到 -315;
对Ellipse的StrokeDashArray进行设置23 100就能达到效果;
Border 做为圆设置 Effect 可实现阴影效果;
1 )RingLoading.cs代码如下;
using System.Windows; using System.Windows.Controls; namespace WPFDevelopers.Controls { public class RingLoading : Control { // Using a DependencyProperty as the backing store for IsStart. This enables animation, styling, binding, etc... public static readonly DependencyProperty IsStartProperty = DependencyProperty.Register("IsStart", typeof(bool), typeof(RingLoading), new PropertyMetadata(default)); // Using a DependencyProperty as the backing store for ProgressValue. This enables animation, styling, binding, etc... public static readonly DependencyProperty ProgressValueProperty = DependencyProperty.Register("ProgressValue", typeof(double), typeof(RingLoading), new PropertyMetadata(0d, OnProgressValueChangedCallBack)); // Using a DependencyProperty as the backing store for Progress. This enables animation, styling, binding, etc... internal static readonly DependencyProperty ProgressProperty = DependencyProperty.Register("Progress", typeof(string), typeof(RingLoading), new PropertyMetadata(default)); // Using a DependencyProperty as the backing store for Maximum. This enables animation, styling, binding, etc... public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(RingLoading), new PropertyMetadata(100d, OnMaximumPropertyChangedCallBack)); // Using a DependencyProperty as the backing store for Description. This enables animation, styling, binding, etc... public static readonly DependencyProperty DescriptiOnProperty= DependencyProperty.Register("Description", typeof(string), typeof(RingLoading), new PropertyMetadata(default)); static RingLoading() { DefaultStyleKeyProperty.OverrideMetadata(typeof(RingLoading), new FrameworkPropertyMetadata(typeof(RingLoading))); } public bool IsStart { get => (bool)GetValue(IsStartProperty); set => SetValue(IsStartProperty, value); } public double ProgressValue { get => (double)GetValue(ProgressValueProperty); set => SetValue(ProgressValueProperty, value); } internal string Progress { get => (string)GetValue(ProgressProperty); set => SetValue(ProgressProperty, value); } public double Maximum { get => (double)GetValue(MaximumProperty); set => SetValue(MaximumProperty, value); } public string Description { get => (string)GetValue(DescriptionProperty); set => SetValue(DescriptionProperty, value); } private static void OnProgressValueChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (!(d is RingLoading control)) return; if (!double.TryParse(e.NewValue?.ToString(), out var value)) return; var progress = value / control.Maximum; control.SetCurrentValue(ProgressProperty, progress.ToString("P0")); } private static void OnMaximumPropertyChangedCallBack(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (!(d is RingLoading control)) return; if (!double.TryParse(e.NewValue?.ToString(), out var maxValue)) return; if (maxValue <= 0) return; var progress = control.ProgressValue / maxValue; control.SetCurrentValue(ProgressProperty, progress.ToString("P0")); } } } 2 )RingLoading.xaml代码如下;
<Style TargetType="controls:RingLoading" BasedOn="{StaticResource ControlBasicStyle}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="controls:RingLoading"> <ControlTemplate.Resources> <Storyboard x:Key="PART_Resource_Storyboard" RepeatBehavior="Forever"> <DoubleAnimation To="-495" Duration="0:0:1.5" Storyboard.TargetName="PART_Ring1" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)"/> <DoubleAnimation To="585" Duration="0:0:1.5" Storyboard.TargetName="PART_Ring2" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)"/> <DoubleAnimation To="-315" Duration="0:0:1.5" Storyboard.TargetName="PART_Ring3" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)"/> </Storyboard> </ControlTemplate.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Viewbox HorizOntalAlignment="Center" VerticalAlignment="Center" > <Border Padding="10" Width="100" Height="100" > <Grid> <Grid x:Name="PART_Ring1" Width="60" Height="60" HorizOntalAlignment="Center" VerticalAlignment="Top" RenderTransformOrigin="0.5,0.5"> <Grid.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform Angle="-135"/> <TranslateTransform/> </TransformGroup> </Grid.RenderTransform> <Ellipse Stroke="Red" StrokeThickness="2" StrokeDashArray="23 100" RenderTransformOrigin="0.5,0.5"/> <Border Width="10" Height="10" CornerRadius="10" Background="Red" HorizOntalAlignment="Right" Margin="0,0,-4,0"> <Border.Effect> lt;DropShadowEffect BlurRadius="10" ShadowDepth="0" Color="Red"/> </Border.Effect> </Border> </Grid> <Grid x:Name="PART_Ring2" Width="60" Height="60" HorizOntalAlignment="Left" VerticalAlignment="Bottom" RenderTransformOrigin="0.5,0.5"> <Grid.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform Angle="225"/> <TranslateTransform/> </TransformGroup> </Grid.RenderTransform> <Ellipse Stroke="Purple" StrokeThickness="2" StrokeDashArray="23 100"/> <Border Width="10" Height="10" CornerRadius="10" Background="Purple" VerticalAlignment="Bottom" HorizOntalAlignment="Center" Margin="0,0,0,-4"> <Border.Effect> <DropShadowEffect BlurRadius="10" ShadowDepth="0" Color="Purple"/> </Border.Effect> </Border> </Grid> <Grid x:Name="PART_Ring3" Width="60" Height="60" HorizOntalAlignment="Right" VerticalAlignment="Bottom" RenderTransformOrigin="0.5,0.5"> <Grid.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform Angle="45"/> <TranslateTransform/> </TransformGroup> </Grid.RenderTransform> <Ellipse Stroke="#0fb8b2" StrokeThickness="2" StrokeDashArray="23 100"/> <Border Width="10" Height="10" CornerRadius="10" Background="#0fb8b2" HorizOntalAlignment="Right" Margin="0,0,-4,0"> <Border.Effect> <DropShadowEffect BlurRadius="10" ShadowDepth="0" Color="#0fb8b2"/> </Border.Effect> </Border> </Grid> </Grid> </Border> </Viewbox> <StackPanel Grid.Row="1" Grid.ColumnSpan="2" Margin="10"> <TextBlock HorizOntalAlignment="Center" Text="Loading..." Margin="0,0,0,15"/> <TextBlock HorizOntalAlignment="Center" Text="{TemplateBinding Description}" Margin="0,0,0,15"/> <TextBlock HorizOntalAlignment="Center" Text="{TemplateBinding Progress}" FOntSize="{StaticResource TitleFontSize}" FOntWeight="Bold"/> </StackPanel> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsStart" Value="True"> <Trigger.EnterActions> <BeginStoryboard Storyboard="{StaticResource PART_Resource_Storyboard}" x:Name="PART_BeginStoryboard"/> </Trigger.EnterActions> <Trigger.ExitActions> <StopStoryboard BeginStoryboardName="PART_BeginStoryboard"/> </Trigger.ExitActions> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> 3 )RingLoadingExample.xaml代码如下;
<UserControl x:Class="WPFDevelopers.Samples.ExampleViews.RingLoadingExample" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:wpfdev="https://github.com/WPFDevelopersOrg/WPFDevelopers" xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Grid> <wpfdev:RingLoading IsStart="true" Width="400" Height="400" Description="WPFDevelopers" Foreground="Black" ProgressValue="50"/> </Grid> </UserControl> RingLoading|Github
RingLoading|码云
RingLoading.xaml|Github
RingLoading.xaml|码云
1 Terry05 2022-08-10 11:26:22 +08:00 emmmmmm……恕我没看懂 |
2 y830CAa5nink4rUQ 2022-08-10 11:28:36 +08:00 老板:这么丑?撤掉,换回上一版那个 GIF ! |
3 runningowl 2022-08-10 12:34:52 +08:00 Lottie 不香么 |
4 loopinfor 2022-08-10 12:40:02 +08:00 这个 loading 效果过于另类了,应该不会有人真的用吧 |
5 yanjinhua OP @runningowl 香 |
9 wangyzj 2022-08-10 14:05:29 +08:00 不够卷 |
10 SeanTheSheep 2022-08-10 14:05:48 +08:00 逛 V 站一年了,第二次看到 WPF 相关的帖子,泪目了。 |
12 yanjinhua OP @SeanTheSheep 那么我很关心第一次的帖子 i |
13 20015jjw 2022-08-10 15:42:37 +08:00 via iPhone amazon 可能会喜欢你(雾 |
14 sinnosong1 2022-08-10 16:06:39 +08:00 每行都能看懂,但是连起来就完全看不懂了,虽然我也写过 WPF ,一直都是入门状态的新手。。。 |
15 yanjinhua OP @sinnosong1 哈哈哈,好像懂了 但是又没完全懂 |
17 vone 2022-08-10 16:24:57 +08:00 当场开除! |
18 dcsuibian 2022-08-10 16:43:54 +08:00 现在 Electron 越来越多,对原生开发者也越来越敬重 |
19 villivateur 2022-08-10 16:47:06 +08:00 star 了,准备在我丑得要命的上位机里面试下这个主题 |
20 stoluoyu 2022-08-10 16:49:15 +08:00 精子有了,load 完不该进入卵子了么(大雾 |
21 marcong95 2022-08-10 17:10:38 +08:00 其实只留一组,甚至把那个东西的头部删掉其实就好多了,最好圆弧的长度还能随着时间变长变短。。。。 |
22 v2byy 2022-08-10 17:45:58 +08:00 就用 windows start 那个 loading 不香吗 |
24 lifeintools 2022-08-10 18:17:55 +08:00 真好玩~ |
25 xyx0826 2022-08-11 10:40:36 +08:00 via iPhone 很酷诶...WPF 我用了一段时间但是完全没搞懂它的高级样式和动画,现在 winui 3 也出了,不知道该学哪个了 |
28 yanjinhua OP @villivateur 欢迎使用哈,https://github.com/WPFDevelopersOrg 这个组织下都是关于 WPF 和 MAUI 的项目。有问题及时反馈哈。 |
31 yanjinhua OP @lifeintools 可以复制源码魔改起来。 |
32 yanjinhua OP @xyx0826 maui 跨平台,win10 之前的系统。动画和高级样式多写几次就熟悉了。“https://github.com/WPFDevelopersOrg” 这个组织下 也有 maui 项目。 |
33 ragnaroks 2022-08-19 08:39:59 +08:00 WPF 设置样式( StyleSetter )和动画( StoryBorad )太累了,我已经放弃了,现在都用网页做 UI ,通过 websocket 链接到本地 |