WPF 加载旋转器

问题描述 投票:0回答:15

目标是显示应用程序正在运行的信息。所以我正在寻找使用 WPF / MVVM 的加载旋转器的智能实现示例。

c# wpf mvvm spinner loading
15个回答
122
投票

一个非常简单的“即插即用”旋转器可以是Font Awesome Wpf PackageSpinning Icons)中的旋转图标之一。

使用方法非常简单,安装nuget包即可:

PM> Install-Package FontAwesome.WPF

然后添加对命名空间的引用

xmlns:fa="http://schemas.fontawesome.io/icons/"

并使用 ImageAwesome 控件。设置 Spin="True" 属性并选择“Spinner”、“Refresh”、“Cog”和“CircleOutlinedNotched”图标之一。它是可扩展的,可以通过设置宽度和高度来调整大小。

<Window x:Class="Example.FontAwesome.WPF.Single"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:fa="http://schemas.fontawesome.io/icons/"
    Title="Single" Height="300" Width="300">
    <Grid  Margin="20">
        <fa:ImageAwesome Icon="Refresh" Spin="True" Height="48" Width="48" />
    </Grid>
</Window>

110
投票

要得到这个:

将其粘贴到用户控件中:

<UserControl.Resources>
    <Color x:Key="FilledColor" A="255" B="155" R="155" G="155"/>
    <Color x:Key="UnfilledColor" A="0" B="155" R="155" G="155"/>

    <Style x:Key="BusyAnimationStyle" TargetType="Control">
        <Setter Property="Background" Value="#7F000000"/>

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="Control">
                    <ControlTemplate.Resources>
                        <Storyboard x:Key="Animation0" BeginTime="00:00:00.0" RepeatBehavior="Forever">
                            <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse0" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>

                        <Storyboard x:Key="Animation1" BeginTime="00:00:00.2" RepeatBehavior="Forever">
                            <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse1" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>

                        <Storyboard x:Key="Animation2" BeginTime="00:00:00.4" RepeatBehavior="Forever">
                            <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse2" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>

                        <Storyboard x:Key="Animation3" BeginTime="00:00:00.6" RepeatBehavior="Forever">
                            <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse3" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>

                        <Storyboard x:Key="Animation4" BeginTime="00:00:00.8" RepeatBehavior="Forever">
                            <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse4" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>

                        <Storyboard x:Key="Animation5" BeginTime="00:00:01.0" RepeatBehavior="Forever">
                            <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse5" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>

                        <Storyboard x:Key="Animation6" BeginTime="00:00:01.2" RepeatBehavior="Forever">
                            <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse6" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>

                        <Storyboard x:Key="Animation7" BeginTime="00:00:01.4" RepeatBehavior="Forever">
                            <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse7" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                            </ColorAnimationUsingKeyFrames>
                        </Storyboard>
                    </ControlTemplate.Resources>

                    <ControlTemplate.Triggers>
                        <Trigger Property="IsVisible" Value="True">
                            <Trigger.EnterActions>
                                <BeginStoryboard Storyboard="{StaticResource Animation0}" x:Name="Storyboard0" />
                                <BeginStoryboard Storyboard="{StaticResource Animation1}" x:Name="Storyboard1"/>
                                <BeginStoryboard Storyboard="{StaticResource Animation2}" x:Name="Storyboard2"/>
                                <BeginStoryboard Storyboard="{StaticResource Animation3}" x:Name="Storyboard3"/>
                                <BeginStoryboard Storyboard="{StaticResource Animation4}" x:Name="Storyboard4"/>
                                <BeginStoryboard Storyboard="{StaticResource Animation5}" x:Name="Storyboard5"/>
                                <BeginStoryboard Storyboard="{StaticResource Animation6}" x:Name="Storyboard6"/>
                                <BeginStoryboard Storyboard="{StaticResource Animation7}" x:Name="Storyboard7"/>
                            </Trigger.EnterActions>

                            <Trigger.ExitActions>
                                <StopStoryboard BeginStoryboardName="Storyboard0"/>
                                <StopStoryboard BeginStoryboardName="Storyboard1"/>
                                <StopStoryboard BeginStoryboardName="Storyboard2"/>
                                <StopStoryboard BeginStoryboardName="Storyboard3"/>
                                <StopStoryboard BeginStoryboardName="Storyboard4"/>
                                <StopStoryboard BeginStoryboardName="Storyboard5"/>
                                <StopStoryboard BeginStoryboardName="Storyboard6"/>
                                <StopStoryboard BeginStoryboardName="Storyboard7"/>
                            </Trigger.ExitActions>
                        </Trigger>
                    </ControlTemplate.Triggers>

                    <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
                        <Grid>
                        <Canvas Height="60" Width="60">
                            <Canvas.Resources>
                                <Style TargetType="Ellipse">
                                    <Setter Property="Width" Value="15"/>
                                    <Setter Property="Height" Value="15" />
                                    <Setter Property="Fill" Value="#009B9B9B" />
                                </Style>
                            </Canvas.Resources>

                            <Ellipse x:Name="ellipse0" Canvas.Left="1.75" Canvas.Top="21"/>
                            <Ellipse x:Name="ellipse1" Canvas.Top="7" Canvas.Left="6.5"/>
                            <Ellipse x:Name="ellipse2" Canvas.Left="20.5" Canvas.Top="0.75"/>
                            <Ellipse x:Name="ellipse3" Canvas.Left="34.75" Canvas.Top="6.75"/>
                            <Ellipse x:Name="ellipse4" Canvas.Left="40.5" Canvas.Top="20.75" />
                            <Ellipse x:Name="ellipse5" Canvas.Left="34.75" Canvas.Top="34.5"/>
                            <Ellipse x:Name="ellipse6" Canvas.Left="20.75" Canvas.Top="39.75"/>
                            <Ellipse x:Name="ellipse7" Canvas.Top="34.25" Canvas.Left="7" />
                            <Ellipse Width="39.5" Height="39.5" Canvas.Left="8.75" Canvas.Top="8" Visibility="Hidden"/>
                        </Canvas>
                            <Label Content="{Binding Path=Text}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</UserControl.Resources>
