所以这是场景,我有一个操作可以清除ObservableCollection,运行查询,并使用查询结果重新填充集合。此集合通过列表框进行数据绑定。这是踢球者,500个查询结果会导致一些严重的更新时间,这会阻止用户界面被视为“太长”(实际上在大多数系统上都是.5-2秒)无论哪种方式,我都知道查询速度足够快,就像添加元素一样,所以我把它缩小到表示层。
进行一些测试,如果我从列表框中删除项目模板(duh),性能会明显提高,但不会太好,甚至不能满足传达给我的期望。我将绑定更新为适用的一种方式,我将虚拟化模式更改为回收,并确保我使用的是静态资源,以上都没有对重新绘制产生明显影响。我想知道是否有其他人知道如何提高列表框中填充大量项目的性能?
<ListBox x:Name="listBox" Grid.Row="1" Grid.ColumnSpan="5" ItemsSource="{Binding SerialResults}" ItemTemplate="{StaticResource UnitHistoryTemplate}" BorderThickness="2" Grid.IsSharedSizeScope="True" VirtualizingStackPanel.VirtualizationMode="Recycling">
<DataTemplate x:Key="UnitHistoryTemplate">
<DataTemplate.Resources>
<DataTemplate x:Key="UnitFailureItemTemplate">
<Grid>
<TextBlock Margin="4,0,4,0" TextWrapping="Wrap" Text="{Binding}" Foreground="Red"/>
</Grid>
</DataTemplate>
</DataTemplate.Resources>
<Grid d:DesignWidth="580" d:DesignHeight="30" Background="#00000000">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="load"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="run"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="ser"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="mot"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="result"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="Load" Text="{Binding Load.LoadNumber, Mode=OneTime}" Margin="4,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" d:LayoutOverrides="HorizontalAlignment"/>
<TextBlock x:Name="Run" Text="{Binding Run.RunNumber, Mode=OneTime}" Margin="4,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" d:LayoutOverrides="HorizontalAlignment" Grid.Column="1"/>
<TextBlock x:Name="Serial" Text="{Binding Unit.SerialNumber, Mode=OneTime}" Margin="4,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" d:LayoutOverrides="HorizontalAlignment" Grid.Column="2"/>
<TextBlock x:Name="Mot" Text="{Binding Unit.MotString, Mode=OneTime}" Margin="4,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" d:LayoutOverrides="HorizontalAlignment" Grid.Column="3"/>
<TextBlock x:Name="Result" Text="{Binding Run.Result, Mode=OneTime}" Margin="4,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Column="4" d:LayoutOverrides="HorizontalAlignment, GridBox"/>
<ItemsControl ItemsSource="{Binding Unit.Failed, Mode=OneTime}" ItemTemplate="{StaticResource UnitFailureItemTemplate}" HorizontalAlignment="Left" Margin="4,0,0,0" Grid.Column="5">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Run.Result, Mode=OneTime}" Value="Aborted">
<DataTrigger.Setters>
<Setter TargetName="Result" Property="Foreground" Value="Red"/>
</DataTrigger.Setters>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
由于以下几次被问过,不是我的可观察集合......我通过做一批更新并将项目源重新分配给新实例来抑制事件。
public async void FindUnitHistory()
{
if (IsCaching)
{
return;
}
else if (_serialSearch.Length <= MIN_SEARCH_LENGTH)
{
SerialResults.Clear();
return;
}
var newData = new ObservableCollection<UnitHistory>();
await TaskEx.Run(() =>
{
var results = from load in cache.LoadData.AsParallel()
from run in load.Runs
from unit in run.Units
where unit.SerialNumber.StartsWith(_serialSearch)
orderby run.RunNumber ascending
orderby load.LoadNumber descending
select new UnitHistory(load, run, unit);
foreach (var p in results)
{
newData.Add(p);
}
});
SerialResults = newData;
}
答案 0 :(得分:2)
如果你想显示(你的查询中)恰好是大量数据的结果,那么我建议你将DataPager与DataGrid / ListView / ListBox一起使用,并且只显示与可用空间一样多的项目可以容纳(不创建垂直滚动条)。为此,您需要编写一个DataPager控件,因为它没有WPF(尽管Silverlight有DataPager!)。 (我为我的项目编写了DataPager来解决类似的问题。编写自己的控件很有趣!)
但即使在此之前你也可以试试这个:( 可能工作)
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
而不是:
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
您可能还想查看这些文章:
Data virtualization
UI Virtualization
How can I filter data virtualized items in WPF?
How can I sort data virtualized items in WPF?
编辑:因为当你填充resultCollection时,每个Add()都会触发一个由WPF控件处理的集合更改事件。所以这不是一个好主意,因为如果有1000个项目要添加到您的resultCollection中,那么您的代码(或WPF控件)会处理1000个事件。这是不必要的。所以你可以像Marijn建议的那样压制这些事件。
但我建议你这样做来抑制不必要的事件:
ObservableCollection<Result> temp = resultCollection;
resultCollection = null ; // make it null so it will not fire any event anymore to be handled by wpf!
temp.clear()
foreach(/*your code*/)
{
temp.Add(item); //since temp is not bound to any control, temp's collection changed event will not be handled by anyone! Means, No Handler, No Code to Execute, No time waste!
}
resultCollection = temp ; //this fires event, which is handled by your code/ wpf.
答案 1 :(得分:1)
您可能希望重写OnCollectionChanged方法,以便在对可观察集合进行批量添加时临时禁止CollectionChanged事件。例如,请参阅this post。
答案 2 :(得分:0)
如果这是错误的,请原谅我,我对新的async / Task内容没有任何经验。
简短回答:在完成向其添加项目之前,不要将SerialResults设置为新的ObservableCollection。
答案很长:ObservableCollection每次向其添加项目时都必须提升其CollectionChanged事件。由于ObservableCollection绑定到ItemsControl,每次该事件触发时ItemsControl都需要重绘窗口,阻止其他任何事情发生。
请参阅Marijn关于为解决方法创建自己的ObservableCollection类型的帖子。