WPF自定义控件与样式(11)-等待/忙/正在加载状态-控件实现

一.前言

  申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接。

  本文主要有三种实现方式

  • 简单忙碌状态控件BusyBox;
  • Win8/win10效果忙碌状态控件ProgressRing;
  • 弹出异步等待框WaitingBox;

二.简单忙碌状态控件BusyBox

  效果图:

 WPF自定义控件与样式(11)-等待/忙/正在加载状态-控件实现

  通过属性"IsActive"控制控件是否启用,后台C#代码:  

WPF自定义控件与样式(11)-等待/忙/正在加载状态-控件实现
    /// <summary>
    /// BusyBox.xaml 的交互逻辑
    /// </summary>
    public partial class BusyBox : UserControl
    {
        public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register("IsActive", typeof(bool), typeof(BusyBox), new PropertyMetadata(false));
        /// <summary>
        /// 是否启用
        /// </summary>
        public bool IsActive
        {
            get { return (bool)GetValue(IsActiveProperty); }
            set { SetValue(IsActiveProperty, value); }
        }

        static BusyBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(BusyBox), new FrameworkPropertyMetadata(typeof(BusyBox)));
        }
    }
WPF自定义控件与样式(11)-等待/忙/正在加载状态-控件实现

  使用了一个字体图标,触发器中实现动画显示的控制,样式代码:  

WPF自定义控件与样式(11)-等待/忙/正在加载状态-控件实现
    <Style TargetType="{x:Type local:BusyBox}">
        <Setter Property="Foreground" Value="{StaticResource TextForeground}"></Setter>
        <Setter Property="Width" Value="32"></Setter>
        <Setter Property="Height" Value="32"></Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:BusyBox}">
                    <Grid VerticalAlignment="Center" HorizontalAlignment="Center" >
                        <Viewbox Stretch="Uniform"  VerticalAlignment="Center" HorizontalAlignment="Center">
                            <TextBlock Text="&#xe65f;" x:Name="FIcon" FontSize="36" Style="{StaticResource FIcon}"  RenderTransformOrigin="0.5,0.5"
                       Foreground="{TemplateBinding Foreground}">
                                <TextBlock.RenderTransform>
                                    <RotateTransform x:Name="TransFIcon" Angle="0"/>
                                </TextBlock.RenderTransform>
                            </TextBlock>
                        </Viewbox>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <!--激活状态-->
                        <Trigger Property="IsActive" Value="true">
                            <Setter Property="Visibility" Value="Visible" TargetName="FIcon"/>
                            <Trigger.EnterActions>
                                <BeginStoryboard >
                                    <Storyboard >
                                        <DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetName="TransFIcon" 
                                     Storyboard.TargetProperty="Angle" To="360" Duration="0:0:2.5"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.EnterActions>
                            <Trigger.ExitActions>
                                <BeginStoryboard >
                                    <Storyboard >
                                        <DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetName="TransFIcon" 
                                     Storyboard.TargetProperty="Angle" To="0" Duration="0"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.ExitActions>
                        </Trigger>
                        <!--非激活状态-->
                        <Trigger Property="IsActive" Value="false">
                            <Setter Property="Visibility" Value="Collapsed" TargetName="FIcon"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
WPF自定义控件与样式(11)-等待/忙/正在加载状态-控件实现

  使用示例:  

            <CheckBox VerticalAlignment="Center" x:Name="cbActive2" IsChecked="True" Margin="5">IsActive</CheckBox>
            <core:BusyBox Width="80" Height="80" Foreground="White" Background="Red"  Margin="5"  IsActive="{Binding IsChecked ,ElementName=cbActive2}" />
            <core:BusyBox Width="30" Height="30" Foreground="White" Background="Red"  Margin="5"  IsActive="{Binding IsChecked ,ElementName=cbActive2}" />

 

三.Win8/win10效果忙碌状态控件ProgressRing

  这是网上一个开源项目里的控件,项目地址:http://mahapps.com/。不做多介绍了,效果图:

 WPF自定义控件与样式(11)-等待/忙/正在加载状态-控件实现

  后台C#代码:  