<Control Style="{StaticResource BusyAnimationStyle}"/>

要在每个椭圆上获得很酷的消失效果,请在每个

ColorAnimationUsingKeyFrames
元素后添加以下内容。请务必将其指向正确的椭圆..

<ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipse0" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
    <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
    <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
</ColorAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ellipse0" Storyboard.TargetProperty="Width" >
    <DoubleAnimationUsingKeyFrames.KeyFrames>
        <SplineDoubleKeyFrame  KeyTime="00:00:00.0" Value="15" />
        <SplineDoubleKeyFrame  KeyTime="00:00:01.0" Value="12" />
        <SplineDoubleKeyFrame  KeyTime="00:00:01.6" Value="0" />
    </DoubleAnimationUsingKeyFrames.KeyFrames>
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="ellipse0" Storyboard.TargetProperty="Height" >
    <DoubleAnimationUsingKeyFrames.KeyFrames>
        <SplineDoubleKeyFrame  KeyTime="00:00:00.0" Value="15" />
        <SplineDoubleKeyFrame  KeyTime="00:00:01.0" Value="12" />
        <SplineDoubleKeyFrame  KeyTime="00:00:01.6" Value="0" />
    </DoubleAnimationUsingKeyFrames.KeyFrames>
</DoubleAnimationUsingKeyFrames>

71
投票

我写了这个用户控件,它可能会有所帮助,它将显示带有旋转进度条的消息,以表明它当前正在加载某些内容。

  <ctr:LoadingPanel x:Name="loadingPanel"
                    IsLoading="{Binding PanelLoading}"
                    Message="{Binding PanelMainMessage}"
                    SubMessage="{Binding PanelSubMessage}" 
                    ClosePanelCommand="{Binding PanelCloseCommand}" />

