Подтвердить что ты не робот

Как я могу остановить WPF ProgressBar пульсировать/оживлять, когда он достигает 100%?

У меня есть приложение WPF 4 на основе MVVM, которое использует ProgressBar, чтобы показать процентное завершение долговременной операции.

<ProgressBar Name="ProgressBar"
    IsIndeterminate="False"
    Minimum="0"
    Maximum="100"
    Value="{Binding Path=ProgressPercentageComplete, Mode=OneWay}"
    Visibility="Visible"/>

Я рад, что анимация "пульсирующих" будет возникать во время движения индикатора прогресса, но как только она достигает 100%, я бы хотел, чтобы она прекратила анимацию и оставалась статичной на 100%.

Я пробовал настройку IsIndeterminate="False", но это не помогает, и я понимаю, почему после прочтения документации MSDN:

Когда это свойство истинно, ProgressBar оживляет несколько полос движения через ProgressBar в непрерывном и игнорирует свойство Value.

Можно ли остановить эту анимацию? Либо полностью, либо просто на 100%.

4b9b3361

Ответ 1

Вы можете выполнить это, скопировав весь ControlTemplate для ProgressBar, затем добавьте Trigger для условия, где ProgressBar.Value=100. XAML as is сделает ProgressBar вести себя так, как сейчас. Удалить комментарий Метки внизу и анимация прекратятся, когда свойство ProgressBar Значение достигнет 100. Единственная слабость заключается в том, что при изменении Максимального свойства ProgressBar вам необходимо также изменить триггер. Кто-нибудь знает, как связать триггер с фактическим значением 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>

Ответ 2

Я написал обобщенное решение для этого, используя прикрепленное свойство, позволяющее мне переключать поведение на любом ProgressBar просто через прямое свойство или установщик стиля, например:

<ProgressBar helpers:ProgressBarHelper.StopAnimationOnCompletion="True" />

Код:

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

В основном, он добавляет обработчик ValueChanged, который настраивает видимость анимированного элемента.

Несколько примечаний:

  • Я использую "PART_GlowRect" для поиска анимированного элемента, хотя кто-то назвал это взломом. Я не согласен: это имя элемента официально зарегистрировано через TemplatePartAttribute, которое вы можете увидеть в Декларация ProgressBar. Хотя верно, что это не обязательно гарантирует, что именованный элемент существует, единственная причина, по которой он должен отсутствовать, заключается в том, что функция анимации вообще не поддерживается. Если он поддерживается, но использует другое имя элемента, чем документальное, я бы подумал об ошибке, а не о детализации реализации.

  • Поскольку я вытягиваю элемент из шаблона, необходимо также обработать событие Loaded (которое возникает при применении шаблона), чтобы дождаться появления шаблона, прежде чем пытаться установить первоначальной видимости и, при необходимости, установить его снова, когда шаблон заменяется "на лету" изменением темы.

  • Вместо явного переключения Visibility между Collapsed и Visible, я использую SetCurrentValue, чтобы установить до Collapsed и InvalidateProperty до reset it. SetCurrentValue применяет значение, которое не имеет приоритета над другими источниками значений, а InvalidateProperty переоценивает свойство без учета значения параметра SetCurrentValue. Это гарантирует, что если существуют существующие стили или триггеры, которые повлияют на видимость в обычных условиях (т.е. Когда это не на 100%), это будет reset к этому поведению, если индикатор выполнения снова используется (переход от 100% к 0%) вместо того, чтобы быть жестко закодированным до Visible.

Ответ 3

Ответ Dabblernl прочен. Вот хак (потому что он опирается на внутреннее имя элемента, который выполняет свечение, которое является деталью реализации и может меняться в следующей версии):

void SetGlowVisibility(ProgressBar progressBar, Visibility visibility) {
    var anim = progressBar.Template.FindName("Animation", progressBar) as FrameworkElement;
    if (anim != null)
        anim.Visibility = visibility;
}

Если как реализовано изменение ProgressBar, этот хак может перестать работать.

С другой стороны, решение, полностью заменяющее XAML и стили, может блокировать и исправлять цвета, границы и т.д. и отключать поведение, которое может быть добавлено в более новую версию ProgressBar в будущем...

Изменить: Деталь реализации изменилась. Изменен "PART_GlowRect" - "Animation" - первый используется только в aero.normalcolor.xaml, а последний используется в более поздних aero2.normalcolor.xaml и aerolite.normalcolor.xaml тоже.

Ответ 4

Вы можете поменять конвертер на PART_Indicator, который по умолчанию - это ProgressBarBrushConverter, из которого происходит анимация...

...

TranslateTransform transform = new TranslateTransform();
double num11 = num8 * 100;
DoubleAnimationUsingKeyFrames animation = new DoubleAnimationUsingKeyFrames();
animation.Duration = new Duration(TimeSpan.FromMilliseconds(num11));
animation.RepeatBehavior = RepeatBehavior.Forever;
for (int i = 1; i <= num8; i++)
{
    double num13 = i * num7;
    animation.KeyFrames.Add(new DiscreteDoubleKeyFrame(num13, KeyTime.Uniform));
}
transform.BeginAnimation(TranslateTransform.XProperty, animation);

...

логика по умолчанию для ProgressBarBrushConverter может быть изменена в соответствии с вашими потребностями.

Вам может потребоваться передать параметры в конвертер, чтобы он мог проверить значение и предоставить соответствующую анимацию или ее отсутствие в зависимости от состояния ProgressBar.

Ответ 5

Я думаю, что единственный способ - создать собственный стиль для ProgressBar.
Описание MSDN для IsIndeterminate относится к другому поведению, и по умолчанию оно ложно, поэтому установка его ничего не меняет.