C# 等待动画

本文介绍两种C#中实现等待动画的方法:一是使用BackgroundWorker组件处理后台任务,确保UI响应;二是自定义等待窗口,通过分离UI线程和后台任务提高用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 方法一:BackgroundWorker

解释参考MSDN和百度百科:BackgroundWorker是·net里用来执行多线程任务的控件,它允许编程者在一个单独的线程上执行一些操作。耗时的操作(如下载和数据库事务)在长时间运行时可能会导致用户界面 (UI) 始终处于停止响应状态。如果您需要能进行响应的用户界面,而且面临与这类操作相关的长时间延迟,则可以使用BackgroundWorker类方便地解决问题。

意思就是:可以做一些非用户界面的工作,缺点就是里面不能有UI的活(结束后可以)。添加UI控件会报“单线程STA***”错误。

【2024/8/26】Dispatcher.BeginInvoke?

下面实际使用示例代码:

var waiting = new Waiting();// 动画,自定义
            if (Application.Current != null && Application.Current.MainWindow != null)
            {
                waiting.Owner = Application.Current.MainWindow;
            }
waiting.backgroundWorker1.DoWork += BackgroundWorker_DoWork;// 后台的活(非UI)

            waiting.ShowDialog();
            waiting.Close();

BackgroundWorker简洁使用,检索很多例子:

            backgroundWorker1 = new BackgroundWorker();
            //backgroundWorker1 .DoWork += backgroundWorker1_DoWork;
            //backgroundWorker1 .WorkerSupportsCancellation = true;
            backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
            if (!this.backgroundWorker1.IsBusy)
            {
                this.backgroundWorker1.RunWorkerAsync();
            }

Waiting交互完整代码:

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

[2024/8/26] 中间想要停止,可以使用ProgressChanged,配合CancellationPending属性使用。

private void Worker_DoWork(object sender, DoWorkEventArgs e)
        {
            var worker = (BackgroundWorker)sender;
            while (!worker.CancellationPending)
            {
                Thread.Sleep(500);
                if (_caches == null) continue;
                foreach (var ri in _caches)
                {
                    var cachelist = _dataLogic.Service.GetRealtime(ri.CachesId);
                    if (cachelist == null || cachelist.Rows.Count == 0) continue;

                    cachelist.DefaultView.Sort = "TriggerTime desc";
                    DataTable maxTriggerTimes = cachelist.DefaultView.ToTable();
                    if (maxTriggerTimes == null || maxTriggerTimes.Rows.Count <= 0) continue;

                    foreach (RowColumnInfo rc in ri.RowColumnInfos)
                    {
                        Thread.Sleep(10);
                        Dispatcher.BeginInvoke(new Action<int, int, object>(
                                delegate (int row, int column, object value)
                                {
                                    if (_view != null)
                                    {
                                        _view.SetCellValueInISheet(_activeWorksheet, value, row, column);
                                        _view.GridModelList[_activeWorksheet].InvalidateCell(GridRangeInfo.Cell(row, column));
                                    }
                                }), new object[] { rc.RowIndex, rc.ColumnIndex, maxTriggerTimes.Rows[0]["Value"] });
                    }
                    Thread.Sleep(10);
                }
            }
        }

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public BackgroundWorker backgroundWorker1;

        public Waiting()
        {
            InitializeComponent();

            SolidColorBrush solid = new SolidColorBrush();
            solid.Color = Colors.White;
            solid.Opacity = 0.5;
            Header = IOResource.Testing;
            this.Background = solid;
            this.DataContext = this;

            backgroundWorker1 = new BackgroundWorker();
            backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
            if (!this.backgroundWorker1.IsBusy)
            {
                this.backgroundWorker1.RunWorkerAsync();
            }
        }

        /// <summary>
        /// Gets or sets the Header of the waiting
        /// </summary>
        /// <value>
        /// The default value is null.
        /// </value>
        public object Header
        {
            get { return (object)GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        ///<summary>
        /// Using a DependencyProperty as the backing store for Header.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register("Header", typeof(object), typeof(Waiting), new PropertyMetadata(null));

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.Close();
        }

        public void SetProgressText(string desc)
        {
            this.Dispatcher.Invoke(new Action(() =>
            {
                Header = desc;
            }));
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            if (!this.backgroundWorker1.IsBusy)
            {
                this.backgroundWorker1.RunWorkerAsync();
            }
        }

带入UI元素的截图忘记保留,以后再加上。

  • 方法二:自定义等待动画方式

等待动画的要义就是动画占用UI线程,时间较长的活放在后台做,自己个人做的等待动画,不会报“单线程STA***”错误。和上面比较各有优缺点。可以多次调用不用担心,在遇见多次弹窗的情况。

主要方法:

        public static void BusyIndicatorWindow(Action actualAction, string header)
        {
            try
            {
                Action action = (() =>
                {
                    try
                    {
                        actualAction.Invoke();
                    }
                    catch (Exception)
                    { }

                    BusyWindow.BusyIndicator.EndCloseBusyIndicator();
                });
                Action busyaction = (() =>
                {
                    BusyWindow.BusyIndicator.OpenBusyIndicator(true, header);
                });

                System.Windows.Application.Current.MainWindow.Dispatcher.Invoke(busyaction, System.Windows.Threading.DispatcherPriority.Normal);
                System.Windows.Application.Current.MainWindow.Dispatcher.Invoke(action, System.Windows.Threading.DispatcherPriority.Background);
            }
            catch (Exception ex)
            {
                Action action = (() =>
                {
                    BusyWindow.BusyIndicator.EndCloseBusyIndicator();
                });
                System.Windows.Application.Current.MainWindow.Dispatcher.Invoke(action, System.Windows.Threading.DispatcherPriority.Background);
            }
        }

下面是使用示例代码:

           var view = new View(model);
            // 等待动画
            Action action = (() =>
            {
                // 长时间任务
            });
            BusyWindow.BusyIndicator.BusyIndicatorWindow(action, Resources.Resource.ProjectLoading);// 动画等待,执行完action主体

BusyIndicator主要代码:

        public static void OpenBusyIndicator(bool isBusy, string header, AnimationTypes animationType = AnimationTypes.SingleCircle)
        {
            _isBusy = isBusy;
            _header = header;
            _animationType = animationType;
            _busyStart = true;
            ShowBusyIndicator();
        }

        /// <summary>
        /// 打开动画
        /// </summary>
        public static void ShowBusyIndicator()
        {
            if (_busyStart)
            {
                try
                {
                    if (_busyWindows != null)
                    {
                        CloseBusyIndicator();
                        if (_busyWindows != null)
                        {
                            return;
                        }
                    }

                    if (thread == null)
                    {
                        thread = new Thread(() =>
                        {
                            try
                            {
                                if (_busyWindows == null)
                                {
                                    _busyWindows = new BusyWindows(_isBusy, _header, null, _animationType);
                                    _busyWindows.ShowDialog();
                                }
                            }
                            catch()
                            {
                                _busyWindows = null;
                            }
                        });
                        thread.SetApartmentState(ApartmentState.STA);
                        thread.Start();
                        while (_busyWindows == null) // 这个一定要有
                        {
                            Thread.Sleep(1);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.Warn("BusyIndicator ShowBusyIndicator:" + ex.Message + Environment.NewLine + ex.StackTrace);
                    CloseBusyIndicator();
                }
            }
        }

        /// <summary>
        /// 结束动画
        /// </summary>
        public static void EndCloseBusyIndicator()
        {
            try
            {
                CloseBusyIndicator();
            }
            catch (Exception) { }

            _busyStart = false;
        }

以上个人总结,仅供参考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值