它有几个可以绑定的基本属性。


58
投票

有图片

旋转图标选项的视觉摘要。使用Screen To Gif录制。


字体-Awesome-WPF

GitHub 上的文档

通过 NuGet 安装:

PM> 安装包 FontAwesome.WPF

看起来像这样:

XAML:

<fa:ImageAwesome Icon="Spinner" Spin="True" SpinDuration="4" />

图中的图标是

Spinner
CircleOutlineNotch
Refresh
Cog
。还有还有很多其他


来自@HAdes的方法

XAML 复制/粘贴。


36
投票

在 WPF 中,您现在可以简单地执行以下操作:

Mouse.OverrideCursor = System.Windows.Input.Cursors.Wait; // set the cursor to loading spinner

Mouse.OverrideCursor = System.Windows.Input.Cursors.Arrow; // set the cursor back to arrow

26
投票

您无需任何额外的控件和库,仅使用图像控制和转换即可做到这一点:

<Image
    Source="/images/spinner.png"
    Width="100"
    Height="100"
    RenderTransformOrigin="0.5, 0.5" Visibility="{Binding IsLoading, Converter={StaticResource BooleanToVisibilityConverter}}">
    <Image.RenderTransform>
        <RotateTransform x:Name="noFreeze" />
    </Image.RenderTransform>
    <Image.Triggers>
        <EventTrigger RoutedEvent="Loaded">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation
                        Storyboard.TargetProperty="(Image.RenderTransform).(RotateTransform.Angle)"
                        To="360" Duration="0:0:1" RepeatBehavior="Forever" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Image.Triggers>
</Image>

/images/spinner.png
替换为您的图像。如果您想逆时针旋转,请将
To="360"
更改为
To="-360"
Duration="0:0:1"
等于每转 1 秒。


10
投票

这是对@HAdes 给出的代码的更新,用于参数化宽度、高度和椭圆尺寸。

此实现会动态自动计算所需的角度、宽度和高度。

用户控件与其自身绑定(代码隐藏),负责所有计算。

XAML

