【问题标题】:Have an ObservableCollection update the UI as elements get added in让 ObservableCollection 在添加元素时更新 UI
【发布时间】:2011-01-13 07:31:46
【问题描述】:

我正在编写一个 silverlight 应用程序,我正在尝试改进加载时间。

当我的页面加载时,我首先初始化我的 ObservableCollection:

        this.MyItems = new ObservableCollection<Item>();

我的 UI 是一个 ListBox,我通过代码将其绑定到 ObservableCollection。在 MainPage_Loaded 中:

        MyList.ItemsSource = App.ViewModel.MyItems;

现在我将 UI 绑定到我的模型。我希望这是有效的,因为集合是空的,并且 UI 的其余部分可以继续加载(不确定我的假设是否正确)。

        DataContext = App.ViewModel;

现在我想将项目添加到我的收藏中:

        for (int i = 0; i < number_of_items; i++)
        {
            this.MyItems.Add(myItems[i]); // myItems is a List<Item> already populated
            Thread.Sleep(20);
        }

我的目标是让线程休眠,以便它有时间为每个列表框项呈现 UI。另外,我希望我的 UI 一次显示一个项目。

结果是 ListBox 元素同时出现。如果我将睡眠时间设置为 1 秒,则 ListBox 会在 1 秒后填充元素数。

优化此操作的好方法是什么?如果它是徒劳的,我也可以将我的 ListBox 绑定到一个完全填充的 ObservableCollection。谢谢!

【问题讨论】:

  • 你使用 VirtualizingStackPanel 吗?

标签: windows-phone-7 silverlight-3.0


【解决方案1】:

尝试将循环移动到后台线程。这是一种方法。

Phạm Tiểu Giao - Threads in WP7

请注意,您需要调度 UI 更新。类似的东西

Dispatcher.BeginInvoke( () => { this.MyItems.Add(myItems[i]); } );

如果您想使用固定的时间段,睡眠将起作用。只需确保时间段始终长于更新显示所用的时间,否则您可能会使 UI 线程过载,更新速度快于其处理速度。

【讨论】:

  • 谢谢,这正是我想要的 :)
  • 如果我让它休眠 1000 毫秒效果很好,但在 100 毫秒时减少会引入一些奇怪的行为——它添加了 i == for 上限的项目(引发了 arguemntoutofrange 异常),并且它同时增加了几次。这是预期的 100 毫秒的睡眠吗?这很奇怪,因为即使是 100 毫秒来填充列表框也有点慢,我期待 20 或 50 毫秒。
  • 我建议对您的代码进行一些基本分析。尝试在相关代码之前/之后创建 DateTimes,并为差异计算 TimeSpan。把它的猜测出来。请记住,可能还有更慢和更繁忙的设备(后台邮件等)。您可以使用 debug.writeline 记录结果。有时您会发现效率低下,也可以进行优化。
  • 最好在每个 BeginInvoke 中添加 2-4 个项目而不是 1 个。但您肯定走在正确的轨道上。
【解决方案2】:

我们绑定到 ObservableCollection 的原因是通过 INotifyPropertyChanged 接口实现发生属性更新的内置通知。这会导致在每次更新基础集合时触发一个事件,进而导致相关 UI 元素(在本例中为 ListBox)的重绘。数据模板在每次重绘时应用于集合中的每个项目,并通过数据绑定自动完成。这些项目被添加到您的收藏中的速度比抽奖发生的速度更快(在一个单独的线程上),因此为什么看起来您的加载被延迟到所有项目都被添加。您在屏幕上视觉上错过了重绘周期,因为它们在添加新项目时在屏幕上被绘制并失效。

这意味着您的 Thread.Sleep 调用只会延迟对添加的每个项目的元素的完全重绘(* 添加的项目数解释了为什么您的 UI 会在每个项目上完全重绘,但只有在进行了各自的 Thread.Sleep 调用,这会阻塞 UI 线程 n * sleepValue 时间)。这就是为什么我们需要使用如上所述的 Dispatcher 对象,因为这些调用是在不同的线程上进行的。这允许我们从 UI 线程重绘,这实质上是同步阻塞调用。

我绝对不会在这里使用 Dispatcher,因为它是多余的,并且会阻止本地同步发生,因为 Dispatcher 可能会引用尚未创建并添加到可视树或逻辑树中的元素(正如您所清楚体验的那样)评论将值设置为 1000 毫秒而不是 20 毫秒)。由于缺少更好的术语,您仍将重绘集合中的所有项目,因为每个项目都会使您的睡眠呼叫无效或不起作用。

我提供的另一种解决方案是,您可以在数据模板的根元素的 Opacity 属性上添加故事板动画,以创建项目执行“一次添加一个”的视觉效果。这样,当每个项目都添加到基础集合中时,它们将使用不透明淡入淡出动画进行绘制,从而产生一种幻觉,即每个项目一次添加一个(并动画到视图中),并带有单独的动画(在不同的偏移量内)定义的动画)。我相信这会给你你想要的效果。但是由于绘图调用来自 ListBox 因为它维护其项目集合,所以整个集合将在每个 .Add 调用到您的 ViewModel 项目 ObservableCollection 对象时失效。确实没有办法覆盖这种行为,因为它发生在层次结构的上游一级。我建议不要使用所提供的方法。

【讨论】:

  • 所以每当有新项目添加到集合源时,ListBox 都会重绘自身?
猜你喜欢
  • 2020-07-22
  • 1970-01-01
  • 1970-01-01
  • 2014-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-07
  • 1970-01-01
  • 2013-08-10
相关资源
最近更新 更多