V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yanjinhua
V2EX  ›  .NET

老板加薪!看我做的 WPF Loading!

  •  
  •   yanjinhua · 2022-08-10 11:21:07 +08:00 · 4834 次点击
    这是一个创建于 615 天前的主题,其中的信息可能已经有所发展或是发生改变。

    老板加薪!看我做的 WPF Loading !!!

    控件名:RingLoading

    作者:WPFDevelopersOrg

    原文链接: https://github.com/WPFDevelopersOrg/WPFDevelopers.Minimal

    • 框架使用大于等于.NET40
    • Visual Studio 2022;
    • 项目使用 MIT 开源许可协议;

    👉视频点我效果预览⬅⬅

    • 最外层使用Viewbox为父控件内部嵌套创建三组 Grid -> Ellipse 、Border 分别给它们指定不同的Angle从左侧开始 -135 225 54,做永久 Angle 动画;
    • PART_Ring1.RotateTransform.AngleFrom -135-495
    • PART_Ring2.RotateTransform.AngleFrom 225-585
    • PART_Ring3.RotateTransform.AngleFrom -54-315
    • 如何绘制;

    • EllipseStrokeDashArray进行设置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>
                                                    <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|码云

    34 条回复    2022-08-22 10:39:01 +08:00
    Terry05
        1
    Terry05  
       2022-08-10 11:26:22 +08:00
    emmmmmm……恕我没看懂
    DrX
        2
    DrX  
       2022-08-10 11:28:36 +08:00   ❤️ 1
    老板:这么丑?撤掉,换回上一版那个 GIF !
    runningowl
        3
    runningowl  
       2022-08-10 12:34:52 +08:00
    Lottie 不香么
    loopinfor
        4
    loopinfor  
       2022-08-10 12:40:02 +08:00
    这个 loading 效果过于另类了,应该不会有人真的用吧
    yanjinhua
        5
    yanjinhua  
    OP
       2022-08-10 13:15:26 +08:00
    yanjinhua
        6
    yanjinhua  
    OP
       2022-08-10 13:15:40 +08:00
    @loopinfor 哈哈哈
    yanjinhua
        7
    yanjinhua  
    OP
       2022-08-10 13:16:05 +08:00
    @DrX 没用 gif
    yanjinhua
        8
    yanjinhua  
    OP
       2022-08-10 13:16:44 +08:00
    @Terry05 呃呃呃呃呃
    wangyzj
        9
    wangyzj  
       2022-08-10 14:05:29 +08:00
    不够卷
    SeanTheSheep
        10
    SeanTheSheep  
       2022-08-10 14:05:48 +08:00
    逛 V 站一年了,第二次看到 WPF 相关的帖子,泪目了。
    yanjinhua
        11
    yanjinhua  
    OP
       2022-08-10 15:31:29 +08:00
    @wangyzj 正在努力
    yanjinhua
        12
    yanjinhua  
    OP
       2022-08-10 15:31:55 +08:00
    @SeanTheSheep 那么我很关心第一次的帖子 i😊
    20015jjw
        13
    20015jjw  
       2022-08-10 15:42:37 +08:00 via iPhone
    amazon 可能会喜欢你(雾
    sinnosong1
        14
    sinnosong1  
       2022-08-10 16:06:39 +08:00
    每行都能看懂,但是连起来就完全看不懂了,虽然我也写过 WPF ,一直都是入门状态的新手。。。
    yanjinhua
        15
    yanjinhua  
    OP
       2022-08-10 16:12:15 +08:00
    @sinnosong1 哈哈哈,好像懂了 但是又没完全懂
    yanjinhua
        16
    yanjinhua  
    OP
       2022-08-10 16:12:32 +08:00
    @20015jjw 我不懂了
    vone
        17
    vone  
       2022-08-10 16:24:57 +08:00   ❤️ 3
    当场开除!
    dcsuibian
        18
    dcsuibian  
       2022-08-10 16:43:54 +08:00
    现在 Electron 越来越多,对原生开发者也越来越敬重
    villivateur
        19
    villivateur  
       2022-08-10 16:47:06 +08:00
    star 了,准备在我丑得要命的上位机里面试下这个主题
    stoluoyu
        20
    stoluoyu  
       2022-08-10 16:49:15 +08:00
    精子有了,load 完不该进入卵子了么(大雾
    marcong95
        21
    marcong95  
       2022-08-10 17:10:38 +08:00
    其实只留一组,甚至把那个东西的头部删掉其实就好多了,最好圆弧的长度还能随着时间变长变短。。。。
    v2byy
        22
    v2byy  
       2022-08-10 17:45:58 +08:00
    就用 windows start 那个 loading 不香吗
    NGXDLK
        23
    NGXDLK  
       2022-08-10 18:00:18 +08:00
    @marcong95 还可以变粗变细,变直变弯
    lifeintools
        24
    lifeintools  
       2022-08-10 18:17:55 +08:00
    真好玩~
    xyx0826
        25
    xyx0826  
       2022-08-11 10:40:36 +08:00 via iPhone
    很酷诶...WPF 我用了一段时间但是完全没搞懂它的高级样式和动画,现在 winui 3 也出了,不知道该学哪个了
    yanjinhua
        26
    yanjinhua  
    OP
       2022-08-11 13:10:55 +08:00
    @vone 哈哈哈哈哈
    yanjinhua
        27
    yanjinhua  
    OP
       2022-08-11 13:11:32 +08:00
    @dcsuibian 现在确实都是使用 electron 。谢谢
    yanjinhua
        28
    yanjinhua  
    OP
       2022-08-11 13:12:16 +08:00
    @villivateur 欢迎使用哈,https://github.com/WPFDevelopersOrg 这个组织下都是关于 WPF 和 MAUI 的项目。有问题及时反馈哈。
    yanjinhua
        29
    yanjinhua  
    OP
       2022-08-11 13:13:03 +08:00
    @stoluoyu 哈哈哈哈,后期应该会增加任务完成关闭。
    yanjinhua
        30
    yanjinhua  
    OP
       2022-08-11 13:13:19 +08:00
    @NGXDLK 是这样的。
    yanjinhua
        31
    yanjinhua  
    OP
       2022-08-11 13:13:38 +08:00
    @lifeintools 可以复制源码魔改起来。
    yanjinhua
        32
    yanjinhua  
    OP
       2022-08-11 13:15:46 +08:00
    @xyx0826 maui 跨平台,win10 之前的系统。动画和高级样式多写几次就熟悉了。“https://github.com/WPFDevelopersOrg” 这个组织下 也有 maui 项目。
    ragnaroks
        33
    ragnaroks  
       2022-08-19 08:39:59 +08:00
    WPF 设置样式( StyleSetter )和动画( StoryBorad )太累了,我已经放弃了,现在都用网页做 UI ,通过 websocket 链接到本地
    yanjinhua
        34
    yanjinhua  
    OP
       2022-08-22 10:39:01 +08:00
    @ragnaroks 熟悉了就还好,也是不错的选择。👍
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1378 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 17:27 · PVG 01:27 · LAX 10:27 · JFK 13:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.