<UserControl x:Class="WpfApplication2.Spinner"
             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:local="clr-namespace:WpfApplication2"
             mc:Ignorable="d" 
             DataContext="{Binding RelativeSource={RelativeSource Self}}"
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <Color x:Key="FilledColor" A="255" B="155" R="155" G="155"/>
        <Color x:Key="UnfilledColor" A="0" B="155" R="155" G="155"/>

        <Style x:Key="BusyAnimationStyle" TargetType="Control">
            <Setter Property="Background" Value="Transparent"/>

            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Control">
                        <ControlTemplate.Resources>
                            <Storyboard x:Key="Animation0" BeginTime="00:00:00.0" RepeatBehavior="Forever">
                                <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseN" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                    <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                    <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                                </ColorAnimationUsingKeyFrames>
                            </Storyboard>

                            <Storyboard x:Key="Animation1" BeginTime="00:00:00.2" RepeatBehavior="Forever">
                                <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseNE" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                    <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                    <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                                </ColorAnimationUsingKeyFrames>
                            </Storyboard>

                            <Storyboard x:Key="Animation2" BeginTime="00:00:00.4" RepeatBehavior="Forever">
                                <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseE" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                    <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                    <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                                </ColorAnimationUsingKeyFrames>
                            </Storyboard>

                            <Storyboard x:Key="Animation3" BeginTime="00:00:00.6" RepeatBehavior="Forever">
                                <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseSE" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                    <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                    <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                                </ColorAnimationUsingKeyFrames>
                            </Storyboard>

                            <Storyboard x:Key="Animation4" BeginTime="00:00:00.8" RepeatBehavior="Forever">
                                <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseS" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                    <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                    <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                                </ColorAnimationUsingKeyFrames>
                            </Storyboard>

                            <Storyboard x:Key="Animation5" BeginTime="00:00:01.0" RepeatBehavior="Forever">
                                <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseSW" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                    <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                    <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                                </ColorAnimationUsingKeyFrames>
                            </Storyboard>

                            <Storyboard x:Key="Animation6" BeginTime="00:00:01.2" RepeatBehavior="Forever">
                                <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseW" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                    <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                    <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                                </ColorAnimationUsingKeyFrames>
                            </Storyboard>

                            <Storyboard x:Key="Animation7" BeginTime="00:00:01.4" RepeatBehavior="Forever">
                                <ColorAnimationUsingKeyFrames Storyboard.TargetName="ellipseNW" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
                                    <SplineColorKeyFrame KeyTime="00:00:00.0" Value="{StaticResource FilledColor}"/>
                                    <SplineColorKeyFrame KeyTime="00:00:01.6" Value="{StaticResource UnfilledColor}"/>
                                </ColorAnimationUsingKeyFrames>
                            </Storyboard>
                        </ControlTemplate.Resources>

                        <ControlTemplate.Triggers>
                            <Trigger Property="IsVisible" Value="True">
                                <Trigger.EnterActions>
                                    <BeginStoryboard Storyboard="{StaticResource Animation0}" x:Name="Storyboard0" />
                                    <BeginStoryboard Storyboard="{StaticResource Animation1}" x:Name="Storyboard1"/>
                                    <BeginStoryboard Storyboard="{StaticResource Animation2}" x:Name="Storyboard2"/>
                                    <BeginStoryboard Storyboard="{StaticResource Animation3}" x:Name="Storyboard3"/>
                                    <BeginStoryboard Storyboard="{StaticResource Animation4}" x:Name="Storyboard4"/>
                                    <BeginStoryboard Storyboard="{StaticResource Animation5}" x:Name="Storyboard5"/>
                                    <BeginStoryboard Storyboard="{StaticResource Animation6}" x:Name="Storyboard6"/>
                                    <BeginStoryboard Storyboard="{StaticResource Animation7}" x:Name="Storyboard7"/>
                                </Trigger.EnterActions>

                                <Trigger.ExitActions>
                                    <StopStoryboard BeginStoryboardName="Storyboard0"/>
                                    <StopStoryboard BeginStoryboardName="Storyboard1"/>
                                    <StopStoryboard BeginStoryboardName="Storyboard2"/>
                                    <StopStoryboard BeginStoryboardName="Storyboard3"/>
                                    <StopStoryboard BeginStoryboardName="Storyboard4"/>
                                    <StopStoryboard BeginStoryboardName="Storyboard5"/>
                                    <StopStoryboard BeginStoryboardName="Storyboard6"/>
                                    <StopStoryboard BeginStoryboardName="Storyboard7"/>
                                </Trigger.ExitActions>
                            </Trigger>
                        </ControlTemplate.Triggers>

                        <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
                            <Grid>
                                <Canvas>
                                    <Canvas.Resources>
                                        <Style TargetType="Ellipse">
                                            <Setter Property="Width" Value="{Binding Path=EllipseSize}"/>
                                            <Setter Property="Height" Value="{Binding Path=EllipseSize}" />
                                            <Setter Property="Fill" Value="Transparent" />
                                        </Style>
                                    </Canvas.Resources>

                                    <Ellipse x:Name="ellipseN" Canvas.Left="{Binding Path=EllipseN.Left}" Canvas.Top="{Binding Path=EllipseN.Top}"/>
                                    <Ellipse x:Name="ellipseNE" Canvas.Left="{Binding Path=EllipseNE.Left}" Canvas.Top="{Binding Path=EllipseNE.Top}"/>
                                    <Ellipse x:Name="ellipseE" Canvas.Left="{Binding Path=EllipseE.Left}" Canvas.Top="{Binding Path=EllipseE.Top}"/>
                                    <Ellipse x:Name="ellipseSE" Canvas.Left="{Binding Path=EllipseSE.Left}" Canvas.Top="{Binding Path=EllipseSE.Top}"/>
                                    <Ellipse x:Name="ellipseS" Canvas.Left="{Binding Path=EllipseS.Left}" Canvas.Top="{Binding Path=EllipseS.Top}"/>
                                    <Ellipse x:Name="ellipseSW" Canvas.Left="{Binding Path=EllipseSW.Left}" Canvas.Top="{Binding Path=EllipseSW.Top}"/>
                                    <Ellipse x:Name="ellipseW" Canvas.Left="{Binding Path=EllipseW.Left}" Canvas.Top="{Binding Path=EllipseW.Top}"/>
                                    <Ellipse x:Name="ellipseNW" Canvas.Left="{Binding Path=EllipseNW.Left}" Canvas.Top="{Binding Path=EllipseNW.Top}"/>

                                </Canvas>
                                <Label Content="{Binding Path=Text}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                            </Grid>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>
    <Border>
        <Control Style="{StaticResource BusyAnimationStyle}"/>
    </Border>
