2

我有一个我编写的助手类,可用于在我的 GUI 上运行长时间运行的任务。它所做的是使用样式来显示“工作”动画并淡出内容,以便在任务运行时,用户可以看到正在进行的事情。

我的问题是,当长时间运行的任务完成时,它会将内容淡入并隐藏工作动画 - 这是它应该做的,但是因为我使用 MVVM 并且主要是数据绑定用于我的所有内容显示,所以更新到GUI 组件与长时间运行的任务分开发生。即数据绑定 OnPropertyChanged("") 事件触发,然后在长时间运行的任务完成后由 GUI 线程拾取。但问题是当长时间运行的任务完成时,Worker Animation 会关闭,但在数据绑定更新之前。

所以最终的结果是,当任务运行时,工作人员动画按预期显示,但是对于所有树数据的大型数据集,数据绑定更新需要 4-5 秒甚至更长的时间,在此期间,应用程序不会在“工作动画模式”中,只是冻结。

有没有一种方法可以让我的工作动画继续运行,不仅适用于 Long running Method,而且适用于来自 OnPropertyChanged 的​​相关数据绑定更新?

4

2 回答 2

0

考虑使用Extended WPF 工具包中的BusyIndi​​cator。它应该提供您描述的功能。它具有 IsBusy 属性,您可以将其绑定到 ViewModel 中的属性,并在所有工作完成后将其设置为False 。您始终可以像使用其他控件一样更改 BusyIndi​​cator 的样式。在我的解决方案中,我总是将此控件与System.ComponentModel中的 BackgroundWorker 类一起使用,并且我通常 在RunWorkerCompleted的末尾设置IsBusy=false

    private void LongRunningMethod()
    {
        this.IsBusy = true;
        var worker = new BackgroundWorker();
        worker.DoWork += this.LongMethodDoWork;
        worker.RunWorkerCompleted += this.RunWorkerCompleted;
        worker.RunWorkerAsync();
    }

    private void LongMethodDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
    {
        ...
    }

    private void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs runWorkerCompletedEventArgs)
    {
        ...
        this.IsBusy = false;
    }
于 2012-11-14T12:07:41.813 回答
0

谢谢大家的回答。我实际上遇到了一个可能有点争议的解决方案,因为有些人会认为它有点像黑客,但它完全符合我的要求,而且似乎没有其他方法可以做到,所以对我来说,这是一个代码解决方案,而不是一个黑客。

我正在使用我从 codeproject 下载的 WPFBackgroundProgressIndicator 开源项目(我认为),它可以选择在主要内容中显示繁忙指示器,带或不带淡出,或作为弹出窗口,它作为后台线程运行理想以及我为什么选择它。

问题是,当您运行长时间运行的方法时,代码执行同步完成,但所有绑定 OnPropertyChanged("") 更新异步运行并在 Dispatcher 线程上排队,因此您的工作方法在 WPF 控件有机会调用之前完成依赖属性的 Getter,以检索新值。您需要做的是有效地“阻塞”,直到所有 Dispatcher 事件完成,这就是为什么不是每个人都会喜欢这个解决方案,因为它“阻塞”,但这正是我想要做的。我想阻止应用程序直到完全更新完成,因为我不希望用户在数据仍在呈现时能够在视觉上做任何事情,所以这是我的要求。干净的阻塞比混乱的交互更可取。

所以不管你信不信,解决方案是在工作方法调用之后的一行代码。如下。

Application.Current.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null); 

正如您所看到的那样,它有效地将新任务排队到 Dispatcher 线程并阻止当前代码执行直到它完成,但是当您给它最低优先级时,此调用将等到所有 OTHER Dispatcher 执行完成,即所有渲染完成。渲染完成后,将执行此行,您将在所有渲染完成后退出。我在上下文中使用它的完整方法如下。我欢迎您对这种方法的想法和讨论。

public void LongRunningTaskWithFade(BusyDecorator busy, Action longTask)
        {
            if (loading) return;
            loading = true;

            busy.FadeTime = TimeSpan.Zero;
            busy.IsBusyIndicatorShowing = true;

            // in order for setting the opacity to take effect, you have to delay the task slightly to ensure WPF has time to process the updated visual
            Application.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                try
                {
                    longTask();
                    Application.Current.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null); 
                }
                finally
                {
                    HideBusyDisplay(busy);                    
                }
            }), DispatcherPriority.Background);
        }
于 2012-11-16T10:02:44.797 回答