如何正确提高ItemsControl加载并避免冻结

Max*_*Max 5 c# wpf animation xaml itemscontrol

我想知道改善 ItemsControl 加载的正确方法是什么。我想要显示加载量相当大的项目(由于复杂的 XAML),并且我不希望我的 WPF 应用程序冻结。我希望我的项目在渲染后显示出来,并且能够同时与窗口的其他元素进行交互(即使速度有点慢)。首先是一些假设:

  1. 假设我无法缩短物品的加载时间;
  2. 我无法使用虚拟化,因为我需要立即查看所有项目;
  3. 视图不等待后台操作完成,所有工作都在 UI 线程上完成。

我已经找到了解决方法,但我对此不满意,使用 aBackgroundWorker和 a sleep:

在此输入图像描述

主窗口.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>
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

HeavyItem.cs(模拟沉重视觉效果的假控件)

public class HeavyItem : ContentControl
{
    protected override Size ArrangeOverride(Size finalSize)
    {
        Thread.Sleep(20);
        return base.ArrangeOverride(finalSize);
    }
}
Run Code Online (Sandbox Code Playgroud)

这个问题提供了类似的方法。我不喜欢这种方法,因为:

  1. ViewModel 正在做视图应该做的事情;
  2. 我使用任意计时器,而该项目可能需要更多或更少的时间来渲染,具体取决于计算机。

我认为最好的方法是重写 ItemsControl 并告诉 UI 线程在添加项目后进行更新,但我没有设法做到这一点。有什么线索或想法吗?