</UserControl>

代码隐藏(C#)

using System;
using System.Windows;
using System.Windows.Controls;

namespace WpfApplication2
{
    /// <summary>
    /// Interaction logic for Spinner.xaml
    /// </summary>
    public partial class Spinner : UserControl
    {
        public int EllipseSize { get; set; } = 8;
        public int SpinnerHeight { get; set; } = 0;
        public int SpinnerWidth { get; set; } = 0;


        // start positions
        public EllipseStartPosition EllipseN { get; private set; }
        public EllipseStartPosition EllipseNE { get; private set; }
        public EllipseStartPosition EllipseE { get; private set; }
        public EllipseStartPosition EllipseSE { get; private set; }
        public EllipseStartPosition EllipseS { get; private set; }
        public EllipseStartPosition EllipseSW { get; private set; }
        public EllipseStartPosition EllipseW { get; private set; }
        public EllipseStartPosition EllipseNW { get; private set; }

        public Spinner()
        {
            InitializeComponent();
        }

        private void initialSetup()
        {
            float horizontalCenter = (float)(SpinnerWidth / 2);
            float verticalCenter = (float)(SpinnerHeight / 2);
            float distance = (float)Math.Min(SpinnerHeight, SpinnerWidth) /2;

            double angleInRadians = 44.8;
            float cosine = (float)Math.Cos(angleInRadians);
            float sine = (float)Math.Sin(angleInRadians);

            EllipseN = newPos(left: horizontalCenter, top: verticalCenter - distance);
            EllipseNE = newPos(left: horizontalCenter + (distance * cosine), top: verticalCenter - (distance * sine));
            EllipseE = newPos(left: horizontalCenter + distance, top: verticalCenter);
            EllipseSE = newPos(left: horizontalCenter + (distance * cosine), top: verticalCenter + (distance * sine));
            EllipseS = newPos(left: horizontalCenter, top: verticalCenter + distance);
            EllipseSW = newPos(left: horizontalCenter - (distance * cosine), top: verticalCenter + (distance * sine));
            EllipseW = newPos(left: horizontalCenter - distance, top: verticalCenter);
            EllipseNW = newPos(left: horizontalCenter - (distance * cosine), top: verticalCenter - (distance * sine));
        }

        private EllipseStartPosition newPos(float left, float top)
        {
            return new EllipseStartPosition() { Left = left, Top = top };
        }

        
        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            if(e.Property.Name == "Height")
            {
                SpinnerHeight = Convert.ToInt32(e.NewValue);
            }

            if (e.Property.Name == "Width")
            {
                SpinnerWidth = Convert.ToInt32(e.NewValue);
            }

            if(SpinnerHeight > 0 && SpinnerWidth > 0)
            {
                initialSetup();
            }

            base.OnPropertyChanged(e);
        }
    }

    public struct EllipseStartPosition
    {
        public float Left { get; set; }
        public float Top { get; set; }
    }
}

