WPF中的BeginUpdate等效方法是什么?

9

我的问题是我需要在WPF中向ListBox添加大量的项。在WinForms中,您只需使用BeginUpdate();方法,添加所有内容,最后使用EndUpdate();方法即可。

那么,在WPF listbox中,如何停止绘制直到添加完所有项,然后一次性绘制所有内容呢?

3个回答

15

正确使用 Dispatcher。

必须从 Dispatcher 线程将内容添加到 ListBox(或其数据源)中,否则 UI 将崩溃。当您这样做时,你要么已经在该线程上的一个方法中了,要么(更有可能的是)在后台线程中并使用 Dispatcher.BeginInvoke 添加项。

当您将更新传递给 Dispatcher 时,请使用Dispatcherpriority.Normal 作为优先级。Dispatcher 有一个工作项队列,您的 Normal 项将推入(也许令人惊讶地)高优先级队列。

WPF 在底层数据更改时运行的用于更新数据绑定的代码位于 DispatcherPriority.DataBind,低于 Normal。这意味着数据绑定通常不会更新,直到所有项添加完成(或者,如果您的项添加时间很长,则可能在 Dispatcher 在添加项之间处于空闲状态时发生)。

实际渲染控件的代码(例如,当其绑定信号更新时)的运行位置是 DispatcherPriority.Render,甚至比绑定还要低。这意味着仅当 Dispatcher 完成所有需要更新的绑定时,才会渲染控件,而这又仅会在 Dispatcher 处理完所有项添加时才发生。

如果听起来有点奇怪,请记住,每个层次(更新-绑定-渲染)都会在下一层设置一个隐喻性的标志 - 您不会得到十次绑定和十次渲染。如果您的项添加速度很快,则会得到所有添加,然后是一次绑定和一次渲染 - 这非常完美。

基本上:如果按照 Dispatcher 的意图使用,您将无需担心。似乎将渲染作为 Dispatcher 上相对较低的优先级是错误的,但实际上这非常聪明 :-)


5
看起来你可能是在逐个程序地将项目添加到列表框中。更好的方法是将要显示的项目存储在模型中,并将模型绑定到列表框的数据上下文中。这将允许WPF处理渲染更新。
如果您在问题中添加XAML和代码片段(以显示您当前正在执行的操作),我们可能会提供更好的答案。
编辑:添加了一个非常简单的示例。
以下是一个示例,将视图模型绑定到主窗口(视图),然后XAML声明ListBox的数据绑定到视图模型的Numbers属性。

MainViewModel.cs

public class MainViewModel
{
    public IEnumerable<int> Numbers
    {
        get { return Enumerable.Range(1, 1000); }
    }
}

MainWindow.xaml

<Window
  x:Class="WpfApplication1.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Height="350"
  Width="525"
  >
  <ListBox ItemsSource="{Binding Numbers}"/>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainViewModel();
    }
}

如果你刚开始学习WPF,我强烈推荐阅读Adam Nathan的Windows Presentation Foundation Unleashed。这是一本很棒的书,而且还是彩色的。在读完这本书之后再去读其他书就像是从使用Visual Studio回到记事本一样。
想要了解更多关于Model-View-ViewModel方面的内容,请查看Josh Smith的Advanced MVVM书。

是的,我正在以编程方式向ListBox添加项目。好吧,没有太多的代码可以展示,因为我还没有编写整个应用程序,我只是通过使用Listbox和以下代码来测试如何实现: for (int i = 0; i < 1000; i++) { lbQuery.Items.Add(i); }无论如何,您能给我一个您解决方案的代码示例吗? - Lith
+1,首先将它们构建成一个列表会更好。除非这不可能,否则你将不得不忍受每次添加都需要重新绘制的开销。 - Alastair Pitts
1
如果您可以一次快速填充整个列表,那么在绑定之前应该这样做... 如果不能,则如果您可以批量添加或类似方式添加,则不一定需要为每个添加重新绘制。 - Dan Puzey
如果您有LINQ可用,@Lith listbox.ItemsSource = lbQuery.Items.Take(1000); 将在代码中等效。但是,如果我是您,我真的会像@Damian建议的那样设置一个适当的模型并正确地完成它。 - Bryan Anderson
@Bryan 嗯,我正在尝试实现Damian的解决方案,但似乎做不对?或者更确切地说,我甚至不知道该怎么做? - Lith
@Bryan。感谢您的示例。虽然我没有真正看到使用您的方法与for循环之间有很大的区别,但我仍然从中学到了一些东西。至于书籍,我一定会去看看的。再次感谢!也感谢其他人尝试帮助我 =)。 - Lith

4

看到您正在处理大型列表,您可能想了解一下WPF UI虚拟化。以下是一系列关于此主题的好博客文章:

http://blogs.msdn.com/dancre/archive/tags/VirtualizingTilePanel/default.aspx

通常情况下,您将项目添加到集合中,并将该集合数据绑定到一个ListBox控件上。如果正确操作ListBox,则不应刷新正在添加但不可见的项目。


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