如何正确改善ItemsControl的加载并避免冻结 [英] How to properly improve ItemsControl loading and avoid freezing
问题描述
我想知道什么是改善我的ItemsControl加载的正确方法.我想显示非常重的项目(由于复杂的XAML),并且我不希望WPF应用程序冻结.我希望我的项目在渲染后能够显示出来,并能够与窗口中的其他元素同时进行交互(即使速度稍慢一些).从这里开始的一些假设是:
I'm wondering what it's the right way to improve my ItemsControl loading. I want to show items that are pretty heavy to load (due to complexe XAML) and I don't want my WPF application to freeze. I want my items to show up once rendered and to be able to interact with the other elements of my window at the same time (even if it's a bit slower). Here is some assumptions to begin with:
- 假设我无法缩短商品的加载时间;
- 我无法使用虚拟化,因为我需要一次查看所有项目;
- 视图不等待后台操作完成,所有工作都在UI线程上完成.
我已经找到了解决方法,但是我不满意,使用BackgroundWorker
和睡眠:
I've been able find a workaround but I'm not happy with it, using a BackgroundWorker
and a sleep:
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"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:local="clr-namespace:WpfApplication1">
<DockPanel>
<Button DockPanel.Dock="Bottom" Content="Click me" Click="OnButtonClick"/>
<ItemsControl ItemsSource="{Binding Items}">
<!-- Items begin to be loaded once the ItemsControl is loaded -->
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Width="25" Height="25" Background="Red" Margin="5">
<local:HeavyItem Content="{Binding}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DockPanel>
</Window>
ViewModel.cs
public class ViewModel
{
public ICommand LoadCommand { get; protected set; }
public ObservableCollection<int> Items { get; protected set; }
public ViewModel()
{
Items = new ObservableCollection<int>();
LoadCommand = new DelegateCommand(AsyncLoad);
//Load();
}
protected void Load()
{
for (int i = 0; i < 250; i++)
{
Items.Add(i);
}
}
protected void AsyncLoad()
{
var bk = new BackgroundWorker();
bk.DoWork += (s, e) =>
{
for (int i = 0; i < 250; i++)
{
// Sleep 50ms to let the UI thread breeze
Thread.Sleep(50);
Application.Current.Dispatcher.Invoke(DispatcherPriority.Render, (Action)(() =>
{
Items.Add(i);
}));
}
};
bk.RunWorkerAsync();
}
}
HeavyItem.cs(模拟沉重视觉效果的伪控件)
public class HeavyItem : ContentControl
{
protected override Size ArrangeOverride(Size finalSize)
{
Thread.Sleep(20);
return base.ArrangeOverride(finalSize);
}
}
此问题提供了类似的方法.我不喜欢这种方法,因为:
This question offers a similar approach. I don't like this method because:
- ViewModel正在执行视图应执行的操作;
- 我使用任意计时器,而根据计算机的不同,渲染项目可能需要花费更多或更少的时间.
我认为最好的方法是重写ItemsControl,并告诉UI线程在添加项目后进行更新,但是我没有做到这一点.有任何线索或想法吗?
I think that the best way to do this is to override ItemsControl and tells the UI thread to update after adding an item, but I didn't manage to do it. Any clue or ideas?
推荐答案
使用CollectionView.DeferRefresh()进行一次更新?
use CollectionView.DeferRefresh() to get one update ?
https://blogs.msdn.microsoft.com/matt/2008/08/28/collectionview-deferrefresh-my-new-best-friend/
这篇关于如何正确改善ItemsControl的加载并避免冻结的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!