示例使用

<Window x:Class="WpfApplication2.MainWindow"
        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:WpfApplication2"
        xmlns:animated="WpfApplication2.MainWindow"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">

    <StackPanel Background="DarkGoldenrod" Width="200" Height="200" VerticalAlignment="Top" HorizontalAlignment="Left" >
        <Button Height="35">
            <Button.Content >
                <DockPanel LastChildFill="True" Height="NaN" Width="NaN" HorizontalAlignment="Left">
                    <local:Spinner EllipseSize="4" DockPanel.Dock="Left" HorizontalAlignment="Left" Margin="0,0,10,5" Height="16" Width="16"/>
                    <TextBlock Text="Cancel" VerticalAlignment="Center"/>
                </DockPanel>
            </Button.Content>
        </Button>

    </StackPanel>

</Window>

3
投票

github 上的这个 repo 似乎做得很好:

https://github.com/blackspikeltd/Xaml-Spinners-WPF

旋转器重量轻,可以轻松放置在需要的地方。存储库中包含一个示例项目,展示了如何使用它们。

也没有带有一堆逻辑的令人讨厌的代码隐藏。如果需要 MVVM 支持,只需将它们放入具有可见性绑定的网格中即可。


3
投票

这是一个全 xaml 解决方案的示例。它绑定到视图模型中的“IsWorking”布尔值以显示控件并启动动画。

<UserControl x:Class="MainApp.Views.SpinnerView"
         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" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">

    <UserControl.Resources>
        <BooleanToVisibilityConverter x:Key="BoolToVisConverter"/>
    </UserControl.Resources>

    <StackPanel Orientation="Horizontal" Margin="5"
                     Visibility="{Binding IsWorking, Converter={StaticResource BoolToVisConverter}}">
        <Label>Wait...</Label>
        <Ellipse x:Name="spinnerEllipse" 
                     Width="20" Height="20">
            <Ellipse.Fill>
                <LinearGradientBrush StartPoint="1,1" EndPoint="0,0" >
                    <GradientStop Color="White" Offset="0"/>
                    <GradientStop Color="CornflowerBlue" Offset="1"/>
                </LinearGradientBrush>
            </Ellipse.Fill>
            <Ellipse.RenderTransform>
                <RotateTransform x:Name="SpinnerRotate" CenterX="10" CenterY="10"/>
            </Ellipse.RenderTransform>
            <Ellipse.Style>
                <Style TargetType="Ellipse">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding IsWorking}" Value="True">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard x:Name="SpinStoryboard">
                                    <Storyboard TargetProperty="RenderTransform.Angle" >
                                        <DoubleAnimation
                                            From="0" To="360" Duration="0:0:01"
                                            RepeatBehavior="Forever" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.EnterActions>
                            <DataTrigger.ExitActions>
                                <StopStoryboard BeginStoryboardName="SpinStoryboard"></StopStoryboard>
                            </DataTrigger.ExitActions>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Ellipse.Style>
        </Ellipse>
    </StackPanel>
</UserControl>  

2
投票

使用枚举类型来指示 ViewModel 的状态

public enum ViewModeType
{
    Default, 
    Busy
    //etc.
}

然后在您的 ViewModels 基类中使用属性

public ViewModeType ViewMode
{
    get { return this.viewMode; }
    set
    {
        if (this.viewMode != value)
        {
            this.viewMode = value;
                            //You should notify property changed here
        }
    }
}

并在视图中触发 ViewMode,如果忙则显示忙指示器:

<Trigger Property="ViewMode" Value="Busy">
    <!-- Show BusyIndicator -->
</Trigger>

1
投票

@Menol 发布的定制旋转器有一个小问题,旋转器会向下和向右移动一个点的大小。我已经更新了代码,以便通过减去一半的点来补偿此偏移。

