转载

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

一.前言

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

本文主要有三种实现方式:

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

二.简单忙碌状态控件BusyBox

效果图:

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

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

/// <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)));  } } 

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

<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="" 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> 

使用示例: 

<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#代码: 

[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);   }   private static void IsLargeChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)   {    var ring = dependencyObject as ProgressRing;    if (ring == null)     return;    ring.UpdateLargeState();   }   private void UpdateLargeState()   {    Action action;    if (IsLarge)     action = () => VisualStateManager.GoToState(this, "Large", true);    else     action = () => VisualStateManager.GoToState(this, "Small", true);    if (_deferredActions != null)     _deferredActions.Add(action);    else     action();   }   private void OnSizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs)   {    BindableWidth = ActualWidth;   }   private static void IsActiveChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)   {    var ring = dependencyObject as ProgressRing;    if (ring == null)     return;    ring.UpdateActiveState();   }   private void UpdateActiveState()   {    Action action;    if (IsActive)     action = () => VisualStateManager.GoToState(this, "Active", true);    else     action = () => VisualStateManager.GoToState(this, "Inactive", true);    if (_deferredActions != null)     _deferredActions.Add(action);    else     action();   }   public override void OnApplyTemplate()   {    //make sure the states get updated    UpdateLargeState();    UpdateActiveState();    base.OnApplyTemplate();    if (_deferredActions != null)     foreach (var action in _deferredActions)      action();    _deferredActions = null;   }  }  internal class WidthToMaxSideLengthConverter : IValueConverter  {   public object Convert(object value, Type targetType, object parameter, CultureInfo culture)   {    if (value is double)    {     var width = (double)value;     return width <= 20 ? 20 : width;    }    return null;   }   public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)   {    throw new NotImplementedException();   }  } View Code 

样式代码: 

<Style TargetType="local:ProgressRing">         <Setter Property="Foreground" Value="White" />         <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="Height" Value="60" />         <Setter Property="Width" Value="60" />         <Setter Property="IsTabStop" Value="False" />         <Setter Property="Template">             <Setter.Value>                 <ControlTemplate TargetType="local:ProgressRing">                     <Border 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"                               Margin="{TemplateBinding Padding}"                               MaxWidth="{Binding MaxSideLength, RelativeSource={RelativeSource Mode=TemplatedParent}}"                               MaxHeight="{Binding MaxSideLength, RelativeSource={RelativeSource Mode=TemplatedParent}}"                               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 EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"            Height="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"            Margin="{Binding EllipseOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}"            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 EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"            Height="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"            Margin="{Binding EllipseOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}"            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 EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"            Height="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"            Margin="{Binding EllipseOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}"            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 EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"            Height="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"            Margin="{Binding EllipseOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}"            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 EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"            Height="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"            Margin="{Binding EllipseOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}"            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 EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"            Height="{Binding EllipseDiameter, RelativeSource={RelativeSource Mode=TemplatedParent}}"            Margin="{Binding EllipseOffset, RelativeSource={RelativeSource Mode=TemplatedParent}}"            Fill="{TemplateBinding Foreground}" />                             </Canvas>                         </Grid>                     </Border>                 </ControlTemplate>             </Setter.Value>         </Setter>     </Style> View Code 

使用示例: 

<CheckBox VerticalAlignment="Center" x:Name="cbActive" Margin="5" IsChecked="True">IsActive</CheckBox>             <core:ProgressRing Width="80" Height="80" Foreground="Red" Margin="5"  IsActive="{Binding IsChecked ,ElementName=cbActive}" />

四.弹出异步等待框WaitingBox

效果图:

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

使用的是一个模式窗体,异步执行传入的操作,实现的比较简单,没有做异常处理。另外一个缺陷就是没有支持取消操作。后台C#代码: 

/// <summary> /// 简单等待框 /// </summary> public partial class WaitingBox : Window {  public string Text { get { return this.txtMessage.Text; } set { this.txtMessage.Text = value; } }  private Action _Callback;  public WaitingBox(Action callback)  {   InitializeComponent();   this._Callback = callback;   this.Loaded += WaitingBox_Loaded;  }  void WaitingBox_Loaded(object sender, RoutedEventArgs e)  {   this._Callback.BeginInvoke(this.OnComplate, null);  }  private void OnComplate(IAsyncResult ar)  {   this.Dispatcher.Invoke(new Action(() =>   {    this.Close();   }));  }  /// <summary>  /// 显示等待框,owner指定宿主视图元素,callback为需要执行的方法体(需要自己做异常处理)。  /// 目前等等框为模式窗体  /// </summary>  public static void Show(FrameworkElement owner, Action callback, string mes = "有一种幸福,叫做等待...")  {   WaitingBox win = new WaitingBox(callback);   Window pwin = Window.GetWindow(owner);   win.Owner = pwin;   win.Text = mes;   var loc = owner.PointToScreen(new Point());   win.Left = loc.X + (owner.ActualWidth - win.Width) / 2;   win.Top = loc.Y + (owner.ActualHeight - win.Height) / 2;   win.ShowDialog();  } } 

样式代码: 

<Window x:Class="System.Windows.WaitingBox" x:Name="wb"   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"    AllowsTransparency="True" WindowStyle="None" WindowStartupLocation="Manual"   ShowInTaskbar="False" Background="Transparent"   Title="WaitingBox" Height="110" Width="260">  <Grid>   <!--Background="{Binding Path=Background,ElementName=wb}"-->   <Border Background="{StaticResource WaitingBoxBackground}"  Opacity="0.89" CornerRadius="1" Effect="{StaticResource WindowDropShadow}"></Border>   <StackPanel VerticalAlignment="Center"  Orientation="Horizontal" HorizontalAlignment="Center" Margin="5">    <TextBlock Text="" x:Name="FIcon" FontSize="50" Style="{StaticResource FIcon}"  RenderTransformOrigin="0.5,0.5" Margin="3">     <TextBlock.RenderTransform>      <RotateTransform x:Name="TransFIcon" Angle="0"/>     </TextBlock.RenderTransform>    </TextBlock>    <TextBlock x:Name="txtMessage" Margin="2,10,15,10" Width="160" VerticalAlignment="Center" TextWrapping="Wrap">Loading...</TextBlock>   </StackPanel>  </Grid>  <Window.Triggers>   <EventTrigger RoutedEvent="Window.Loaded">    <BeginStoryboard >     <Storyboard >      <DoubleAnimation RepeatBehavior="Forever" Storyboard.TargetName="TransFIcon"            Storyboard.TargetProperty="Angle" To="360" Duration="0:0:2.5"/>     </Storyboard>    </BeginStoryboard>   </EventTrigger>  </Window.Triggers> </Window> 

使用比较简单,示例: 

WaitingBox.Show(this, () =>             {                 System.Threading.Thread.Sleep(3000);             },"正在玩命的加载,请稍后...");             var res = MessageBoxX.Question("已经完了?");

附录:参考引用  

WPF自定义控件与样式(1)-矢量字体图标(iconfont)

WPF自定义控件与样式(2)-自定义按钮FButton

WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

WPF自定义控件与样式(4)-CheckBox/RadioButton自定义样式

WPF自定义控件与样式(5)-Calendar/DatePicker日期控件自定义样式及扩展

WPF自定义控件与样式(6)-ScrollViewer与ListBox自定义样式

WPF自定义控件与样式(7)-列表控件DataGrid与ListView自定义样式

WPF自定义控件与样式(8)-ComboBox与自定义多选控件MultComboBox

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

WPF自定义控件与样式(10)-进度控件ProcessBar自定义样

版权所有,文章来源: http://www.cnblogs.com/anding

个人能力有限,本文内容仅供学习、探讨,欢迎指正、交流。

正文到此结束
Loading...