一个自定义的可控制的时间进度条

tech2025-07-22  4

WPF中的常规进度条ProgressBar使用Value,Max Value来控制进度,但是有时候并不能完全满足我们的业务需求,需要自己开发一个自定义的进度条,今天带来一个可以根据设置的时间显示进度动画的进度条,并可以控制进度播放流程。

下面演示一个规定在十秒内播放完毕的进度条动画:

这是一个自己封装的User Control,下面展示代码。

XAML:

<StackPanel Width="auto" Height="37">         <Grid Height="25" Background="Transparent" Name="borderPanel" ClipToBounds="False">             <Rectangle Fill="#008e97" Height="25"  Width="5"  VerticalAlignment="Center" HorizontalAlignment="Left" x:Name="progressRect" ClipToBounds="False"/>             <Border BorderBrush="#008e97" BorderThickness="2" Height="25" x:Name="mainBorder" HorizontalAlignment="Left" ClipToBounds="False"/>         </Grid>     </StackPanel>

XAML.CS:

public partial class TimeProgressUC : UserControl { public TimeProgressUC() { InitializeComponent(); } private Storyboard _storyboard = new Storyboard(); #region 依赖属性 public static readonly DependencyProperty PregressTimeProperty = DependencyProperty.Register("PregressTime", typeof(double), typeof(TimeProgressUC), new FrameworkPropertyMetadata((double)0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, CommonPropertyChangedCallback)); public static readonly DependencyProperty DPIProperty = DependencyProperty.Register("DPI", typeof(int), typeof(TimeProgressUC), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, CommonPropertyChangedCallback)); public static readonly DependencyProperty StartRenderProperty = DependencyProperty.Register("StartRender", typeof(bool), typeof(TimeProgressUC), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, CommonPropertyChangedCallback)); public static readonly DependencyProperty StopRenderProperty = DependencyProperty.Register("StopRender", typeof(bool), typeof(TimeProgressUC), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, CommonPropertyChangedCallback)); public static readonly DependencyProperty HadRenderTimeProperty = DependencyProperty.Register("HadRenderTime", typeof(double), typeof(TimeProgressUC), new FrameworkPropertyMetadata((double)0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null)); public static readonly DependencyProperty OnProgressCompleteProperty = DependencyProperty.Register("OnProgressCompleteCommand", typeof(ICommand), typeof(TimeProgressUC), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, null)); public ICommand OnProgressCompleteCommand { get { return (ICommand)GetValue(OnProgressCompleteProperty); } set { SetValue(OnProgressCompleteProperty, value); } } //动画的总时间 public double PregressTime { get { return (double)GetValue(PregressTimeProperty); } set { SetValue(PregressTimeProperty, value); } } //单位刻度,每秒所占的像素 public int DPI { get { return (int)GetValue(DPIProperty); } set { SetValue(DPIProperty, value); } } //开始动画 public bool StartRender { get { return (bool)GetValue(StartRenderProperty); } set { SetValue(StartRenderProperty, value); } } //停止动画 public bool StopRender { get { return (bool)GetValue(StopRenderProperty); } set { SetValue(StopRenderProperty, value); } } //完成进度 public double HadRenderTime { get { return (double)GetValue(HadRenderTimeProperty); } set { SetValue(HadRenderTimeProperty, value); } } #endregion private static void CommonPropertyChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs args) { TimeProgressUC uc = obj as TimeProgressUC; if (args.Property == StartRenderProperty) { uc?.GoOnPregress(); return; } if (args.Property == StopRenderProperty) { if ((bool)args.NewValue) { uc.StopRenderProgress(); } return; } uc?.Refresh(); } internal void Refresh() { if (!IsLoaded) return; mainBorder.Width = PregressTime * DPI; borderPanel.Width = mainBorder.Width; progressRect.Width = HadRenderTime* DPI; } internal void GoOnPregress() { if (StartRender) { double processTime = PregressTime - HadRenderTime; HadRenderTime = Math.Round(progressRect.Width / DPI, 2) + processTime; StartLinearAnimation(processTime , processTime * DPI); StartRender = false; StopRender = false; } } internal void StartLinearAnimation(double scanTime, double targetWidth) { if(_storyboard.Children.Count>0) { _storyboard.Resume(progressRect); return; } DoubleAnimation progressWidthAnimation = new DoubleAnimation(); progressWidthAnimation.Completed += ProgressWidthAnimation_Completed; progressWidthAnimation.From = progressRect.Width; progressWidthAnimation.To = targetWidth; progressWidthAnimation.Duration = new Duration(TimeSpan.FromSeconds(scanTime)); Storyboard.SetTargetName(progressWidthAnimation, progressRect.Name); Storyboard.SetTargetProperty(progressWidthAnimation, new PropertyPath(FrameworkElement.WidthProperty)); _storyboard.Children.Add(progressWidthAnimation); _storyboard.Begin(progressRect,true); } private void ProgressWidthAnimation_Completed(object sender, EventArgs e) { HadRenderTime = PregressTime; OnProgressCompleteCommand.Execute(null); } private void StopRenderProgress() { _storyboard.Pause(progressRect); HadRenderTime = Math.Round(progressRect.ActualWidth / DPI, 2); progressRect.Width = progressRect.ActualWidth; } private void mainControl_Loaded(object sender, RoutedEventArgs e) { Refresh(); } }

这是一个封装的UserContorl,在它的CS文件里面,定义了几个依赖属性,用于在调用时绑定ViewModel,其中StopRender 和StopRender用来控制动画,DPI表示单位时间所占的像素,用来控制控件的长度,PregressTime和HadRenderTime分别用来表示进度条的整体时间和暂停时的已完成时间。

控制进度动画的流程主要用到了故事版。

另外还定义了一个可以在进度条流程走完之后触发的OnProgressCompleteCommand,可在ViewModel中触发相关的业务流程。

在页面调用时:

<StackPanel Orientation="Horizontal"> <Button Width="100" Height="20" Content="开始" Margin="0,10,0,0" Command="{Binding StartCommand}"/> <Button Width="100" Height="20" Content="结束" Margin="0,10,0,0" Command="{Binding StopCommand}"/> <basic:TimeProgressUC PregressTime="{Binding ProgressTime}" DPI="{Binding DPI}" StartRender="{Binding StartRender}" StopRender="{Binding StopRender}" OnProgressCompleteCommand="{Binding OnProgressCompleteCommand}"/> </StackPanel>

ViewModel:

public class ViewModel:NotificationObject { private double dpi=50; public double DPI { get { return dpi; } set { dpi = value; RaisePropertyChanged(() => DPI); } } private double progressTime = 10; public double ProgressTime { get { return progressTime; } set { progressTime = value; RaisePropertyChanged(() => ProgressTime); } } private bool startRender; public bool StartRender { get { return startRender; } set { startRender = value; RaisePropertyChanged(() => StartRender); } } private bool stopRender; public bool StopRender { get { return stopRender; } set { stopRender = value; RaisePropertyChanged(() => StopRender); } } private DelegateCommand startCommand = null; public DelegateCommand StartCommand { get { if (startCommand == null) { startCommand = new DelegateCommand(Start); } return startCommand; } } private void Start() { StartRender = true; } private DelegateCommand stopCommand = null; public DelegateCommand StopCommand { get { if (stopCommand == null) { stopCommand = new DelegateCommand(Stop); } return stopCommand; } } private void Stop() { StopRender = true; } private DelegateCommand onProgressCompleteCommand = null; public DelegateCommand OnProgressCompleteCommand { get { if (onProgressCompleteCommand == null) { onProgressCompleteCommand = new DelegateCommand(MyMethod); } return onProgressCompleteCommand; } } private void MyMethod() { MessageBox.Show("Complete!~"); } }

如此,控制进度动画的代码可以完全定义在ViewModel中,同时可以根据业务需求扩展进度条。

最新回复(0)