WPF Work-In-Progress动画不显示数据绑定更新

时间:2012-11-14 11:36:14

标签: wpf data-binding progress

我有一个我写过的辅助类,可以用来在我的GUI上运行一个长时间运行的任务。它的作用是使用样式显示“工作”动画并淡化内容,以便在任务运行时,用户可以看到某些内容正在进行中。

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

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

有没有办法可以让我的工作者动画不仅继续运行Long运行方法,还运行OnPropertyChanged的关联数据绑定更新?

2 个答案:

答案 0 :(得分:0)

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

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

答案 1 :(得分:0)

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

我正在使用我从codeproject(我认为)下载的WPFBackgroundProgressIndicator开源项目,该项目可以选择在主要内容中显示繁忙指示符,有或没有淡出,或者作为弹出窗口,它作为背景运行线程是理想的,为什么我选择它。

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

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

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

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

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);
        }