使用WPF ItemsControl Virtualization仅为可见项加载数据

时间:2017-07-24 19:08:37

标签: c# wpf virtualization

我们有像这样的ItemsControl设置

<ScrollViewer>
    <ItemsControl ItemsSource="{Binding Items}" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" >
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Data}"></TextBlock>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</ScrollViewer>

使用MainViewModel:

public class MainViewModel : ViewModelBase
{
    public ObservableCollection<AutoUpdatingItem> Items
    {
        get;
        set;
    }

    public MainViewModel()
    {
        Items = new ObservableCollection<AutoUpdatingItem>();
        for (int i = 0; i < 1000; i++)
        {
            Items.Add(new AutoUpdatingItem(i));
        }
    }
}

和行的ViewModel

public class AutoUpdatingItem : ViewModelBase
{
    private readonly DispatcherTimer timer;

    private int data;
    public int Data
    {
        get { return data; }
        set { Set(ref data, value); }
    }

    public AutoUpdatingItem(int i)
    {
        Data = i;
        timer = new DispatcherTimer();
        timer.Interval = TimeSpan.FromMilliseconds(20);
        timer.Start();
        timer.Tick += Timer_Tick;
    }

    private void Timer_Tick(object sender, EventArgs e)
    {
        // Data would be fetched from WebService here
        Data++;
    }
}

我们如何才能实现只有可见项目的更新?有什么方法可以在回收虚拟化面板中的项目时收到通知吗?

1 个答案:

答案 0 :(得分:0)

这可能有用(参见:In WPF, how can I determine whether a control is visible to the user?):

public class AutoUpdatingItem : INotifyPropertyChanged
{
    private readonly DispatcherTimer timer;

    private int data;
    public int Data
    {
        get { return data; }
        set 
        { 
            data = value; 
            propertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Data))); 
        }
    }

    public AutoUpdatingItem()
    {
        timer = new DispatcherTimer(new TimeSpan(0,0,0,0,200), DispatcherPriority.Background, Tick, Dispatcher.CurrentDispatcher);
        timer.Stop();
    }

    private void Tick(object sender, EventArgs e)
    {
        Data++;
    }

    private event PropertyChangedEventHandler propertyChanged;
    public event PropertyChangedEventHandler PropertyChanged
    {
        add
        {
            propertyChanged += value;
            timer.Start();
        }
        remove
        {
            timer.Stop();
            propertyChanged -= value;
        }
    }
}

使用以下XAML:

 <ItemsControl ItemsSource="{Binding Items}"
                  VirtualizingStackPanel.IsVirtualizing="True"
                  VirtualizingStackPanel.VirtualizationMode="Recycling" Height="500">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Data}"  />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.Template>
            <ControlTemplate>
                <Border >
                    <ScrollViewer VerticalScrollBarVisibility="Visible"
                                  CanContentScroll="True">
                        <ItemsPresenter />
                    </ScrollViewer>
                </Border>
            </ControlTemplate>
        </ItemsControl.Template>
    </ItemsControl>