wpf 三种 loading 样式

3 篇文章 0 订阅

1.ProgressRing

cs代码

 [TemplateVisualState(GroupName = VisualStates.GroupActive, Name = VisualStates.StateActive)]
    [TemplateVisualState(GroupName = VisualStates.GroupActive, Name = VisualStates.StateInactive)]
    public partial class ProgressRing: Control
    {
        private bool hasAppliedTemplate = false;
        public bool IsActive
        {
            get { return (bool)GetValue(IsActiveProperty); }
            set { SetValue(IsActiveProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsActiveProperty =
            DependencyProperty.Register("IsActive", typeof(bool), typeof(ProgressRing), new PropertyMetadata(false,new PropertyChangedCallback (OnActiveChanged)));

        public TemplateSettingValues TemplateSettings
        {
            get { return (TemplateSettingValues)GetValue(TemplateSettingsProperty); }
            set { SetValue(TemplateSettingsProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TemplateSettings.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TemplateSettingsProperty =
            DependencyProperty.Register("TemplateSettings", typeof(TemplateSettingValues), typeof(ProgressRing), new PropertyMetadata(null));

        private static void OnActiveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var pr = (ProgressRing)d;
            var isActive = (bool)e.NewValue;
            pr.UpdateState(isActive);
        }

        public ProgressRing()
        {
            DefaultStyleKey = typeof(ProgressRing);
            TemplateSettings = new TemplateSettingValues(150);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            hasAppliedTemplate = true;
            UpdateState(IsActive);
        }

        private void UpdateState(bool isActive)
        {
            if (hasAppliedTemplate)
            {
                string state = isActive ? VisualStates.StateActive : VisualStates.StateInactive;
                VisualStateManager.GoToState(this, state, true);
            }
        }
    }
public class TemplateSettingValues : System.Windows.DependencyObject
    {
        // Using a DependencyProperty as the backing store for MaxSideLength.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MaxSideLengthProperty =
            DependencyProperty.Register("MaxSideLength", typeof(double), typeof(TemplateSettingValues), new PropertyMetadata(0D));

        // Using a DependencyProperty as the backing store for EllipseDiameter.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty EllipseDiameterProperty =
            DependencyProperty.Register("EllipseDiameter", typeof(double), typeof(TemplateSettingValues), new PropertyMetadata(0D));

        // Using a DependencyProperty as the backing store for EllipseOffset.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty EllipseOffsetProperty =
            DependencyProperty.Register("EllipseOffset", typeof(Thickness), typeof(TemplateSettingValues), new PropertyMetadata(default(Thickness)));

        public TemplateSettingValues(double width)
        {
            if (width <= 40)
            {
                EllipseDiameter = (width / 10) + 1;
            }
            else
            {
                EllipseDiameter = width / 10;
            }
            MaxSideLength = width - EllipseDiameter;
            EllipseOffset = new System.Windows.Thickness(0, EllipseDiameter * 2.5, 0, 0);
        }

        public double MaxSideLength
        {
            get { return (double)GetValue(MaxSideLengthProperty); }
            set { SetValue(MaxSideLengthProperty, value); }
        }

        public double EllipseDiameter
        {
            get { return (double)GetValue(EllipseDiameterProperty); }
            set { SetValue(EllipseDiameterProperty, value); }
        }

        public Thickness EllipseOffset
        {
            get { return (Thickness)GetValue(EllipseOffsetProperty); }
            set { SetValue(EllipseOffsetProperty, value); }
        }
    }

 

xaml代码:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:WpfAppStyle.ProgressRing">

    <Style TargetType="{x:Type local:ProgressRing}">
        <Setter Property="Background"
                Value="Transparent" />
        <Setter Property="Foreground"
                Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
        <Setter Property="IsHitTestVisible"
                Value="False" />
        <Setter Property="HorizontalAlignment"
                Value="Center" />
        <Setter Property="VerticalAlignment"
                Value="Center" />
        <Setter Property="MinHeight"
                Value="20" />
        <Setter Property="MinWidth"
                Value="20" />
        <Setter Property="IsTabStop"
                Value="False" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ProgressRing}">
                    <Border x:Name="ProgressRingRoot"
                            Background="{TemplateBinding Background}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            BorderBrush="{TemplateBinding BorderBrush}">
                        <Border.Resources>
                            <Style x:Key="ProgressRingEllipseStyle"
                                   TargetType="Ellipse">
                                <Setter Property="Opacity" Value="0" />
                                <Setter Property="HorizontalAlignment" Value="Left" />
                                <Setter Property="VerticalAlignment" Value="Top" />
                            </Style>
                        </Border.Resources>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="SizeStates">
                                <VisualState x:Name="Large">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="SixthCircle" Storyboard.TargetProperty="Visibility">
                                            <DiscreteObjectKeyFrame KeyTime="0">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Visible</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Small" />
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="ActiveStates">
                                <VisualState x:Name="Inactive" />
                                <VisualState x:Name="Active">
                                    <Storyboard RepeatBehavior="Forever">
                                        <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetName="Ring" Storyboard.TargetProperty="Visibility">
                                            <DiscreteObjectKeyFrame KeyTime="0">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Visible</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E1" Storyboard.TargetProperty="Opacity" BeginTime="0">
                                            <DiscreteDoubleKeyFrame KeyTime="0" Value="1" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.21" Value="1" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.22" Value="0" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.47" Value="0" />
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E2" Storyboard.TargetProperty="Opacity" BeginTime="00:00:00.167">
                                            <DiscreteDoubleKeyFrame KeyTime="0" Value="1" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.21" Value="1" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.22" Value="0" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.47" Value="0" />
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E3" Storyboard.TargetProperty="Opacity" BeginTime="00:00:00.334">
                                            <DiscreteDoubleKeyFrame KeyTime="0" Value="1" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.21" Value="1" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.22" Value="0" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.47" Value="0" />
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E4" Storyboard.TargetProperty="Opacity" BeginTime="00:00:00.501">
                                            <DiscreteDoubleKeyFrame KeyTime="0" Value="1" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.21" Value="1" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.22" Value="0" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.47" Value="0" />
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E5" Storyboard.TargetProperty="Opacity" BeginTime="00:00:00.668">
                                            <DiscreteDoubleKeyFrame KeyTime="0" Value="1" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.21" Value="1" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.22" Value="0" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.47" Value="0" />
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E6" Storyboard.TargetProperty="Opacity" BeginTime="00:00:00.835">
                                            <DiscreteDoubleKeyFrame KeyTime="0" Value="1" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.21" Value="1" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.22" Value="0" />
                                            <DiscreteDoubleKeyFrame KeyTime="0:0:3.47" Value="0" />
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E1R" BeginTime="0" Storyboard.TargetProperty="Angle">
                                            <SplineDoubleKeyFrame KeyTime="0" Value="-110" KeySpline="0.13,0.21,0.1,0.7" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:0.433" Value="10" KeySpline="0.02,0.33,0.38,0.77" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:1.2" Value="93" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:1.617" Value="205" KeySpline="0.57,0.17,0.95,0.75" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:2.017" Value="357" KeySpline="0,0.19,0.07,0.72" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:2.783" Value="439" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:3.217" Value="585" KeySpline="0,0,0.95,0.37" />
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E2R" BeginTime="00:00:00.167" Storyboard.TargetProperty="Angle">
                                            <SplineDoubleKeyFrame KeyTime="0" Value="-116" KeySpline="0.13,0.21,0.1,0.7" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:0.433" Value="4" KeySpline="0.02,0.33,0.38,0.77" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:1.2" Value="87" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:1.617" Value="199" KeySpline="0.57,0.17,0.95,0.75" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:2.017" Value="351" KeySpline="0,0.19,0.07,0.72" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:2.783" Value="433" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:3.217" Value="579" KeySpline="0,0,0.95,0.37" />
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E3R" BeginTime="00:00:00.334" Storyboard.TargetProperty="Angle">
                                            <SplineDoubleKeyFrame KeyTime="0" Value="-122" KeySpline="0.13,0.21,0.1,0.7" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:0.433" Value="-2" KeySpline="0.02,0.33,0.38,0.77" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:1.2" Value="81" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:1.617" Value="193" KeySpline="0.57,0.17,0.95,0.75" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:2.017" Value="345" KeySpline="0,0.19,0.07,0.72" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:2.783" Value="427" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:3.217" Value="573" KeySpline="0,0,0.95,0.37" />
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E4R" BeginTime="00:00:00.501" Storyboard.TargetProperty="Angle">
                                            <SplineDoubleKeyFrame KeyTime="0" Value="-128" KeySpline="0.13,0.21,0.1,0.7" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:0.433" Value="-8" KeySpline="0.02,0.33,0.38,0.77" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:1.2" Value="75" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:1.617" Value="187" KeySpline="0.57,0.17,0.95,0.75" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:2.017" Value="339" KeySpline="0,0.19,0.07,0.72" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:2.783" Value="421" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:3.217" Value="567" KeySpline="0,0,0.95,0.37" />
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E5R" BeginTime="00:00:00.668" Storyboard.TargetProperty="Angle">
                                            <SplineDoubleKeyFrame KeyTime="0" Value="-134" KeySpline="0.13,0.21,0.1,0.7" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:0.433" Value="-14" KeySpline="0.02,0.33,0.38,0.77" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:1.2" Value="69" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:1.617" Value="181" KeySpline="0.57,0.17,0.95,0.75" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:2.017" Value="331" KeySpline="0,0.19,0.07,0.72" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:2.783" Value="415" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:3.217" Value="561" KeySpline="0,0,0.95,0.37" />
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E6R" BeginTime="00:00:00.835" Storyboard.TargetProperty="Angle">
                                            <SplineDoubleKeyFrame KeyTime="0" Value="-140" KeySpline="0.13,0.21,0.1,0.7" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:0.433" Value="-20" KeySpline="0.02,0.33,0.38,0.77" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:1.2" Value="63" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:1.617" Value="175" KeySpline="0.57,0.17,0.95,0.75" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:2.017" Value="325" KeySpline="0,0.19,0.07,0.72" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:2.783" Value="409" />
                                            <SplineDoubleKeyFrame KeyTime="0:0:3.217" Value="555" KeySpline="0,0,0.95,0.37" />
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Grid x:Name="Ring"
                              Background="{TemplateBinding Background}"
                              MaxWidth="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.MaxSideLength}"
                              MaxHeight="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.MaxSideLength}"
                              Visibility="Collapsed"
                              RenderTransformOrigin=".5,.5"
                              FlowDirection="LeftToRight">
                            <Canvas RenderTransformOrigin=".5,.5">
                                <Canvas.RenderTransform>
                                    <RotateTransform x:Name="E1R" />
                                </Canvas.RenderTransform>
                                <Ellipse x:Name="E1"
                                Style="{StaticResource ProgressRingEllipseStyle}"
                                Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
                                Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
                                Margin="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseOffset}"
                                Fill="{TemplateBinding Foreground}" />
                            </Canvas>
                            <Canvas RenderTransformOrigin=".5,.5">
                                <Canvas.RenderTransform>
                                    <RotateTransform x:Name="E2R" />
                                </Canvas.RenderTransform>
                                <Ellipse x:Name="E2"
                                Style="{StaticResource ProgressRingEllipseStyle}"
                                Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
                                Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
                                Margin="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseOffset}"
                                Fill="{TemplateBinding Foreground}" />
                            </Canvas>
                            <Canvas RenderTransformOrigin=".5,.5">
                                <Canvas.RenderTransform>
                                    <RotateTransform x:Name="E3R" />
                                </Canvas.RenderTransform>
                                <Ellipse x:Name="E3"
                                Style="{StaticResource ProgressRingEllipseStyle}"
                                Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
                                Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
                                Margin="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseOffset}"
                                Fill="{TemplateBinding Foreground}" />
                            </Canvas>
                            <Canvas RenderTransformOrigin=".5,.5">
                                <Canvas.RenderTransform>
                                    <RotateTransform x:Name="E4R" />
                                </Canvas.RenderTransform>
                                <Ellipse x:Name="E4"
                                Style="{StaticResource ProgressRingEllipseStyle}"
                                Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
                                Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
                                Margin="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseOffset}"
                                Fill="{TemplateBinding Foreground}" />
                            </Canvas>
                            <Canvas RenderTransformOrigin=".5,.5">
                                <Canvas.RenderTransform>
                                    <RotateTransform x:Name="E5R" />
                                </Canvas.RenderTransform>
                                <Ellipse x:Name="E5"
                                Style="{StaticResource ProgressRingEllipseStyle}"
                                Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
                                Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
                                Margin="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseOffset}"
                                Fill="{TemplateBinding Foreground}" />
                            </Canvas>
                            <Canvas RenderTransformOrigin=".5,.5" Visibility="Collapsed" x:Name="SixthCircle">
                                <Canvas.RenderTransform>
                                    <RotateTransform x:Name="E6R" />
                                </Canvas.RenderTransform>
                                <Ellipse x:Name="E6"
                                Style="{StaticResource ProgressRingEllipseStyle}"
                                Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
                                Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseDiameter}"
                                Margin="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.EllipseOffset}"
                                Fill="{TemplateBinding Foreground}" />
                            </Canvas>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

使用:

<Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/WpfAppStyle;component/ProgressRing/ProgressRing.xaml" />
        </ResourceDictionary>
    </Window.Resources>

 <progressRing:ProgressRing IsActive="False" Width="150" Height="150" x:Name="progressRing" />

效果:

2.busyIndicator

cs代码


    /// <summary>
    /// A control to provide a visual indicator when an application is busy.
    /// </summary>
    [TemplateVisualState(Name = VisualState4BusyIndicator.StateIdle, GroupName = VisualState4BusyIndicator.GroupBusyStatus)]
    [TemplateVisualState(Name = VisualState4BusyIndicator.StateBusy, GroupName = VisualState4BusyIndicator.GroupBusyStatus)]
    [TemplateVisualState(Name = VisualState4BusyIndicator.StateVisible, GroupName = VisualState4BusyIndicator.GroupVisibility)]
    [TemplateVisualState(Name = VisualState4BusyIndicator.StateHidden, GroupName = VisualState4BusyIndicator.GroupVisibility)]
    [StyleTypedProperty(Property = "OverlayStyle", StyleTargetType = typeof(Rectangle))]
    [StyleTypedProperty(Property = "ProgressBarStyle", StyleTargetType = typeof(ProgressBar))]
    public partial class BusyIndicator : ContentControl
    {
        #region Private Members

        /// <summary>
        /// Timer used to delay the initial display and avoid flickering.
        /// </summary>
        private DispatcherTimer _displayAfterTimer = new DispatcherTimer();

        #endregion //Private Members

        #region Constructors

        static BusyIndicator()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(BusyIndicator), new FrameworkPropertyMetadata(typeof(BusyIndicator)));
        }

        public BusyIndicator()
        {
            _displayAfterTimer.Tick += DisplayAfterTimerElapsed;
        }

        #endregion //Constructors

        #region Base Class Overrides

        /// <summary>
        /// Overrides the OnApplyTemplate method.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            ChangeVisualState(false);
        }

        #endregion //Base Class Overrides

        #region Properties

        /// <summary>
        /// Gets or sets a value indicating whether the BusyContent is visible.
        /// </summary>
        protected bool IsContentVisible
        {
            get;
            set;
        }

        #endregion //Properties

        #region Dependency Properties

        #region IsBusy

        /// <summary>
        /// Identifies the IsBusy dependency property.
        /// </summary>
        public static readonly DependencyProperty IsBusyProperty = DependencyProperty.Register(
            "IsBusy",
            typeof(bool),
            typeof(BusyIndicator),
            new PropertyMetadata(false, new PropertyChangedCallback(OnIsBusyChanged)));

        /// <summary>
        /// Gets or sets a value indicating whether the busy indicator should show.
        /// </summary>
        public bool IsBusy
        {
            get
            {
                return (bool)GetValue(IsBusyProperty);
            }
            set
            {
                SetValue(IsBusyProperty, value);
            }
        }

        /// <summary>
        /// IsBusyProperty property changed handler.
        /// </summary>
        /// <param name="d">BusyIndicator that changed its IsBusy.</param>
        /// <param name="e">Event arguments.</param>
        private static void OnIsBusyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((BusyIndicator)d).OnIsBusyChanged(e);
        }

        /// <summary>
        /// IsBusyProperty property changed handler.
        /// </summary>
        /// <param name="e">Event arguments.</param>
        protected virtual void OnIsBusyChanged(DependencyPropertyChangedEventArgs e)
        {
            if (IsBusy)
            {
                if (DisplayAfter.Equals(TimeSpan.Zero))
                {
                    // Go visible now
                    IsContentVisible = true;
                }
                else
                {
                    // Set a timer to go visible
                    _displayAfterTimer.Interval = DisplayAfter;
                    _displayAfterTimer.Start();
                }
            }
            else
            {
                // No longer visible
                _displayAfterTimer.Stop();
                IsContentVisible = false;

                if (this.FocusAfterBusy != null)
                {
                    this.FocusAfterBusy.Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() =>
                    {
                        this.FocusAfterBusy.Focus();
                    }
                    ));
                }
            }

            ChangeVisualState(true);
        }

        #endregion //IsBusy

        #region Busy Content

        /// <summary>
        /// Identifies the BusyContent dependency property.
        /// </summary>
        public static readonly DependencyProperty BusyContentProperty = DependencyProperty.Register(
            "BusyContent",
            typeof(object),
            typeof(BusyIndicator),
            new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets a value indicating the busy content to display to the user.
        /// </summary>
        public object BusyContent
        {
            get
            {
                return (object)GetValue(BusyContentProperty);
            }
            set
            {
                SetValue(BusyContentProperty, value);
            }
        }

        #endregion //Busy Content

        #region Busy Content Template

        /// <summary>
        /// Identifies the BusyTemplate dependency property.
        /// </summary>
        public static readonly DependencyProperty BusyContentTemplateProperty = DependencyProperty.Register(
            "BusyContentTemplate",
            typeof(DataTemplate),
            typeof(BusyIndicator),
            new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets a value indicating the template to use for displaying the busy content to the user.
        /// </summary>
        public DataTemplate BusyContentTemplate
        {
            get
            {
                return (DataTemplate)GetValue(BusyContentTemplateProperty);
            }
            set
            {
                SetValue(BusyContentTemplateProperty, value);
            }
        }

        #endregion //Busy Content Template

        #region Display After

        /// <summary>
        /// Identifies the DisplayAfter dependency property.
        /// </summary>
        public static readonly DependencyProperty DisplayAfterProperty = DependencyProperty.Register(
            "DisplayAfter",
            typeof(TimeSpan),
            typeof(BusyIndicator),
            new PropertyMetadata(TimeSpan.FromSeconds(0.1)));

        /// <summary>
        /// Gets or sets a value indicating how long to delay before displaying the busy content.
        /// </summary>
        public TimeSpan DisplayAfter
        {
            get
            {
                return (TimeSpan)GetValue(DisplayAfterProperty);
            }
            set
            {
                SetValue(DisplayAfterProperty, value);
            }
        }

        #endregion //Display After

        #region FocusAfterBusy

        /// <summary>
        /// Identifies the FocusAfterBusy dependency property.
        /// </summary>
        public static readonly DependencyProperty FocusAfterBusyProperty = DependencyProperty.Register(
            "FocusAfterBusy",
            typeof(Control),
            typeof(BusyIndicator),
            new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets a Control that should get focus when the busy indicator disapears.
        /// </summary>
        public Control FocusAfterBusy
        {
            get
            {
                return (Control)GetValue(FocusAfterBusyProperty);
            }
            set
            {
                SetValue(FocusAfterBusyProperty, value);
            }
        }

        #endregion //FocusAfterBusy

        #region Overlay Style

        /// <summary>
        /// Identifies the OverlayStyle dependency property.
        /// </summary>
        public static readonly DependencyProperty OverlayStyleProperty = DependencyProperty.Register(
            "OverlayStyle",
            typeof(Style),
            typeof(BusyIndicator),
            new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets a value indicating the style to use for the overlay.
        /// </summary>
        public Style OverlayStyle
        {
            get
            {
                return (Style)GetValue(OverlayStyleProperty);
            }
            set
            {
                SetValue(OverlayStyleProperty, value);
            }
        }
        #endregion //Overlay Style

        #region ProgressBar Style

        /// <summary>
        /// Identifies the ProgressBarStyle dependency property.
        /// </summary>
        public static readonly DependencyProperty ProgressBarStyleProperty = DependencyProperty.Register(
            "ProgressBarStyle",
            typeof(Style),
            typeof(BusyIndicator),
            new PropertyMetadata(null));

        /// <summary>
        /// Gets or sets a value indicating the style to use for the progress bar.
        /// </summary>
        public Style ProgressBarStyle
        {
            get
            {
                return (Style)GetValue(ProgressBarStyleProperty);
            }
            set
            {
                SetValue(ProgressBarStyleProperty, value);
            }
        }

        #endregion //ProgressBar Style

        #endregion //Dependency Properties

        #region Methods

        /// <summary>
        /// Handler for the DisplayAfterTimer.
        /// </summary>
        /// <param name="sender">Event sender.</param>
        /// <param name="e">Event arguments.</param>
        private void DisplayAfterTimerElapsed(object sender, EventArgs e)
        {
            _displayAfterTimer.Stop();
            IsContentVisible = true;
            ChangeVisualState(true);
        }

        /// <summary>
        /// Changes the control's visual state(s).
        /// </summary>
        /// <param name="useTransitions">True if state transitions should be used.</param>
        protected virtual void ChangeVisualState(bool useTransitions)
        {
            VisualStateManager.GoToState(this, IsBusy ? VisualState4BusyIndicator.StateBusy : VisualState4BusyIndicator.StateIdle, useTransitions);
            VisualStateManager.GoToState(this, IsContentVisible ? VisualState4BusyIndicator.StateVisible : VisualState4BusyIndicator.StateHidden, useTransitions);
        }

        #endregion //Methods
    }
 public class ProgressBarWidthConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var contentWidth = (double)values[0];
            var parentMinWidth = (double)values[1];

            return Math.Max(contentWidth, parentMinWidth);
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
public class VisualState4BusyIndicator
    {
        /// <summary>
        /// Busyness group name.
        /// </summary>
        public const string GroupBusyStatus = "BusyStatusStates";

        /// <summary>
        /// Busy state for BusyIndicator.
        /// </summary>
        public const string StateBusy = "Busy";

        /// <summary>
        /// Idle state for BusyIndicator.
        /// </summary>
        public const string StateIdle = "Idle";

        /// <summary>
        /// BusyDisplay group.
        /// </summary>
        public const string GroupVisibility = "VisibilityStates";

        /// <summary>
        /// Visible state name for BusyIndicator.
        /// </summary>
        public const string StateVisible = "Visible";

        /// <summary>
        /// Hidden state name for BusyIndicator.
        /// </summary>
        public const string StateHidden = "Hidden";
    }

xmal代码

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:converter="clr-namespace:WpfAppStyle.Busy.Converter"
                    xmlns:local="clr-namespace:WpfAppStyle.Busy">
    <converter:ProgressBarWidthConverter x:Key="ProgressBarWidthConverter" />
    <Style TargetType="{x:Type local:BusyIndicator}">
        <Setter Property="BusyContent" Value="Please wait..." />
        <Setter Property="IsTabStop" Value="False" />
        <Setter Property="Focusable" Value="False" />
        <Setter Property="OverlayStyle">
            <Setter.Value>
                <Style TargetType="Rectangle">
                    <Setter Property="Fill" Value="White" />
                    <Setter Property="Opacity" Value="0.5" />
                </Style>
            </Setter.Value>
        </Setter>
        <Setter Property="ProgressBarStyle">
            <Setter.Value>
                <Style TargetType="ProgressBar">
                    <Setter Property="IsIndeterminate" Value="True" />
                    <Setter Property="Height" Value="15" />
                    <Setter Property="Foreground" Value="BurlyWood"></Setter>
                    <Setter Property="Margin" Value="8,0,8,8" />
                </Style>
            </Setter.Value>
        </Setter>
        <Setter Property="DisplayAfter" Value="00:00:00.1" />
        <Setter Property="HorizontalAlignment" Value="Stretch" />
        <Setter Property="VerticalAlignment" Value="Stretch" />
        <Setter Property="HorizontalContentAlignment" Value="Stretch" />
        <Setter Property="VerticalContentAlignment" Value="Stretch" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:BusyIndicator}">
                    <Grid>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="VisibilityStates">
                                <VisualState x:Name="Hidden">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.001" Storyboard.TargetName="busycontent" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Collapsed</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.001" Storyboard.TargetName="overlay" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Collapsed</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Visible">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.001" Storyboard.TargetName="busycontent" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Visible</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.001" Storyboard.TargetName="overlay" Storyboard.TargetProperty="(UIElement.Visibility)">
                                            <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <Visibility>Visible</Visibility>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                            <VisualStateGroup x:Name="BusyStatusStates">
                                <VisualState x:Name="Idle">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.001" Storyboard.TargetName="content" Storyboard.TargetProperty="(Control.IsEnabled)">
                                            <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <sys:Boolean>True</sys:Boolean>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Busy">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Duration="00:00:00.001" Storyboard.TargetName="content" Storyboard.TargetProperty="(Control.IsEnabled)">
                                            <DiscreteObjectKeyFrame KeyTime="00:00:00">
                                                <DiscreteObjectKeyFrame.Value>
                                                    <sys:Boolean>False</sys:Boolean>
                                                </DiscreteObjectKeyFrame.Value>
                                            </DiscreteObjectKeyFrame>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <ContentControl x:Name="content" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" IsTabStop="False" Focusable="False" />
                        <Rectangle x:Name="overlay" Style="{TemplateBinding OverlayStyle}" />
                        <ContentPresenter x:Name="busycontent">
                            <ContentPresenter.Content>
                                <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
                                    <Border Background="White" BorderThickness="1" CornerRadius="2">
                                        <Border.BorderBrush>
                                            <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                                                <GradientStop Color="#FFA3AEB9" Offset="0" />
                                                <GradientStop Color="#FF8399A9" Offset="0.375" />
                                                <GradientStop Color="#FF718597" Offset="0.375" />
                                                <GradientStop Color="#FF617584" Offset="1" />
                                            </LinearGradientBrush>
                                        </Border.BorderBrush>
                                        <Border CornerRadius="1.5" Margin="1">
                                            <Border.Background>
                                                <LinearGradientBrush StartPoint="0.5,0" EndPoint="0.5,1">
                                                    <GradientStop Color="#FFF6F8F9" Offset="0.02" />
                                                    <GradientStop Color="#FFB8B8B8" Offset="0.996" />
                                                </LinearGradientBrush>
                                            </Border.Background>
                                            <Grid x:Name="_grid" 
                                       MinWidth="150">
                                                <Grid.RowDefinitions>
                                                    <RowDefinition />
                                                    <RowDefinition Height="Auto" />
                                                </Grid.RowDefinitions>
                                                <ContentPresenter x:Name="busyContent"
                                                      Content="{TemplateBinding BusyContent}"
                                                      ContentTemplate="{TemplateBinding BusyContentTemplate}"
                                                      HorizontalAlignment="Center"
                                                      Margin="8" />
                                                <ProgressBar Grid.Row="1" Style="{TemplateBinding ProgressBarStyle}" >
                                                    <ProgressBar.Width>
                                                        <MultiBinding Converter="{StaticResource ProgressBarWidthConverter}">
                                                            <Binding Path="ActualWidth"
                                                      ElementName="busyContent" />
                                                            <Binding Path="MinWidth"
                                                      ElementName="_grid" />
                                                        </MultiBinding>
                                                    </ProgressBar.Width>
                                                </ProgressBar>
                                            </Grid>
                                        </Border>
                                    </Border>
                                </Grid>
                            </ContentPresenter.Content>
                        </ContentPresenter>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

效果

 3.progress

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:System="clr-namespace:System;assembly=mscorlib"
                    xmlns:local="clr-namespace:WpfAppStyle.Loading">
    <System:Double x:Key="ProgressBarMinHeight">6</System:Double>
    <Style TargetType="{x:Type local:Loading}">
        <Setter Property="Background" Value="#1FFFFFFF" />
        <Setter Property="BorderBrush" Value="Transparent" />
        <Setter Property="BorderThickness" Value="0" />
        <Setter Property="Foreground" Value="#FF086F9E" />
        <Setter Property="IsTabStop" Value="False" />
        <Setter Property="Maximum" Value="100" />
        <Setter Property="MinHeight" Value="{StaticResource ProgressBarMinHeight}" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:Loading}">
                    <ControlTemplate.Resources>
                        <Storyboard x:Key="IndeterminateStoryboard" RepeatBehavior="Forever">
                            <DoubleAnimation x:Name="MainDoubleAnim"
                                             Storyboard.TargetName="EllipseGrid"
                                             Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
                                             Duration="0:0:3.917" />

                            <DoubleAnimationUsingKeyFrames x:Name="E1Anim"
                                                           Storyboard.TargetName="E1"
                                                           Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
                                <EasingDoubleKeyFrame KeyTime="0" Value="0" />
                                <SplineDoubleKeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:1" />
                                <EasingDoubleKeyFrame KeyTime="0:0:2" />
                                <SplineDoubleKeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:3" />
                            </DoubleAnimationUsingKeyFrames>

                            <DoubleAnimationUsingKeyFrames x:Name="E2Anim"
                                                           Storyboard.TargetName="E2"
                                                           Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
                                <EasingDoubleKeyFrame KeyTime="0" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:0.167" Value="0" />
                                <SplineDoubleKeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:1.167" />
                                <EasingDoubleKeyFrame KeyTime="0:0:2.167" />
                                <SplineDoubleKeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:3.167" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames x:Name="E3Anim"
                                                           Storyboard.TargetName="E3"
                                                           Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
                                <EasingDoubleKeyFrame KeyTime="0" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:0.333" Value="0" />
                                <SplineDoubleKeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:1.333" />
                                <EasingDoubleKeyFrame KeyTime="0:0:2.333" />
                                <SplineDoubleKeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:3.333" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames x:Name="E4Anim"
                                                           Storyboard.TargetName="E4"
                                                           Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
                                <EasingDoubleKeyFrame KeyTime="0" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0" />
                                <SplineDoubleKeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:1.5" />
                                <EasingDoubleKeyFrame KeyTime="0:0:2.5" />
                                <SplineDoubleKeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:3.5" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames x:Name="E5Anim"
                                                           Storyboard.TargetName="E5"
                                                           Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
                                <EasingDoubleKeyFrame KeyTime="0" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:0.667" Value="0" />
                                <SplineDoubleKeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:1.667" />
                                <EasingDoubleKeyFrame KeyTime="0:0:2.667" />
                                <SplineDoubleKeyFrame KeySpline="0.4,0,0.6,1" KeyTime="0:0:3.667" />
                            </DoubleAnimationUsingKeyFrames>

                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="B1" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
                                <EasingDoubleKeyFrame KeyTime="0" Value="-50" />
                                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:2" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:3" Value="100" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="B2" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
                                <EasingDoubleKeyFrame KeyTime="0" Value="-50" />
                                <EasingDoubleKeyFrame KeyTime="0:0:0.667" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:2.167" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:3.167" Value="100" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="B3" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
                                <EasingDoubleKeyFrame KeyTime="0" Value="-50" />
                                <EasingDoubleKeyFrame KeyTime="0:0:0.833" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:2.333" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:3.333" Value="100" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="B4" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
                                <EasingDoubleKeyFrame KeyTime="0" Value="-50" />
                                <EasingDoubleKeyFrame KeyTime="0:0:1" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:2.5" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:3.5" Value="100" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="B5" Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)">
                                <EasingDoubleKeyFrame KeyTime="0" Value="-50" />
                                <EasingDoubleKeyFrame KeyTime="0:0:1.167" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:2.667" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:3.667" Value="100" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimation Storyboard.TargetName="EllipseGrid"
                                             Storyboard.TargetProperty="Opacity"
                                             To="1"
                                             Duration="0" />

                            <DoubleAnimation Storyboard.TargetName="DeterminateRoot"
                                             Storyboard.TargetProperty="Opacity"
                                             To="0"
                                             Duration="0" />
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E1" Storyboard.TargetProperty="Opacity">
                                <EasingDoubleKeyFrame KeyTime="0" Value="1" />
                                <EasingDoubleKeyFrame KeyTime="0:0:3" Value="1" />
                                <EasingDoubleKeyFrame KeyTime="0:0:3" Value="0" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E2" Storyboard.TargetProperty="Opacity">
                                <EasingDoubleKeyFrame KeyTime="0" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:0.167" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:0.167" Value="1" />
                                <EasingDoubleKeyFrame KeyTime="0:0:3.167" Value="1" />
                                <EasingDoubleKeyFrame KeyTime="0:0:3.167" Value="0" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E3" Storyboard.TargetProperty="Opacity">
                                <EasingDoubleKeyFrame KeyTime="0" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:0.333" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:0.333" Value="1" />
                                <EasingDoubleKeyFrame KeyTime="0:0:3.333" Value="1" />
                                <EasingDoubleKeyFrame KeyTime="0:0:3.333" Value="0" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E4" Storyboard.TargetProperty="Opacity">
                                <EasingDoubleKeyFrame KeyTime="0" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1" />
                                <EasingDoubleKeyFrame KeyTime="0:0:3.5" Value="1" />
                                <EasingDoubleKeyFrame KeyTime="0:0:3.5" Value="0" />
                            </DoubleAnimationUsingKeyFrames>
                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="E5" Storyboard.TargetProperty="Opacity">
                                <EasingDoubleKeyFrame KeyTime="0" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:0.667" Value="0" />
                                <EasingDoubleKeyFrame KeyTime="0:0:0.667" Value="1" />
                                <EasingDoubleKeyFrame KeyTime="0:0:3.667" Value="1" />
                                <EasingDoubleKeyFrame KeyTime="0:0:3.667" Value="0" />
                            </DoubleAnimationUsingKeyFrames>
                        </Storyboard>
                    </ControlTemplate.Resources>
                    <Grid x:Name="ContainingGrid">
                        <Grid x:Name="EllipseClip" ClipToBounds="True">
                            <Grid x:Name="EllipseGrid" Opacity="0">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="Auto" />
                                </Grid.ColumnDefinitions>
                                <Grid.RenderTransform>
                                    <TranslateTransform />
                                </Grid.RenderTransform>
                                <Border x:Name="B1"
                                        Grid.Column="8"
                                        RenderTransformOrigin="0.5,0.5">
                                    <Border.RenderTransform>
                                        <TranslateTransform />
                                    </Border.RenderTransform>
                                    <Ellipse x:Name="E1"
                                             Width="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                             Height="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                             Fill="{TemplateBinding Foreground}"
                                             RenderTransformOrigin="0.5,0.5">
                                        <Ellipse.RenderTransform>
                                            <TranslateTransform />
                                        </Ellipse.RenderTransform>
                                    </Ellipse>
                                </Border>
                                <Rectangle Grid.Column="7" Width="{Binding EllipseOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
                                <Border x:Name="B2"
                                        Grid.Column="6"
                                        RenderTransformOrigin="0.5,0.5">
                                    <Border.RenderTransform>
                                        <TranslateTransform />
                                    </Border.RenderTransform>
                                    <Ellipse x:Name="E2"
                                             Width="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                             Height="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                             Fill="{TemplateBinding Foreground}"
                                             RenderTransformOrigin="0.5,0.5">
                                        <Ellipse.RenderTransform>
                                            <TranslateTransform />
                                        </Ellipse.RenderTransform>
                                    </Ellipse>
                                </Border>
                                <Rectangle Grid.Column="5" Width="{Binding EllipseOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
                                <Border x:Name="B3"
                                        Grid.Column="4"
                                        RenderTransformOrigin="0.5,0.5">
                                    <Border.RenderTransform>
                                        <TranslateTransform />
                                    </Border.RenderTransform>
                                    <Ellipse x:Name="E3"
                                             Width="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                             Height="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                             Fill="{TemplateBinding Foreground}"
                                             RenderTransformOrigin="0.5,0.5">
                                        <Ellipse.RenderTransform>
                                            <TranslateTransform />
                                        </Ellipse.RenderTransform>
                                    </Ellipse>
                                </Border>
                                <Rectangle Grid.Column="3" Width="{Binding EllipseOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
                                <Border x:Name="B4"
                                        Grid.Column="2"
                                        RenderTransformOrigin="0.5,0.5">
                                    <Border.RenderTransform>
                                        <TranslateTransform />
                                    </Border.RenderTransform>
                                    <Ellipse x:Name="E4"
                                             Width="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                             Height="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                             Fill="{TemplateBinding Foreground}"
                                             RenderTransformOrigin="0.5,0.5">
                                        <Ellipse.RenderTransform>
                                            <TranslateTransform />
                                        </Ellipse.RenderTransform>
                                    </Ellipse>
                                </Border>
                                <Rectangle Grid.Column="1" Width="{Binding EllipseOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
                                <Border x:Name="B5"
                                        Grid.Column="0"
                                        RenderTransformOrigin="0.5,0.5">
                                    <Border.RenderTransform>
                                        <TranslateTransform />
                                    </Border.RenderTransform>
                                    <Ellipse x:Name="E5"
                                             Width="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                             Height="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                                             Fill="{TemplateBinding Foreground}"
                                             RenderTransformOrigin="0.5,0.5">
                                        <Ellipse.RenderTransform>
                                            <TranslateTransform />
                                        </Ellipse.RenderTransform>
                                    </Ellipse>
                                </Border>
                            </Grid>
                        </Grid>
                        <Grid x:Name="DeterminateRoot"
                              Margin="{TemplateBinding Padding}"
                              Opacity="0">
                            <Border x:Name="PART_Track"
                                    Background="{TemplateBinding Background}"
                                    BorderBrush="{TemplateBinding BorderBrush}"
                                    BorderThickness="{TemplateBinding BorderThickness}" />
                            <Rectangle x:Name="PART_Indicator"
                                       HorizontalAlignment="Left"
                                       Fill="{TemplateBinding Foreground}" />
                        </Grid>
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Determinate" />
                                <VisualState x:Name="Indeterminate" />
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsIndeterminate" Value="False">
                            <Setter TargetName="DeterminateRoot" Property="Opacity" Value="1" />
                        </Trigger>
                        <Trigger Property="Orientation" Value="Vertical">
                            <Setter Property="MinHeight" Value="0" />
                            <Setter Property="MinWidth" Value="{StaticResource ProgressBarMinHeight}" />
                            <Setter Property="UseLayoutRounding" Value="True" />
                            <Setter TargetName="ContainingGrid" Property="LayoutTransform">
                                <Setter.Value>
                                    <RotateTransform Angle="-90" />
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>
 public class Loading : ProgressBar
    {
        public static readonly DependencyProperty EllipseDiameterProperty
           = DependencyProperty.Register(nameof(EllipseDiameter),
                                         typeof(double),
                                         typeof(Loading),
                                         new PropertyMetadata(default(double)));

        public static readonly DependencyProperty EllipseOffsetProperty =
            DependencyProperty.Register(nameof(EllipseOffset),
                                        typeof(double),
                                        typeof(Loading),
                                        new PropertyMetadata(default(double)));

        private readonly object lockme = new object();
        private Storyboard indeterminateStoryboard;

        static Loading()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(Loading), new FrameworkPropertyMetadata(typeof(Loading)));
            IsIndeterminateProperty.OverrideMetadata(typeof(Loading), new FrameworkPropertyMetadata(OnIsIndeterminateChanged));
        }

        public Loading()
        {
            this.IsVisibleChanged += this.VisibleChangedHandler;
        }

        private void VisibleChangedHandler(object sender, DependencyPropertyChangedEventArgs e)
        {
            // reset Storyboard if Visibility is set to Visible #1300
            if (this.IsIndeterminate)
            {
                ToggleIndeterminate(this, (bool)e.OldValue, (bool)e.NewValue);
            }
        }

        private static void OnIsIndeterminateChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var bar = (Loading)dependencyObject;
            if (bar.IsLoaded && bar.IsVisible)
            {
                ToggleIndeterminate(bar, (bool)e.OldValue, (bool)e.NewValue);
            }
        }

        private static void ToggleIndeterminate(Loading bar, bool oldValue, bool newValue)
        {
            if (newValue == oldValue)
            {
                return;
            }
            var indeterminateState = bar.GetIndeterminate();
            var containingObject = bar.GetTemplateChild("ContainingGrid") as FrameworkElement;
            if (indeterminateState != null && containingObject != null)
            {
                var resetAction = new Action(() =>
                {
                    if (oldValue && indeterminateState.Storyboard != null)
                    {
                        // remove the previous storyboard from the Grid #1855
                        indeterminateState.Storyboard.Stop(containingObject);
                        indeterminateState.Storyboard.Remove(containingObject);
                    }
                    if (newValue)
                    {
                        bar.ResetStoryboard(bar.ActualSize(true), false);
                    }
                });
                resetAction?.Invoke();
            }
        }

        /// <summary>
        /// Gets/sets the diameter of the ellipses used in the indeterminate animation.
        /// </summary>
        public double EllipseDiameter
        {
            get { return (double)this.GetValue(EllipseDiameterProperty); }
            set { this.SetValue(EllipseDiameterProperty, value); }
        }

        /// <summary>
        /// Gets/sets the offset of the ellipses used in the indeterminate animation.
        /// </summary>
        public double EllipseOffset
        {
            get { return (double)this.GetValue(EllipseOffsetProperty); }
            set { this.SetValue(EllipseOffsetProperty, value); }
        }

        private void SizeChangedHandler(object sender, SizeChangedEventArgs e)
        {
            var size = this.ActualSize(false);
            var bar = this;
            if (this.Visibility == Visibility.Visible && this.IsIndeterminate)
            {
                bar.ResetStoryboard(size, true);
            }
        }

        private double ActualSize(bool invalidateMeasureArrange)
        {
            if (invalidateMeasureArrange)
            {
                this.UpdateLayout();
                this.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                this.InvalidateArrange();
            }
            return this.Orientation == Orientation.Horizontal ? this.ActualWidth : this.ActualHeight;
        }

        private void ResetStoryboard(double width, bool removeOldStoryboard)
        {
            if (!this.IsIndeterminate)
            {
                return;
            }
            lock (this.lockme)
            {
                //perform calculations
                var containerAnimStart = this.CalcContainerAnimStart(width);
                var containerAnimEnd = this.CalcContainerAnimEnd(width);
                var ellipseAnimWell = this.CalcEllipseAnimWell(width);
                var ellipseAnimEnd = this.CalcEllipseAnimEnd(width);
                //reset the main double animation
                try
                {
                    var indeterminate = this.GetIndeterminate();

                    if (indeterminate != null && this.indeterminateStoryboard != null)
                    {
                        var newStoryboard = this.indeterminateStoryboard.Clone();
                        var doubleAnim = newStoryboard.Children.First(t => t.Name == "MainDoubleAnim");
                        doubleAnim.SetValue(DoubleAnimation.FromProperty, containerAnimStart);
                        doubleAnim.SetValue(DoubleAnimation.ToProperty, containerAnimEnd);

                        var namesOfElements = new[] { "E1", "E2", "E3", "E4", "E5" };
                        foreach (var elemName in namesOfElements)
                        {
                            var doubleAnimParent = (DoubleAnimationUsingKeyFrames)newStoryboard.Children.First(t => t.Name == elemName + "Anim");
                            DoubleKeyFrame first,
                                           second,
                                           third;
                            if (elemName == "E1")
                            {
                                first = doubleAnimParent.KeyFrames[1];
                                second = doubleAnimParent.KeyFrames[2];
                                third = doubleAnimParent.KeyFrames[3];
                            }
                            else
                            {
                                first = doubleAnimParent.KeyFrames[2];
                                second = doubleAnimParent.KeyFrames[3];
                                third = doubleAnimParent.KeyFrames[4];
                            }

                            first.Value = ellipseAnimWell;
                            second.Value = ellipseAnimWell;
                            third.Value = ellipseAnimEnd;
                            first.InvalidateProperty(DoubleKeyFrame.ValueProperty);
                            second.InvalidateProperty(DoubleKeyFrame.ValueProperty);
                            third.InvalidateProperty(DoubleKeyFrame.ValueProperty);

                            doubleAnimParent.InvalidateProperty(Storyboard.TargetPropertyProperty);
                            doubleAnimParent.InvalidateProperty(Storyboard.TargetNameProperty);
                        }

                        var containingGrid = (FrameworkElement)this.GetTemplateChild("ContainingGrid");

                        if (removeOldStoryboard && indeterminate.Storyboard != null)
                        {
                            // remove the previous storyboard from the Grid #1855
                            indeterminate.Storyboard.Stop(containingGrid);
                            indeterminate.Storyboard.Remove(containingGrid);
                        }

                        indeterminate.Storyboard = newStoryboard;

                        indeterminate.Storyboard?.Begin(containingGrid, true);
                    }
                }
                catch (Exception)
                {
                    //we just ignore 
                }
            }
        }

        private VisualState GetIndeterminate()
        {
            var templateGrid = this.GetTemplateChild("ContainingGrid") as FrameworkElement;
            if (templateGrid == null)
            {
                this.ApplyTemplate();
                templateGrid = this.GetTemplateChild("ContainingGrid") as FrameworkElement;
                if (templateGrid == null) return null;
            }
            var groups = VisualStateManager.GetVisualStateGroups(templateGrid);
            return groups?.OfType<VisualStateGroup>()
                         .SelectMany(group => group.States.OfType<VisualState>())
                         .FirstOrDefault(state => state.Name == "Indeterminate");
        }

        private void SetEllipseDiameter(double width)
        {
            this.SetCurrentValue(EllipseDiameterProperty, width <= 180 ? 4d : (width <= 280 ? 5d : 6d));
        }

        private void SetEllipseOffset(double width)
        {
            this.SetCurrentValue(EllipseOffsetProperty, width <= 180 ? 4d : (width <= 280 ? 7d : 9d));
        }

        private double CalcContainerAnimStart(double width)
        {
            return width <= 180 ? -34 : (width <= 280 ? -50.5 : -63);
        }

        private double CalcContainerAnimEnd(double width)
        {
            var firstPart = 0.4352 * width;
            return width <= 180 ? firstPart - 25.731 : (width <= 280 ? firstPart + 27.84 : firstPart + 58.862);
        }

        private double CalcEllipseAnimWell(double width)
        {
            return width * 1.0 / 3.0;
        }

        private double CalcEllipseAnimEnd(double width)
        {
            return width * 2.0 / 3.0;
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            lock (this.lockme)
            {
                this.indeterminateStoryboard = this.TryFindResource("IndeterminateStoryboard") as Storyboard;
            }

            this.Loaded -= this.LoadedHandler;
            this.Loaded += this.LoadedHandler;
        }

        private void LoadedHandler(object sender, RoutedEventArgs routedEventArgs)
        {
            this.Loaded -= this.LoadedHandler;
            this.SizeChangedHandler(null, null);
            this.SizeChanged += this.SizeChangedHandler;
        }

        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            base.OnRenderSizeChanged(sizeInfo);
            this.UpdateEllipseProperties();
        }

        private void UpdateEllipseProperties()
        {
            // Update the Ellipse properties to their default values
            // only if they haven't been user-set.
            var actualSize = this.ActualSize(true);
            if (actualSize > 0)
            {
                if (this.EllipseDiameter.Equals(0))
                {
                    this.SetEllipseDiameter(actualSize);
                }
                if (this.EllipseOffset.Equals(0))
                {
                    this.SetEllipseOffset(actualSize);
                }
            }
        }
    }

效果(使用时IsIndeterminate="True")

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值