I have an MVVM-based WPF 4 application which uses a ProgressBar to show the percentage completion of a long-running operation.
<ProgressBar Name="ProgressBar"
IsIndeterminate="False"
Minimum="0"
Maximum="100"
Value="{Binding Path=ProgressPercentageComplete, Mode=OneWay}"
Visibility="Visible"/>
I am happy for the "pulsing" animation to occur while the progress bar is moving, but once it reaches 100% I'd like it to stop animating and just remain static at 100%.
I've tried setting IsIndeterminate="False"
but this doesn't help and I can see why after reading the MSDN Documentation:
When this property is true, the ProgressBar animates a few bars moving across the ProgressBar in a continuous manner and ignores the Value property.
Is it possible to stop this animation? Either completely, or just at 100%.
I think the only way is to create a custom style for the
ProgressBar
.The description of MSDN for IsIndeterminate refers to another behaviour and, for default, it is false so setting it doesn't change anything.
I wrote a generalized solution for this using an attached property, allowing me to toggle the behavior on any
ProgressBar
simply through a direct property or style setter, like so:The code:
Basically, it adds a
ValueChanged
handler which adjusts the visibility of the animated element.A few notes:
I'm using
"PART_GlowRect"
to find the animated element, although someone called this a hack. I disagree: this element name is officially documented through TemplatePartAttribute, which you can see in ProgressBar's declaration. While it's true that this doesn't necessarily guarantee that the named element exists, the only reason it should be missing is if the animation feature isn't supported at all. If it is supported but uses a different element name than the one documented, I would consider that a bug, not an implementation detail.Since I'm pulling an element out of the template, it's also necessary to handle the
Loaded
event (which is raised when a template is applied) to wait for the template to become available before attempting to set initial visibility, and if necessary set it again when the template is replaced on the fly by a theme change.Rather than explicitly toggling
Visibility
betweenCollapsed
andVisible
, I'm using SetCurrentValue to set toCollapsed
, and InvalidateProperty to reset it.SetCurrentValue
applies a value that does not take priority over other value sources, andInvalidateProperty
re-evaluates the property without taking theSetCurrentValue
setting into consideration. This ensures that if there are existing styles or triggers which would affect the visibility under normal conditions (i.e. when it is not at 100%), it would reset to that behavior if the progress bar is reused (going from 100% back to 0%) rather than being hard-coded toVisible
.You can swap the converter being used by
PART_Indicator
which by default is theProgressBarBrushConverter
which is where the animation is coming from...The default logic for the
ProgressBarBrushConverter
can then be modified to suit your needs.You may have to end up passing parameters to the converter so that it can check the value and provide the appropriate animation or lack thereof contingent on the state of the
ProgressBar
.You can accomplish this by copying the entire
ControlTemplate
for theProgressBar
, then add aTrigger
for the condition whereProgressBar.Value=100
. The XAML as is will make theProgressBar
behave as it does now. Remove the comment Tags at the bottom and the animation will stop when theProgressBar
's Value property reaches 100. The only weakness is, that when you change the Maximum Property of theProgressBar
you need to change the Trigger as well. Anyone know how to bind the Trigger to the actual value of the Maximum Property?Dabblernl's answer is robust. Here is a hack (because it relies on the internal name of the element that does the glow, which is an implementation detail and may change in a subsequent version):
If how a
ProgressBar
is implemented changes, this hack may stop working.On the other hand, a solution that completely replaces the XAML and styles may lock-in and fix colours, borders etc. and disable behaviour that might be added to a newer version of the ProgressBar in the future...
Edit: The implementation detail did change. Changed
"PART_GlowRect"
to"Animation"
-- the former is used only inaero.normalcolor.xaml
, while the latter is used in more recentaero2.normalcolor.xaml
andaerolite.normalcolor.xaml
too.