Tom*_*son 27 wpf mvvm progress-bar
我有一个基于MVVM的WPF 4应用程序,它使用ProgressBar来显示长时间运行的操作的完成百分比.
<ProgressBar Name="ProgressBar"
IsIndeterminate="False"
Minimum="0"
Maximum="100"
Value="{Binding Path=ProgressPercentageComplete, Mode=OneWay}"
Visibility="Visible"/>
Run Code Online (Sandbox Code Playgroud)
我很高兴在进度条移动时发生"脉冲"动画,但一旦达到100%,我希望它停止动画并保持静态100%.
我尝试过设置,IsIndeterminate="False"
但这没有用,我在阅读MSDN文档后可以看到原因:
当此属性为true时,ProgressBar会以连续的方式设置几个在ProgressBar中移动的条形图并忽略Value属性.
是否可以停止此动画?完全,或只是100%.
nmc*_*ean 10
我使用附加属性为此编写了一个通用解决方案,允许我ProgressBar
通过直接属性或样式设置器切换任何行为,如下所示:
<ProgressBar helpers:ProgressBarHelper.StopAnimationOnCompletion="True" />
Run Code Online (Sandbox Code Playgroud)
代码:
public static class ProgressBarHelper {
public static readonly DependencyProperty StopAnimationOnCompletionProperty =
DependencyProperty.RegisterAttached("StopAnimationOnCompletion", typeof(bool), typeof(ProgressBarHelper),
new PropertyMetadata(OnStopAnimationOnCompletionChanged));
public static bool GetStopAnimationOnCompletion(ProgressBar progressBar) {
return (bool)progressBar.GetValue(StopAnimationOnCompletionProperty);
}
public static void SetStopAnimationOnCompletion(ProgressBar progressBar, bool value) {
progressBar.SetValue(StopAnimationOnCompletionProperty, value);
}
private static void OnStopAnimationOnCompletionChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) {
var progressBar = obj as ProgressBar;
if (progressBar == null) return;
var stopAnimationOnCompletion = (bool)e.NewValue;
if (stopAnimationOnCompletion) {
progressBar.Loaded += StopAnimationOnCompletion_Loaded;
progressBar.ValueChanged += StopAnimationOnCompletion_ValueChanged;
} else {
progressBar.Loaded -= StopAnimationOnCompletion_Loaded;
progressBar.ValueChanged -= StopAnimationOnCompletion_ValueChanged;
}
if (progressBar.IsLoaded) {
ReevaluateAnimationVisibility(progressBar);
}
}
private static void StopAnimationOnCompletion_Loaded(object sender, RoutedEventArgs e) {
ReevaluateAnimationVisibility((ProgressBar)sender);
}
private static void StopAnimationOnCompletion_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) {
var progressBar = (ProgressBar)sender;
if (e.NewValue == progressBar.Maximum || e.OldValue == progressBar.Maximum) {
ReevaluateAnimationVisibility(progressBar);
}
}
private static void ReevaluateAnimationVisibility(ProgressBar progressBar) {
if (GetStopAnimationOnCompletion(progressBar)) {
var animationElement = GetAnimationElement(progressBar);
if (animationElement != null) {
if (progressBar.Value == progressBar.Maximum) {
animationElement.SetCurrentValue(UIElement.VisibilityProperty, Visibility.Collapsed);
} else {
animationElement.InvalidateProperty(UIElement.VisibilityProperty);
}
}
}
}
private static DependencyObject GetAnimationElement(ProgressBar progressBar) {
var template = progressBar.Template;
if (template == null) return null;
return template.FindName("PART_GlowRect", progressBar) as DependencyObject;
}
}
Run Code Online (Sandbox Code Playgroud)
基本上,它添加了一个ValueChanged
调整动画元素可见性的处理程序.
几点说明:
我正在"PART_GlowRect"
寻找动画元素,虽然有人称这是一个黑客.我不同意:这个元素名称是通过TemplatePartAttribute正式记录的,您可以在ProgressBar的声明中看到它.虽然这确实不一定能保证命名元素的存在,但它应该缺少的唯一原因是完全不支持动画功能.如果它受支持但使用的元素名称与记录的元素名称不同,我会认为是一个错误,而不是实现细节.
由于我从模板中提取元素,因此Loaded
在尝试设置初始可见性之前,还需要处理事件(在应用模板时引发)以等待模板可用,并且如果需要,再次设置它当模板被主题更改动态替换时.
我没有明确地Visibility
在Collapsed
和之间切换,而是Visible
使用SetCurrentValue设置为Collapsed
,而使用InvalidateProperty来重置它.SetCurrentValue
应用不优先于其他值源的值,并InvalidateProperty
在不考虑该SetCurrentValue
设置的情况下重新评估该属性.这确保了如果现有样式或触发器会影响正常条件下的可见性(即,当它不是 100%时),如果重复使用进度条(从100%返回到0%),它将重置为该行为而不是硬编码Visible
.
您可以通过复制整个做到这一点ControlTemplate
的ProgressBar
,然后添加一个Trigger
为条件在那里ProgressBar.Value=100
.原样的XAML将使其ProgressBar
行为与现在一样.删除底部的注释标签,当ProgressBar
"值"属性达到100 时,动画将停止.唯一的缺点是,当您更改"最大属性"时,ProgressBar
您还需要更改"触发器".任何人都知道如何将Trigger绑定到Maximum Property的实际值?
<Window x:Class="ProgressBarSpike.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
Loaded="Window_Loaded">
<Window.Resources>
<LinearGradientBrush x:Key="ProgressBarBackground" EndPoint="1,0" StartPoint="0,0">
<GradientStop Color="#BABABA" Offset="0"/>
<GradientStop Color="#C7C7C7" Offset="0.5"/>
<GradientStop Color="#BABABA" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarBorderBrush" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#B2B2B2" Offset="0"/>
<GradientStop Color="#8C8C8C" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarGlassyHighlight" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#50FFFFFF" Offset="0.5385"/>
<GradientStop Color="#00FFFFFF" Offset="0.5385"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarTopHighlight" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#80FFFFFF" Offset="0.05"/>
<GradientStop Color="#00FFFFFF" Offset="0.25"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorAnimatedFill" EndPoint="1,0" StartPoint="0,0">
<GradientStop Color="#00FFFFFF" Offset="0"/>
<GradientStop Color="#60FFFFFF" Offset="0.4"/>
<GradientStop Color="#60FFFFFF" Offset="0.6"/>
<GradientStop Color="#00FFFFFF" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorDarkEdgeLeft" EndPoint="1,0" StartPoint="0,0">
<GradientStop Color="#0C000000" Offset="0"/>
<GradientStop Color="#20000000" Offset="0.3"/>
<GradientStop Color="#00000000" Offset="1"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorDarkEdgeRight" EndPoint="1,0" StartPoint="0,0">
<GradientStop Color="#00000000" Offset="0"/>
<GradientStop Color="#20000000" Offset="0.7"/>
<GradientStop Color="#0C000000" Offset="1"/>
</LinearGradientBrush>
<RadialGradientBrush x:Key="ProgressBarIndicatorLightingEffectLeft" RadiusY="1" RadiusX="1" RelativeTransform="1,0,0,1,0.5,0.5">
<GradientStop Color="#60FFFFC4" Offset="0"/>
<GradientStop Color="#00FFFFC4" Offset="1"/>
</RadialGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorLightingEffect" EndPoint="0,0" StartPoint="0,1">
<GradientStop Color="#60FFFFC4" Offset="0"/>
<GradientStop Color="#00FFFFC4" Offset="1"/>
</LinearGradientBrush>
<RadialGradientBrush x:Key="ProgressBarIndicatorLightingEffectRight" RadiusY="1" RadiusX="1" RelativeTransform="1,0,0,1,-0.5,0.5">
<GradientStop Color="#60FFFFC4" Offset="0"/>
<GradientStop Color="#00FFFFC4" Offset="1"/>
</RadialGradientBrush>
<LinearGradientBrush x:Key="ProgressBarIndicatorGlassyHighlight" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#90FFFFFF" Offset="0.5385"/>
<GradientStop Color="#00FFFFFF" Offset="0.5385"/>
</LinearGradientBrush>
<Style x:Key="ProgressBarStyleStopAnimation" TargetType="{x:Type ProgressBar}">
<Setter Property="Foreground" Value="#01D328"/>
<Setter Property="Background" Value="{StaticResource ProgressBarBackground}"/>
<Setter Property="BorderBrush" Value="{StaticResource ProgressBarBorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Grid x:Name="TemplateRoot" SnapsToDevicePixels="true">
<Rectangle Fill="{TemplateBinding Background}" RadiusY="2" RadiusX="2"/>
<Border Background="{StaticResource ProgressBarGlassyHighlight}" CornerRadius="2" Margin="1"/>
<Border BorderBrush="#80FFFFFF" BorderThickness="1,0,1,1" Background="{StaticResource ProgressBarTopHighlight}" Margin="1"/>
<Rectangle x:Name="PART_Track" Margin="1"/>
<Decorator x:Name="PART_Indicator" HorizontalAlignment="Left" Margin="1">
<Grid x:Name="Foreground">
<Rectangle x:Name="Indicator" Fill="{TemplateBinding Foreground}"/>
<Grid x:Name="Animation" ClipToBounds="true">
<Rectangle x:Name="PART_GlowRect" Fill="{StaticResource ProgressBarIndicatorAnimatedFill}" HorizontalAlignment="Left" Margin="-100,0,0,0" Width="100"/>
</Grid>
<Grid x:Name="Overlay">
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="15"/>
<ColumnDefinition Width="0.1*"/>
<ColumnDefinition MaxWidth="15"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle x:Name="LeftDark" Fill="{StaticResource ProgressBarIndicatorDarkEdgeLeft}" Margin="1,1,0,1" RadiusY="1" RadiusX="1" Grid.RowSpan="2"/>
<Rectangle x:Name="RightDark" Grid.Column="2" Fill="{StaticResource ProgressBarIndicatorDarkEdgeRight}" Margin="0,1,1,1" RadiusY="1" RadiusX="1" Grid.RowSpan="2"/>
<Rectangle x:Name="LeftLight" Grid.Column="0" Fill="{StaticResource ProgressBarIndicatorLightingEffectLeft}" Grid.Row="2"/>
<Rectangle x:Name="CenterLight" Grid.Column="1" Fill="{StaticResource ProgressBarIndicatorLightingEffect}" Grid.Row="2"/>
<Rectangle x:Name="RightLight" Grid.Column="2" Fill="{StaticResource ProgressBarIndicatorLightingEffectRight}" Grid.Row="2"/>
<Border x:Name="Highlight1" Background="{StaticResource ProgressBarIndicatorGlassyHighlight}" Grid.ColumnSpan="3" Grid.RowSpan="2"/>
<Border x:Name="Highlight2" Background="{StaticResource ProgressBarTopHighlight}" Grid.ColumnSpan="3" Grid.RowSpan="2"/>
</Grid>
</Grid>
</Decorator>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="LayoutTransform" TargetName="TemplateRoot">
<Setter.Value>
<RotateTransform Angle="-90"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsIndeterminate" Value="true">
<Setter Property="Visibility" TargetName="LeftDark" Value="Collapsed"/>
<Setter Property="Visibility" TargetName="RightDark" Value="Collapsed"/>
<Setter Property="Visibility" TargetName="LeftLight" Value="Collapsed"/>
<Setter Property="Visibility" TargetName="CenterLight" Value="Collapsed"/>
<Setter Property="Visibility" TargetName="RightLight" Value="Collapsed"/>
<Setter Property="Visibility" TargetName="Indicator" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsIndeterminate" Value="false">
<Setter Property="Background" TargetName="Animation" Value="#80B5FFA9"/>
</Trigger>
<!--
<Trigger Property="Value" Value="100">
<Setter Property="Visibility" TargetName="Animation" Value="Collapsed"/>
</Trigger>
-->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel>
<ProgressBar Name="Progress" Height="50" Style="{DynamicResource ProgressBarStyleStopAnimation}"/>
</StackPanel>
</Window>
Run Code Online (Sandbox Code Playgroud)
小智 8
Dabblernl的答案很强大.这是一个hack(因为它依赖于执行发光的元素的内部名称,这是一个实现细节,可能在后续版本中更改):
void SetGlowVisibility(ProgressBar progressBar, Visibility visibility) {
var anim = progressBar.Template.FindName("Animation", progressBar) as FrameworkElement;
if (anim != null)
anim.Visibility = visibility;
}
Run Code Online (Sandbox Code Playgroud)
如果a ProgressBar
实现如何更改,这个hack可能会停止工作.
另一方面,完全取代XAML和样式的解决方案可以锁定并修复颜色,边框等,并禁用将来可能添加到更新版本的ProgressBar的行为...
编辑:实现细节确实发生了变化.改"PART_GlowRect"
到"Animation"
-前者只用在aero.normalcolor.xaml
,而后者则是在最近的使用aero2.normalcolor.xaml
和aerolite.normalcolor.xaml
太.
归档时间: |
|
查看次数: |
12535 次 |
最近记录: |