/// <summary> /// of collection details. /// </summary> /// <typeparam name="T"> </typeparam> public interface IItemsProvider<T> { /// <summary> /// /// </summary> /// <returns></returns> int FetchCount(); /// <summary> /// /// </summary> /// <param name="startIndex"> </param> /// <param name="count"> </param> /// <returns></returns> IList<T> FetchRange(int startIndex, int count); }
Private int _count = -1; public virtual int Count { get { if (_count == -1) { LoadCount(); } return _count; } protected set { _count = value; } } protected virtual void LoadCount() { Count = FetchCount(); } protected int FetchCount() { return ItemsProvider.FetchCount(); }
public T this[int index] { get { // int pageIndex = index / PageSize; int pageOffset = index % PageSize; // RequestPage(pageIndex); // 50% if ( pageOffset > PageSize/2 && pageIndex < Count / PageSize) RequestPage(pageIndex + 1); // 50% if (pageOffset < PageSize/2 && pageIndex > 0) RequestPage(pageIndex - 1); // CleanUpPages(); // if (_pages[pageIndex] == null) return default(T); // return _pages[pageIndex][pageOffset]; } set { throw new NotSupportedException(); } }
private readonly Dictionary<int, IList<T>> _pages = new Dictionary<int, IList<T>>(); private readonly Dictionary<int, DateTime> _pageTouchTimes = new Dictionary<int, DateTime>(); protected virtual void RequestPage(int pageIndex) { if (!_pages.ContainsKey(pageIndex)) { _pages.Add(pageIndex, null); _pageTouchTimes.Add(pageIndex, DateTime.Now); LoadPage(pageIndex); } else { _pageTouchTimes[pageIndex] = DateTime.Now; } } protected virtual void PopulatePage(int pageIndex, IList<T> page) { if (_pages.ContainsKey(pageIndex)) _pages[pageIndex] = page; } public void CleanUpPages() { List<int> keys = new List<int>(_pageTouchTimes.Keys); foreach (int key in keys) { // page 0 is a special case, since the WPF ItemsControl // accesses the first item frequently if ( key != 0 && (DateTime.Now - _pageTouchTimes[key]).TotalMilliseconds > PageTimeout ) { _pages.Remove(key); _pageTouchTimes.Remove(key); } } }
protected virtual void LoadPage(int pageIndex) { PopulatePage(pageIndex, FetchPage(pageIndex)); } protected IList<T> FetchPage(int pageIndex) { return ItemsProvider.FetchRange(pageIndex*PageSize, PageSize); }
public event NotifyCollectionChangedEventHandler CollectionChanged; protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { NotifyCollectionChangedEventHandler h = CollectionChanged; if (h != null) h(this, e); } private void FireCollectionReset() { NotifyCollectionChangedEventArgs e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); OnCollectionChanged(e); } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) { PropertyChangedEventHandler h = PropertyChanged; if (h != null) h(this, e); } private void FirePropertyChanged(string propertyName) { PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName); OnPropertyChanged(e); }
protected override void LoadCount() { Count = 0; IsLoading = true; ThreadPool.QueueUserWorkItem(LoadCountWork); } private void LoadCountWork(object args) { int count = FetchCount(); SynchronizationContext.Send(LoadCountCompleted, count); } private void LoadCountCompleted(object args) { Count = (int)args; IsLoading = false; FireCollectionReset(); }
protected override void LoadPage(int index) { IsLoading = true; ThreadPool.QueueUserWorkItem(LoadPageWork, index); } private void LoadPageWork(object args) { int pageIndex = (int)args; IList<T> page = FetchPage(pageIndex); SynchronizationContext.Send(LoadPageCompleted, new object[]{ pageIndex, page }); } private void LoadPageCompleted(object args) { int pageIndex = (int)((object[]) args)[0]; IList<T> page = (IList<T>)((object[])args)[1]; PopulatePage(pageIndex, page); IsLoading = false; FireCollectionReset(); }
public bool IsLoading { get { return _isLoading; } set { if ( value != _isLoading ) { _isLoading = value; FirePropertyChanged("IsLoading"); } } }
public class DemoCustomerProvider : IItemsProvider<Customer> { private readonly int _count; private readonly int _fetchDelay; public DemoCustomerProvider(int count, int fetchDelay) { _count = count; _fetchDelay = fetchDelay; } public int FetchCount() { Thread.Sleep(_fetchDelay); return _count; } public IList<Customer> FetchRange(int startIndex, int count) { Thread.Sleep(_fetchDelay); List<Customer> list = new List<Customer>(); for( int i=startIndex; i<startIndex+count; i++ ) { Customer customer = new Customer {Id = i+1, Name = "Customer " + (i+1)}; list.Add(customer); } return list; } }
<Window x:Class="DataVirtualization.DemoWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Data Virtualization Demo - By Paul McClean" Height="600" Width="600"> <Window.Resources> <Style x:Key="lvStyle" TargetType="{x:Type ListView}"> <Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"/> <Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling"/> <Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="True"/> <Setter Property="ListView.ItemsSource" Value="{Binding}"/> <Setter Property="ListView.View"> <Setter.Value> <GridView> <GridViewColumn Header="Id" Width="100"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding Id}"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <GridViewColumn Header="Name" Width="150"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding Name}"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> </GridView> </Setter.Value> </Setter> <Style.Triggers> <DataTrigger Binding="{Binding IsLoading}" Value="True"> <Setter Property="ListView.Cursor" Value="Wait"/> <Setter Property="ListView.Background" Value="LightGray"/> </DataTrigger> </Style.Triggers> </Style> </Window.Resources> <Grid Margin="5"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <GroupBox Grid.Row="0" Header="ItemsProvider"> <StackPanel Orientation="Horizontal" Margin="0,2,0,0"> <TextBlock Text="Number of items:" Margin="5" TextAlignment="Right" VerticalAlignment="Center"/> <TextBox x:Name="tbNumItems" Margin="5" Text="1000000" Width="60" VerticalAlignment="Center"/> <TextBlock Text="Fetch Delay (ms):" Margin="5" TextAlignment="Right" VerticalAlignment="Center"/> <TextBox x:Name="tbFetchDelay" Margin="5" Text="1000" Width="60" VerticalAlignment="Center"/> </StackPanel> </GroupBox> <GroupBox Grid.Row="1" Header="Collection"> <StackPanel> <StackPanel Orientation="Horizontal" Margin="0,2,0,0"> <TextBlock Text="Type:" Margin="5" TextAlignment="Right" VerticalAlignment="Center"/> <RadioButton x:Name="rbNormal" GroupName="rbGroup" Margin="5" Content="List(T)" VerticalAlignment="Center"/> <RadioButton x:Name="rbVirtualizing" GroupName="rbGroup" Margin="5" Content="VirtualizingList(T)" VerticalAlignment="Center"/> <RadioButton x:Name="rbAsync" GroupName="rbGroup" Margin="5" Content="AsyncVirtualizingList(T)" IsChecked="True" VerticalAlignment="Center"/> </StackPanel> <StackPanel Orientation="Horizontal" Margin="0,2,0,0"> <TextBlock Text="Page size:" Margin="5" TextAlignment="Right" VerticalAlignment="Center"/> <TextBox x:Name="tbPageSize" Margin="5" Text="100" Width="60" VerticalAlignment="Center"/> <TextBlock Text="Page timeout (s):" Margin="5" TextAlignment="Right" VerticalAlignment="Center"/> <TextBox x:Name="tbPageTimeout" Margin="5" Text="30" Width="60" VerticalAlignment="Center"/> </StackPanel> </StackPanel> </GroupBox> <StackPanel Orientation="Horizontal" Grid.Row="2"> <TextBlock Text="Memory Usage:" Margin="5" VerticalAlignment="Center"/> <TextBlock x:Name="tbMemory" Margin="5" Width="80" VerticalAlignment="Center"/> <Button Content="Refresh" Click="Button_Click" Margin="5" Width="100" VerticalAlignment="Center"/> <Rectangle Name="rectangle" Width="20" Height="20" Fill="Blue" Margin="5" VerticalAlignment="Center"> <Rectangle.RenderTransform> <RotateTransform Angle="0" CenterX="10" CenterY="10"/> </Rectangle.RenderTransform> <Rectangle.Triggers> <EventTrigger RoutedEvent="Rectangle.Loaded"> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetName="rectangle" Storyboard.TargetProperty= "(TextBlock.RenderTransform).(RotateTransform.Angle)" From="0" To="360" Duration="0:0:5" RepeatBehavior="Forever" /> </Storyboard> </BeginStoryboard> </EventTrigger> </Rectangle.Triggers> </Rectangle> <TextBlock Margin="5" VerticalAlignment="Center" FontStyle="Italic" Text="Pause in animation indicates UI thread stalled."/> </StackPanel> <ListView Grid.Row="3" Margin="5" Style="{DynamicResource lvStyle}"/> </Grid> </Window>
public partial class DemoWindow { /// <summary> /// Initializes a new instance of the <see cref="DemoWindow"/> class. /// </summary> public DemoWindow() { InitializeComponent(); // use a timer to periodically update the memory usage DispatcherTimer timer = new DispatcherTimer(); timer.Interval = new TimeSpan(0, 0, 1); timer.Tick += timer_Tick; timer.Start(); } private void timer_Tick(object sender, EventArgs e) { tbMemory.Text = string.Format("{0:0.00} MB", GC.GetTotalMemory(true)/1024.0/1024.0); } private void Button_Click(object sender, RoutedEventArgs e) { // create the demo items provider according to specified parameters int numItems = int.Parse(tbNumItems.Text); int fetchDelay = int.Parse(tbFetchDelay.Text); DemoCustomerProvider customerProvider = new DemoCustomerProvider(numItems, fetchDelay); // create the collection according to specified parameters int pageSize = int.Parse(tbPageSize.Text); int pageTimeout = int.Parse(tbPageTimeout.Text); if ( rbNormal.IsChecked.Value ) { DataContext = new List<Customer>(customerProvider.FetchRange(0, customerProvider.FetchCount())); } else if ( rbVirtualizing.IsChecked.Value ) { DataContext = new VirtualizingCollection<Customer>(customerProvider, pageSize); } else if ( rbAsync.IsChecked.Value ) { DataContext = new AsyncVirtualizingCollection<Customer>(customerProvider, pageSize, pageTimeout*1000); } } }
Source: https://habr.com/ru/post/208792/
All Articles