V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
yanjinhua
V2EX  ›  .NET

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

  •  
  •   yanjinhua · Aug 10, 2022 · 6304 views
    This topic created in 1362 days ago, the information mentioned may be changed or developed.

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