WPF自定义控件与样式(11)-等待/忙/正在加载状态-控件实现
WPF自定义控件与样式(11)-等待/忙/正在加载状态-控件实现
    [TemplateVisualState(Name = "Large", GroupName = "SizeStates")]
    [TemplateVisualState(Name = "Small", GroupName = "SizeStates")]
    [TemplateVisualState(Name = "Inactive", GroupName = "ActiveStates")]
    [TemplateVisualState(Name = "Active", GroupName = "ActiveStates")]
    public class ProgressRing : Control
    {
        public static readonly DependencyProperty BindableWidthProperty = DependencyProperty.Register("BindableWidth", typeof(double), typeof(ProgressRing), new PropertyMetadata(default(double), BindableWidthCallback));

        public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register("IsActive", typeof(bool), typeof(ProgressRing), new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, IsActiveChanged));

        public static readonly DependencyProperty IsLargeProperty = DependencyProperty.Register("IsLarge", typeof(bool), typeof(ProgressRing), new PropertyMetadata(true, IsLargeChangedCallback));

        public static readonly DependencyProperty MaxSideLengthProperty = DependencyProperty.Register("MaxSideLength", typeof(double), typeof(ProgressRing), new PropertyMetadata(default(double)));

        public static readonly DependencyProperty EllipseDiameterProperty = DependencyProperty.Register("EllipseDiameter", typeof(double), typeof(ProgressRing), new PropertyMetadata(default(double)));

        public static readonly DependencyProperty EllipseOffsetProperty = DependencyProperty.Register("EllipseOffset", typeof(Thickness), typeof(ProgressRing), new PropertyMetadata(default(Thickness)));

        private List<Action> _deferredActions = new List<Action>();

        static ProgressRing()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ProgressRing), new FrameworkPropertyMetadata(typeof(ProgressRing)));
            VisibilityProperty.OverrideMetadata(typeof(ProgressRing),
                                                new FrameworkPropertyMetadata(
                                                    new PropertyChangedCallback(
                                                        (ringObject, e) =>
                                                        {
                                                            if (e.NewValue != e.OldValue)
                                                            {
                                                                var ring = (ProgressRing)ringObject;
                                                                //auto set IsActive to false if we‘re hiding it.
                                                                if ((Visibility)e.NewValue != Visibility.Visible)
                                                                {
                                                                    //sets the value without overriding it‘s binding (if any).
                                                                    ring.SetCurrentValue(ProgressRing.IsActiveProperty, false);
                                                                }
                                                                else
                                                                {
                                                                    // #1105 don‘t forget to re-activate
                                                                    ring.IsActive = true;
                                                                }
                                                            }
                                                        })));
        }

        public ProgressRing()
        {
            SizeChanged += OnSizeChanged;
        }

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

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

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

        public double BindableWidth
        {
            get { return (double)GetValue(BindableWidthProperty); }
            private set { SetValue(BindableWidthProperty, value); }
        }

        public bool IsActive
        {
            get { return (bool)GetValue(IsActiveProperty); }
            set { SetValue(IsActiveProperty, value); }
        }

        public bool IsLarge
        {
            get { return (bool)GetValue(IsLargeProperty); }
            set { SetValue(IsLargeProperty, value); }
        }

        private static void BindableWidthCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            var ring = dependencyObject as ProgressRing;
            if (ring == null)
                return;

            var action = new Action(() =>
            {
                ring.SetEllipseDiameter(
                    (double)dependencyPropertyChangedEventArgs.NewValue);
                ring.SetEllipseOffset(
                    (double)dependencyPropertyChangedEventArgs.NewValue);
                ring.SetMaxSideLength(
                    (double)dependencyPropertyChangedEventArgs.NewValue);
            });

            if (ring._deferredActions != null)
                ring._deferredActions.Add(action);
            else
                action();
        }

        private void SetMaxSideLength(double width)
        {
            MaxSideLength = width <= 20 ? 20 : width;
        }

        private void SetEllipseDiameter(double width)
        {
            EllipseDiameter = width / 8;
        }

        private void SetEllipseOffset(double width)
        {
            EllipseOffset = new Thickness(0, width / 2, 0, 0
上一篇:WPF自定义控件与样式(10)-进度控件ProcessBar自定义样


下一篇:WPF自定义控件与样式(9)-树控件TreeView与菜单Menu-ContextMenu