WPF在模板中平滑地动画ProgressBar

Ter*_*oll 4 c# wpf mvvm progress-bar

我正在开发一个MVVM应用程序,我希望有一个ProgressBar,当该属性发生变化时,它可以平滑地动画到它的新值.我已经使用c#看到了这个问题的几个答案,但我更喜欢在模板中完成所有操作.我遇到的问题是正确设置和定位事件和故事板.这是我目前的情况:

进度条 -

风格 - (只是触发器)

                    <ControlTemplate.Triggers>
                    <EventTrigger RoutedEvent="RangeBase.ValueChanged">
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation
                                    Storyboard.TargetName="???????" 
                                    Storyboard.TargetProperty="Value"
                                    To="???????" Duration="0:0:5"  />
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
Run Code Online (Sandbox Code Playgroud)

我从这里获取了触发器代码:http://msdn.microsoft.com/en-us/library/system.windows.controls.progressbar(v = vs.110).aspx.

如何将TargetName设置为模板本身,以便它适用于使用此模板的所有控件?如何为传入值设置"收件人"?似乎有一种方法可以获取"Binding"值,但我有Value和Max都绑定在progressbar元素上.怎么会知道怎么用?

以下是整个模板供参考:

    <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>
Run Code Online (Sandbox Code Playgroud)

任何帮助,将不胜感激!

Nie*_*lny 5

我认为这是更好的方式.

您可以创建行为来执行此操作.(MVVM WPF)

创建类:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

简单的用法:

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>
Run Code Online (Sandbox Code Playgroud)


Ter*_*oll 2

我实际上从未找到解决方案。我最终只是编写了自己的控件。从技术上讲,这不是问题的答案,但我想我也可以发布它。如果有人正在寻找 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));
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是 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>
Run Code Online (Sandbox Code Playgroud)