这是更新后的代码:

    private void initialSetup()
    {
        float horizontalCenter = (float)(SpinnerWidth / 2);
        float verticalCenter = (float)(SpinnerHeight / 2);
        float distance = (float)Math.Min(SpinnerHeight, SpinnerWidth) / 2;
        float dotComp = (float)(EllipseSize / 2);

        double angleInRadians = 44.8;
        float cosine = (float)Math.Cos(angleInRadians);
        float sine = (float)Math.Sin(angleInRadians);

        EllipseN = newPos(left: horizontalCenter - dotComp, top: verticalCenter - distance - dotComp);
        EllipseNE = newPos(left: horizontalCenter + (distance * cosine) - dotComp, top: verticalCenter - (distance * sine) - dotComp);
        EllipseE = newPos(left: horizontalCenter + distance - dotComp, top: verticalCenter - dotComp);
        EllipseSE = newPos(left: horizontalCenter + (distance * cosine) - dotComp, top: verticalCenter + (distance * sine) - dotComp);
        EllipseS = newPos(left: horizontalCenter - dotComp, top: verticalCenter + distance - dotComp);
        EllipseSW = newPos(left: horizontalCenter - (distance * cosine) - dotComp, top: verticalCenter + (distance * sine) - dotComp);
        EllipseW = newPos(left: horizontalCenter - distance - dotComp, top: verticalCenter - dotComp);
        EllipseNW = newPos(left: horizontalCenter - (distance * cosine) - dotComp, top: verticalCenter - (distance * sine) - dotComp);
    }

0
投票

CircularProgressBarBlue.xaml

<UserControl 
x:Class="CircularProgressBarBlue"   
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="Transparent"
Name="progressBar">
<UserControl.Resources>
    <Storyboard x:Key="spinning" >
        <DoubleAnimation 
            Storyboard.TargetName="SpinnerRotate" 
            Storyboard.TargetProperty="(RotateTransform.Angle)"                 
            From="0" 
            To="360"                 
            RepeatBehavior="Forever"/>
    </Storyboard>
</UserControl.Resources>
<Grid 
    x:Name="LayoutRoot" 
    Background="Transparent" 
    HorizontalAlignment="Center" 
    VerticalAlignment="Center">
    <Image Source="C:\SpinnerImage\BlueSpinner.png" RenderTransformOrigin="0.5,0.5" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
        <Image.RenderTransform>
            <RotateTransform 
                x:Name="SpinnerRotate"
                Angle="0"/>
        </Image.RenderTransform>
    </Image>


</Grid>

CircularProgressBarBlue.xaml.cs

using System;

using System.Windows;

using System.Windows.Media.Animation;


        /// <summary>
    /// Interaction logic for CircularProgressBarBlue.xaml
    /// </summary>
    public partial class CircularProgressBarBlue
    {
        private Storyboard _sb;

        public CircularProgressBarBlue()
        {
            InitializeComponent();
            StartStoryBoard();
            IsVisibleChanged += CircularProgressBarBlueIsVisibleChanged;
        }

        void CircularProgressBarBlueIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (_sb == null) return;
            if (e != null && e.NewValue != null && (((bool)e.NewValue)))
            {
                _sb.Begin();
                _sb.Resume();
            }
            else
            {
                _sb.Stop();
            }
        }

        void StartStoryBoard()
        {
            try
            {
                _sb = (Storyboard)TryFindResource("spinning");
                if (_sb != null)
                    _sb.Begin();
            }
            catch
            { }
        }
    }

0
投票

该方法是使用应用了动画的几何图形。将所需的几何图形添加到 Path 并为其 RotateTransform 设置 0-360° 的动画。

我的微调器支持两种类型的微调器:

  1. 圆圈:
  2. 戒指:

中心逻辑如下:

