[英]WPF Smoothly animated ProgressBar in template
I am working on an MVVM application and I would like to have a ProgressBar that smoothly animates to it's new value when that property changes. 我正在开发一个MVVM应用程序,我希望有一个ProgressBar,当该属性发生变化时,它可以平滑地动画到它的新值。 I have seen several answers to this question using c# but I'd prefer to do it all inside the template. 我已经使用c#看到了这个问题的几个答案,但我更喜欢在模板中完成所有操作。 The problem I'm having is setting up and targeting the event and storyboard properly. 我遇到的问题是正确设置和定位事件和故事板。 Here is what I have currently: 这是我目前的情况:
The progress bar- 进度条 -
The style- (just the triggers) 风格 - (只是触发器)
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="RangeBase.ValueChanged">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="???????"
Storyboard.TargetProperty="Value"
To="???????" Duration="0:0:5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
I took the trigger code from here: http://msdn.microsoft.com/en-us/library/system.windows.controls.progressbar(v=vs.110).aspx . 我从这里获取了触发器代码: http : //msdn.microsoft.com/en-us/library/system.windows.controls.progressbar(v = vs.110).aspx 。
How do I set the TargetName to the template itself so that it applies to all the controls which use this template? 如何将TargetName设置为模板本身,以便它适用于使用此模板的所有控件? How do I set "To" to the incoming Value? 如何为传入值设置“收件人”? There appears to be a way to grab the "Binding" value but I have Value and Max both bound on the progressbar element. 似乎有一种方法可以获取“Binding”值,但我有Value和Max都绑定在progressbar元素上。 How would it know what to use? 怎么会知道怎么用?
Here is the whole template for reference: 以下是整个模板供参考:
<Style x:Key="ProgressStyle" TargetType="{x:Type ProgressBar}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Grid MinHeight="14" MinWidth="20">
<Border x:Name="BaseRectangle" Background="{StaticResource BaseColor}" CornerRadius="10,0,10,0"></Border>
<Border x:Name="GlassRectangle" CornerRadius="10,0,10,0" Background="{StaticResource GlassFX}" Panel.ZIndex="10"></Border>
<Border x:Name="animation" CornerRadius="10,0,10,0" Opacity=".7" Background="{Binding Path=Foreground, RelativeSource={RelativeSource TemplatedParent}}" HorizontalAlignment="Left"></Border>
<Border x:Name="PART_Indicator" CornerRadius="10,0,10,0" Background="{Binding Path=Foreground, RelativeSource={RelativeSource TemplatedParent}}" HorizontalAlignment="Left"></Border>
<Border x:Name="PART_Track" BorderThickness="1" CornerRadius="10,0,10,0" BorderBrush="Black"></Border>
<Border x:Name="BordeCabeceraSombra" BorderThickness="2" CornerRadius="10,0,10,0" BorderBrush="DarkGray" Opacity=".2" Margin="1,1,1,0"></Border>
<Label x:Name="Progress" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontWeight="Bold" Foreground="White" Opacity=".7" Content="{Binding Path=Value, RelativeSource={RelativeSource TemplatedParent}}"></Label>
</Grid>
<ControlTemplate.Triggers>
<EventTrigger RoutedEvent="RangeBase.ValueChanged">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="???????"
Storyboard.TargetProperty="Value"
From="???????" To="???????" Duration="0:0:5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<Trigger Property="IsIndeterminate" Value="True">
<Setter Property="Visibility" TargetName="Progress" Value="Hidden"></Setter>
<Setter Property="Background" TargetName="PART_Indicator">
<Setter.Value>
<MultiBinding>
<MultiBinding.Converter>
<wintheme:ProgressBarHighlightConverter/>
</MultiBinding.Converter>
<Binding Source="{StaticResource GlowFXProgressAnimated}"/>
<Binding Path="ActualWidth" ElementName="BaseRectangle"/>
<Binding Path="ActualHeight" ElementName="BaseRectangle"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value=".5"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Any help would be appreciated! 任何帮助,将不胜感激!
I think its better way. 我认为这是更好的方式。
You can create behavior to do this. 您可以创建行为来执行此操作。 (MVVM WPF) (MVVM WPF)
Create class: 创建类:
class ProgresBarAnimateBehavior : Behavior<ProgressBar>
{
bool _IsAnimating = false;
protected override void OnAttached()
{
base.OnAttached();
ProgressBar progressBar = this.AssociatedObject;
progressBar.ValueChanged += ProgressBar_ValueChanged;
}
private void ProgressBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (_IsAnimating)
return;
_IsAnimating = true;
DoubleAnimation doubleAnimation = new DoubleAnimation
(e.OldValue, e.NewValue, new Duration(TimeSpan.FromSeconds(0.3)), FillBehavior.Stop);
doubleAnimation.Completed += Db_Completed;
((ProgressBar)sender).BeginAnimation(ProgressBar.ValueProperty, doubleAnimation);
e.Handled = true;
}
private void Db_Completed(object sender, EventArgs e)
{
_IsAnimating = false;
}
protected override void OnDetaching()
{
base.OnDetaching();
ProgressBar progressBar = this.AssociatedObject;
progressBar.ValueChanged -= ProgressBar_ValueChanged;
}
}
And simply usage: 简单的用法:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:b="clr-namespace:YOURNAMESPACE.Behaviors"
<ProgressBar Height="7"
Value="{Binding LoadingValue}">
<i:Interaction.Behaviors>
<b:ProgresBarAnimateBehavior />
</i:Interaction.Behaviors>
</ProgressBar>
I never actually found a solution to this. 我从未真正找到解决方案。 I ended up just writing my own control. 我最后只是写自己的控件。 This isn't technically an answer to the question, but I figure I may as well post it. 这在技术上不是问题的答案,但我想我也可以发布它。 If someone is looking for an animating progress control for MVVM this may help. 如果有人正在寻找MVVM的动画进度控制,这可能会有所帮助。
namespace Card_System.Controls
{
/// <summary>
/// Interaction logic for StatProgressBar.xaml
/// </summary>
public partial class StatProgressBar : UserControl
{
private double _trackWidth;
private bool _isAnimate;
private bool _isRefresh;
public StatProgressBar()
{
InitializeComponent();
var descriptor = DependencyPropertyDescriptor.FromProperty(ActualWidthProperty, typeof(Border));
if (descriptor != null)
{
descriptor.AddValueChanged(TrackBorder, ActualWidth_ValueChanged);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private double _barValueSet;
public double BarValueSet
{
get { return _barValueSet; }
set
{
_barValueSet = value;
OnPropertyChanged("BarValueSet");
_isAnimate = true;
AnimateWidth();
}
}
public double BarValueDesired
{
get { return (double)GetValue(BarValueProperty); }
set { SetValue(BarValueProperty, value); }
}
public static readonly DependencyProperty BarValueProperty =
DependencyProperty.Register("BarValueDesired", typeof(double), typeof(StatProgressBar), new UIPropertyMetadata(0.0d, new PropertyChangedCallback(BarValueDesired_PropertyChanged)));
public double BarMaximum
{
get { return (double)GetValue(BarMaximumProperty); }
set { SetValue(BarMaximumProperty, value); }
}
public static readonly DependencyProperty BarMaximumProperty =
DependencyProperty.Register("BarMaximum", typeof(double), typeof(StatProgressBar), new UIPropertyMetadata(0.0d));
public static void BarValueDesired_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//Set BarValue to the value of BarValueDesired BEFORE it was just changed.
((StatProgressBar)d).BarValueSet = (double)e.OldValue;
}
public Brush BarColor
{
get { return (Brush)GetValue(BarColorProperty); }
set { SetValue(BarColorProperty, value); }
}
public static readonly DependencyProperty BarColorProperty =
DependencyProperty.Register("BarColor", typeof(Brush), typeof(StatProgressBar), new UIPropertyMetadata(Brushes.White, new PropertyChangedCallback(BarColor_PropertyChanged)));
public static void BarColor_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((StatProgressBar)d).BarFill.Background = (Brush)e.NewValue;
}
private void ActualWidth_ValueChanged(object a_sender, EventArgs a_e)
{
_trackWidth = TrackBorder.ActualWidth;
_isRefresh = true;
AnimateWidth();
}
public void AnimateWidth()
{
if (_isAnimate && _isRefresh)
{
double StartPoint = new double();
double EndPoint = new double();
double PercentEnd = new double();
double PercentStart = new double();
PercentStart = BarValueSet / BarMaximum;
StartPoint = _trackWidth * PercentStart;
PercentEnd = BarValueDesired / BarMaximum;
EndPoint = _trackWidth * PercentEnd;
DoubleAnimation animation = new DoubleAnimation(StartPoint, EndPoint, TimeSpan.FromSeconds(3));
this.BarFill.BeginAnimation(Border.WidthProperty, animation);
}
else return;
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
And here is the XAML: 这是XAML:
<Grid>
<Grid MinHeight="14" MinWidth="20">
<Border x:Name="BaseRectangle" Background="{StaticResource BaseColor}" CornerRadius="0,0,0,0"/>
<Border x:Name="TrackBorder" BorderThickness="1" CornerRadius="0,0,0,0" BorderBrush="Black" Panel.ZIndex="20"/>
<Border x:Name="BarFill" HorizontalAlignment="Left" Opacity=".7" Background="White"/>
<Border x:Name="GlassOverlay" CornerRadius="0,0,0,0" Background="{StaticResource GlassFX}" Panel.ZIndex="10"/>
<Border x:Name="GlassOverlayBorder" BorderThickness="4" CornerRadius="0,0,0,0" BorderBrush="DarkGray" Opacity=".2" Panel.ZIndex="12"/>
</Grid>
I know this question is solved, but I found a really good implementation that doesn't require creating a UserControl. 我知道这个问题已经解决了,但我找到了一个非常好的实现,不需要创建UserControl。 It imitates the "barber pole effect" and works right out of the box: 它模仿“理发杆效应”,开箱即用:
<SolidColorBrush x:Key="ProgressBarBorderBrush" Color="Transparent" />
<SolidColorBrush x:Key="ProgressBarBackgroundBrush" Color="White" />
<SolidColorBrush x:Key="ProgressBarTrackBackgroundBrush" Color="#63D055" />
<Style x:Key="{x:Type ProgressBar}" TargetType="{x:Type ProgressBar}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<local:ClippingBorder x:Name="BorderBackground" CornerRadius="3" BorderThickness="0"
BorderBrush="{StaticResource ProgressBarBorderBrush}"
Background="{StaticResource ProgressBarBackgroundBrush}">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Determinate" />
<VisualState x:Name="Indeterminate" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border x:Name="PART_Track" Margin="0" BorderThickness="0" CornerRadius="3" />
<Border x:Name="PART_Indicator" Margin="0" BorderThickness="0" CornerRadius="3" HorizontalAlignment="Left"
Background="{StaticResource ProgressBarTrackBackgroundBrush}" ClipToBounds="True">
<Border x:Name="DiagonalDecorator" Width="5000">
<Border.Background>
<DrawingBrush TileMode="Tile" Stretch="None" Viewbox="0,0,1,1" Viewport="0,0,36,34" ViewportUnits="Absolute">
<DrawingBrush.RelativeTransform>
<TranslateTransform X="0" Y="0" />
</DrawingBrush.RelativeTransform>
<DrawingBrush.Drawing>
<GeometryDrawing Brush="#48C739" Geometry="M0,0 18,0 36,34 18,34 Z" />
</DrawingBrush.Drawing>
</DrawingBrush>
</Border.Background>
<Border.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(Border.Background).(DrawingBrush.RelativeTransform).(TranslateTransform.X)"
From="0" To=".36" RepeatBehavior="Forever" Duration="0:0:18" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Border.Triggers>
</Border>
</Border>
</Grid>
</local:ClippingBorder>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Simply edit speed and colors to your liking. 只需根据自己的喜好编辑速度和颜色。
I have posted a similar concept in another forum and link is mentioned below. 我在另一个论坛中发布了类似的概念,下面提到了链接。 I hope this will help you. 我希望这能帮到您。
http://www.codeproject.com/Tips/837994/WPF-Preloader-Control http://www.codeproject.com/Tips/837994/WPF-Preloader-Control
Cheers 干杯
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.