WPF绑定渲染GUI进度

3
我知道有很多实现方式,但我仍然无法找到真正有用的东西...
每当我将某个组件的DataContext或ItemsSource设置为某个大对象时,都会出现“渲染时间冻结GUI”,这使得应用程序变得非常烦人(即使使用虚拟化)。
我知道我可以迭代对象并逐个设置项目并显示进度,但我正在寻找其他方法,可以让我在GUI渲染时显示一些移动指示。我还希望有一些进度条,而不仅仅是改变鼠标光标。
有没有一个体面的方法来实现以下内容?
非常感谢
3个回答

3

Zamboni的例子非常好,但仍然无法解决GUI冻结问题

正如提到的那样,目前没有简单的方法来在GUI忙于渲染时更新GUI控件。

我目前找到了一些事件,GUI渲染时是“活跃的”,尽管它应该在不需要时关闭,因为它可能每秒触发60次。

CompositionTarget.Rendering += ReportRenderProgress;

您可以根据需要实现ReportRenderProgress()以通知进度条更新。目前,在WPF中没有更好的解决方案来在渲染时更新进度指示,因此我将其标记为答案。


2
BackgroundWorker 拥有你所需的一切。 编辑 在 WPF 中,调度程序会自动被调用以实现跨线程方法调用。请参阅 MSDN 杂志中的 使用 Dispatcher 构建更加响应的应用程序
我还整理了一些来自 ViewModel 的代码片段,展示了 BackgroundWorker 如何更新进度条。
<ProgressBar 
    VerticalContentAlignment="Stretch" VerticalAlignment="Stretch"
    HorizontalAlignment="Stretch"
    Minimum="0" Maximum="100"
    Value="{Binding Path=BarPosition, Mode=TwoWay}"/>


// configure the background worker...
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.WorkerReportsProgress = true;
_backgroundWorker.WorkerSupportsCancellation = true;
_backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
_backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);
_backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(_backgroundWorker_ProgressChanged);

// control progress bar position
private int _barPosition = 0;
public int BarPosition
{
   get { return _barPosition; }
   set
   {
      _barPosition = value;
      OnPropertyChanged("BarPosition");
   }
}

// long operation
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
  BackgroundWorker bw = sender as BackgroundWorker;
  if (bw != null)
  {
     int pos;

     for (int i = 0; i < 100; ++i
     {
        // report progress here for our long running operation..
        pos = i/100;
        bw.ReportProgress(pos);
        Thread.Sleep(1000);            // fake long operation
     }
  }
}

// report progress,,,
void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
   BackgroundWorker bw = sender as BackgroundWorker;
   if (bw != null)
   {
      BarPosition = e.ProgressPercentage;
   }
}

// reset scroll bar position
void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
   BackgroundWorker bw = sender as BackgroundWorker;
   if (bw != null)
   {
     BarPosition = 0;

     // Forcing the CommandManager to raise the RequerySuggested event to refresh UI...
     CommandManager.InvalidateRequerySuggested();
   }
}

我知道BackgroundWorker类。但在WPF上,我不能使用这个类执行GUI任务,只能执行非GUI操作。正如您在我的问题中看到的那样,我正在寻找一些方法,可以让我保留XAML绑定,并在加载时仍然显示进度 - 这是目前使用BackgroundWorker似乎不可能实现的事情。如果我错了,请告诉我。 - OrPaz
我添加了更多的细节,希望这可以帮助到你。 - Zamboni
谢谢。但我仍然没有看到GUI停止问题的解决方案。我可以使用_Do_Work方法执行非GUI操作,但一旦绑定数据,一切都停止了...这就是我一直在寻找的。 - OrPaz
你需要使用ProgressChanged或RunWorkerCompleted事件来更新UI,或者你需要在DoWork中使用Dispatcher - Zamboni
如果绑定本身需要10秒钟,那么就无法使用ProgressChanged吗?你的进度GUI将会在这10秒钟内停止... - OrPaz

2

这实际上是一个问题。您正在使用GUI线程来填充数据(从对象结构到GUI)。GUI线程需要读取Windows消息队列(防止应用程序冻结,允许应用程序移动/响应),并且需要对GUI进行任何更新。

一种解决方案可能是在绑定后缓慢填充对象结构。这必须从GUI线程中完成,因此您可以添加DoEvents()和/或一些百分比指示器+强制刷新以使应用程序看起来活着。

我很想听听是否有更好的解决方案。


我认为你的方法很有趣。虽然我还在等待更多的回复,但它可能会证明是一个不错的解决方案。 - OrPaz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接