一.前言

  申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等。

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

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

二.简单忙碌状态控件BusyBox

  效果图:

  通过属性"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/。不做多介绍了,效果图:

  后台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 <= ? : width;
} private void SetEllipseDiameter(double width)
{
EllipseDiameter = width / ;
} private void SetEllipseOffset(double width)
{
EllipseOffset = new Thickness(, width / , , );
} 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 <= ? : width;
} return null;
} public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

样式代码:

<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>

使用示例:

 <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

  效果图:

  使用的是一个模式窗体,异步执行传入的操作,实现的比较简单,没有做异常处理。另外一个缺陷就是没有支持取消操作。后台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) / ;
win.Top = loc.Y + (owner.ActualHeight - win.Height) / ;
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();
},"正在玩命的加载,请稍后...");
var res = MessageBoxX.Question("已经完了?");

原文地址:https://www.cnblogs.com/anding/p/5006279.html

【转】WPF自定义控件与样式(11)-等待/忙/正在加载状态-控件实现的更多相关文章

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

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

  2. QT自定义控件系列(二) --- Loading加载动画控件

    本系列主要使用Qt painter来实现一些基础控件.主要是对平时自行编写的一些自定义控件的总结. 为了简洁.低耦合,我们尽量不使用图片,qrc,ui等文件,而只使用c++的.h和.cpp文件. 由于 ...

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

    一.图标字体 图标字体在网页开发上运用非常广泛,具体可以网络搜索了解,网页上的运用有很多例子,如Bootstrap.但在C/S程序中使用还不多,字体图标其实就是把矢量图形打包到字体文件里,就像使用一般 ...

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

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

  5. WPF自定义控件与样式(15)-终结篇 & 系列文章索引 & 源码共享

    系列文章目录  WPF自定义控件与样式(1)-矢量字体图标(iconfont) WPF自定义控件与样式(2)-自定义按钮FButton WPF自定义控件与样式(3)-TextBox & Ric ...

  6. WPF自定义控件与样式(12)-缩略图ThumbnailImage /gif动画图/图片列表

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

  7. WPF自定义控件与样式(13)-自定义窗体Window & 自适应内容大小消息框MessageBox

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

  8. WPF自定义控件与样式(14)-轻量MVVM模式实践

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

  9. WPF自定义控件与样式(15)-终结篇

    原文:WPF自定义控件与样式(15)-终结篇 系列文章目录  WPF自定义控件与样式(1)-矢量字体图标(iconfont) WPF自定义控件与样式(2)-自定义按钮FButton WPF自定义控件与 ...

随机推荐

  1. [CF575B]Bribes

    [CF575B]Bribes 题目大意: 一棵\(n(n\le10^5)\)个结点的树,有些边有方向,对于每条边,如果第\(i\)次逆向走过这条边,就会产生\(2^{i-1}\)的代价.开始在\(1\ ...

  2. 全国青少年信息学奥林匹克分区联赛(N)竞赛大纲

    全国青少年信息学(计算机)奥林匹克分区联赛竞赛大纲 一.初赛内容与要求:(#表示普及组不涉及,以下同) 计算机的基本发展 诞生与发展 特点 在现代社会中的应用 计算机系统的基本组成 计算机的工作原理# ...

  3. [Codeforces113C]Double Happiness(数论)

    题意 给定闭区间[l,r] [l,r] [l,r],找出区间内满足t=a2+b2 t=a^{2}+b^{2} t=a2+b2的所有素数t t t的个数( a,b a,b a,b为任意正整数). 思路 ...

  4. video设置视频的宽高

    一般情况下<video loop="loop" style="width: 100%;height:300px;" controls="cont ...

  5. javascript中的onmousewheel事件处理

    滚轮事件在不同浏览器会有一点点区别,一个像Firefox使用DOMMouseScroll ,FF也可以使用addEventListener方法绑定DomMouseScroll事件,其他的浏览器滚轮事件 ...

  6. 近期 Unity 提交苹果审核被拒的问题

    游戏提交苹果审核,被打回.在 bugly 上没有查到崩溃信息,苹果给了 crash 日志也说明. 拒绝原因如下: Your app crashed on iPad or iPhone running ...

  7. golang 使用pprof和go-torch做性能分析

    软件开发过程中,项目上线并不是终点.上线后,还要对程序的取样分析运行情况,并重构现有的功能,让程序执行更高效更稳写. golang的工具包内自带pprof功能,使找出程序中占内存和CPU较多的部分功能 ...

  8. linux 通过nvm安装node

    官方介绍:https://github.com/creationix/nvm#installation PS:通常不要用root权限安装软件,因为线上任何服务部署都不允许用root,其他软件用root ...

  9. 朗科32G TF卡的读写测试

    卡是这样的, 下面是实际测试的结果. 容量测试 SKS的USB2外置读卡器, X240内置读卡器加上SD卡套    UNITEK的USB3.0读卡器, 经过UNITEK的USB3.0 HUB 看来读4 ...

  10. (原)visual studio 2015中添加dll路径

    转载请注明出处: https://www.cnblogs.com/darkknightzh/p/9922033.html 使用vs2015调用opencv 3.4时,除了需要在“VC++目录”中”包含 ...