我的问题是我需要在WPF中向ListBox添加大量的项。在WinForms中,您只需使用BeginUpdate();方法,添加所有内容,最后使用EndUpdate();方法即可。
那么,在WPF listbox中,如何停止绘制直到添加完所有项,然后一次性绘制所有内容呢?
正确使用 Dispatcher。
必须从 Dispatcher 线程将内容添加到 ListBox(或其数据源)中,否则 UI 将崩溃。当您这样做时,你要么已经在该线程上的一个方法中了,要么(更有可能的是)在后台线程中并使用 Dispatcher.BeginInvoke
添加项。
当您将更新传递给 Dispatcher 时,请使用Dispatcherpriority.Normal
作为优先级。Dispatcher 有一个工作项队列,您的 Normal
项将推入(也许令人惊讶地)高优先级队列。
WPF 在底层数据更改时运行的用于更新数据绑定的代码位于 DispatcherPriority.DataBind
,低于 Normal
。这意味着数据绑定通常不会更新,直到所有项添加完成(或者,如果您的项添加时间很长,则可能在 Dispatcher 在添加项之间处于空闲状态时发生)。
实际渲染控件的代码(例如,当其绑定信号更新时)的运行位置是 DispatcherPriority.Render
,甚至比绑定还要低。这意味着仅当 Dispatcher 完成所有需要更新的绑定时,才会渲染控件,而这又仅会在 Dispatcher 处理完所有项添加时才发生。
如果听起来有点奇怪,请记住,每个层次(更新-绑定-渲染)都会在下一层设置一个隐喻性的标志 - 您不会得到十次绑定和十次渲染。如果您的项添加速度很快,则会得到所有添加,然后是一次绑定和一次渲染 - 这非常完美。
基本上:如果按照 Dispatcher 的意图使用,您将无需担心。似乎将渲染作为 Dispatcher 上相对较低的优先级是错误的,但实际上这非常聪明 :-)
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 UI虚拟化。以下是一系列关于此主题的好博客文章:
http://blogs.msdn.com/dancre/archive/tags/VirtualizingTilePanel/default.aspx
通常情况下,您将项目添加到集合中,并将该集合数据绑定到一个ListBox控件上。如果正确操作ListBox,则不应刷新正在添加但不可见的项目。
for (int i = 0; i < 1000; i++) { lbQuery.Items.Add(i); }
无论如何,您能给我一个您解决方案的代码示例吗? - Lith