WPF在模板中平滑地动画ProgressBar

时间:2014-10-28 20:55:59

标签: 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>

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

如何将TargetName设置为模板本身,以便它适用于使用此模板的所有控件?如何设置&#34; To&#34;传入的价值?似乎有一种方法可以抓住&#34; Binding&#34;值,但我有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>

任何帮助将不胜感激!

4 个答案:

答案 0 :(得分: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;
    }
}

简单地用法:

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>

答案 1 :(得分: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));
            }
        }
    }
}

这是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>

答案 2 :(得分:1)

我知道这个问题已经解决,但我找到了一个非常好的实现,并不需要创建UserControl。它模仿&#34;理发杆效应&#34;并开箱即用:

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

只需根据自己的喜好编辑速度和颜色。

答案 3 :(得分:0)

我在另一个论坛发布了类似的概念,下面提到了链接。我希望这会对你有所帮助。

http://www.codeproject.com/Tips/837994/WPF-Preloader-Control

干杯