if(spinner.SpinnerType == SpinnerType.Ring)
{
 double innerRad = spinner.Radius - spinner.ItemRadius;
 Point center = new Point(0, 0);
 grp.Children.Add(new EllipseGeometry( center, spinner.Radius, spinner.Radius));
 grp.Children.Add(new EllipseGeometry(center, innerRad, innerRad));                
 return;
}
var points = GetPointsOnCircle( spinner.Diameter/ 2);
double r = spinner.ItemRadius;
foreach (var point in points)
{
 grp.Children.Add(new EllipseGeometry(point, r, r));
 r -= spinner.ContinuousSizeReduction;
}

使用方法简单如下:

<local:SpinnerControl Diameter="60" Fill="#FFE8B311"/>

这里是源代码!


0
投票
<Grid Width="100" Height="100">
<Ellipse Width="20" Height="20" Stroke="Gray" StrokeThickness="4">
    <Ellipse.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation
                        Storyboard.TargetName="rotateTransform"
                        Storyboard.TargetProperty="Angle"
                        From="0" To="360"
                        Duration="0:0:1"
                        RepeatBehavior="Forever" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Ellipse.Triggers>
    <Ellipse.RenderTransform>
        <RotateTransform x:Name="rotateTransform" CenterX="10" CenterY="10"/>
    </Ellipse.RenderTransform>
</Ellipse>

在此示例中,使用描边为“灰色”且描边厚度为“4”的椭圆来创建环形。 RenderTransform 属性用于使用 RotateTransform 对象旋转 Ellipse,动画由 FrameworkElement 的 Loaded 事件触发。 DoubleAnimation 的持续时间设置为“0:0:1”,这意味着旋转需要 1 秒才能完成。 RepeatBehavior 设置为“Forever”,因此动画将无限期地重复。

其他方形旋转器:

<Grid Width="100" Height="100">
<Path Stroke="Gray" StrokeThickness="4" StrokeEndLineCap="Round">
    <Path.Data>
        <PathGeometry>
            <PathFigure StartPoint="50,10">
                <LineSegment Point="10,50" />
                <LineSegment Point="50,90" />
                <LineSegment Point="90,50" />
                <LineSegment Point="50,10" />
            </PathFigure>
        </PathGeometry>
    </Path.Data>
    <Path.Triggers>
        <EventTrigger RoutedEvent="FrameworkElement.Loaded">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation
                        Storyboard.TargetName="rotateTransform"
                        Storyboard.TargetProperty="Angle"
                        From="0" To="360"
                        Duration="0:0:1"
                        RepeatBehavior="Forever" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Path.Triggers>
    <Path.RenderTransform>
        <RotateTransform x:Name="rotateTransform" CenterX="50" CenterY="50" />
    </Path.RenderTransform>
</Path>

Pic


0
投票

我想我也会添加我的解决方案,因为它不需要任何外部资源(例如图像),并且可以无限缩放而不会损失质量,您可以将路径绑定。IsEnabled 到指示旋转器是否旋转的属性:

结果:

XAML:

<Grid Background="White">
    <Viewbox Height="30" Width="30" Stretch="Fill">
        <Path Stretch="Fill" Fill="Black" Data="M20,12A8,8 0 0,1 12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4C12.76,4 13.5,4.11 14.2,4.31L15.77,2.74C14.61,2.26 13.34,2 12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12" RenderTransformOrigin="0.5,0.5">
            <Path.RenderTransform>
                <RotateTransform/>
            </Path.RenderTransform>

            <Path.Style>
                <Style>
                    <Style.Triggers>
                        <Trigger Property="Image.IsEnabled" Value="True">
                            <Trigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <DoubleAnimation
        Storyboard.TargetProperty="RenderTransform.Angle"
        From="0"
        To="360"
        Duration="0:0:1"
        RepeatBehavior="Forever" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.EnterActions>
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </Path.Style>
        </Path>
    </Viewbox>
</Grid>
© www.soinside.com 2019 - 2024. All rights reserved.