WPF Datagrid loaded 79M items in mvvm , Microsoft.Extensions.DependencyInjection
Install-Package Microsoft.Extensions.DependencyInjection;
public async Task InitBooksCollection() { stopwatch.Start(); BooksCollection = new ObservableCollection<Book>(); List<Book> booksList = new List<Book>(); await Task.Run(async () => { for (int i = 1; i < 100000001; i++) { booksList.Add(new Book() { Id = idService.GetID(), Name = nameService.GetName(), ISBN = isbnService.GetISBN(), Author = $"Author_{i}", Title = $"Title_{i}", Topic = $"Topic_{i}" }); if (i < 1001 && i % 100 == 0) { var tempList = booksList.ToList(); booksList.Clear(); await PopulateBooksCollection(tempList); } else if (i > 1000 && i % 1000000 == 0) { var tempList = booksList.ToList(); booksList.Clear(); await PopulateBooksCollection(tempList); } } if (booksList.Any()) { var tempList = booksList.ToList(); booksList.Clear(); await PopulateBooksCollection(tempList); } }); } private async Task PopulateBooksCollection(List<Book> booksList) { await Application.Current.Dispatcher.InvokeAsync(() => { foreach (var bk in booksList) { BooksCollection.Add(bk); } StatusMsg = $"MainWindow,now is {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}," + $"loaded {BooksCollection.Count} items,{GetMemory()},{GetTimeCost()}"; }, System.Windows.Threading.DispatcherPriority.Background); System.Diagnostics.Debug.WriteLine(StatusMsg); } private string GetMemory() { double memory = Process.GetCurrentProcess().PrivateMemorySize64; return $"Memory:{memory.ToString("#,##0.00")}"; } private string GetTimeCost() { return $"Time cost:{stopwatch.Elapsed.TotalSeconds.ToString("#,##0.00")} seconds"; }
//App.xaml <Application x:Class="WpfApp5.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApp5"> <Application.Resources> </Application.Resources> </Application> //App.xaml.cs using Microsoft.Extensions.DependencyInjection; using System.Configuration; using System.Data; using System.Windows; using WpfApp5.Services; namespace WpfApp5 { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { ServiceProvider serviceProvider; protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); var services = new ServiceCollection(); ConfigureServices(services); serviceProvider = services.BuildServiceProvider(); var mainWin = serviceProvider.GetRequiredService<MainWindow>(); mainWin?.Show(); } private void ConfigureServices(ServiceCollection services) { services.AddSingleton<IISBNService, ISBNService>(); services.AddSingleton<INameService, NameService>(); services.AddSingleton<IIDService, IDService>(); services.AddSingleton<MainWindow>(); services.AddSingleton<MainVM>(); } } } //MainWindow.xaml <Window x:Class="WpfApp5.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" WindowState="Maximized" xmlns:local="clr-namespace:WpfApp5" mc:Ignorable="d" Title="{Binding StatusMsg}" Height="450" Width="800"> <Grid> <DataGrid ItemsSource="{Binding BooksCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling" VirtualizingPanel.ScrollUnit="Pixel" VirtualizingPanel.CacheLengthUnit="Pixel" VirtualizingPanel.CacheLength="1,2" ScrollViewer.IsDeferredScrollingEnabled="True" ScrollViewer.CanContentScroll="True" EnableColumnVirtualization="True" EnableRowVirtualization="True" AutoGenerateColumns="False"> <DataGrid.Resources> <Style TargetType="DataGridCell"> <Setter Property="FontSize" Value="30"/> <Setter Property="Width" Value="Auto"/> <Style.Triggers> <Trigger Property="FontSize" Value="50"/> <Trigger Property="Foreground" Value="Red"/> </Style.Triggers> </Style> </DataGrid.Resources> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding Id}"/> <DataGridTextColumn Binding="{Binding Name}"/> <DataGridTextColumn Binding="{Binding Author}"/> <DataGridTextColumn Binding="{Binding Title}"/> <DataGridTextColumn Binding="{Binding Topic}"/> <DataGridTextColumn Binding="{Binding ISBN}"/> <!--<DataGridTemplateColumn> <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Grid> <Grid.Resources> <Style TargetType="TextBlock"> <Setter Property="FontSize" Value="30"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="FontSize" Value="50"/> <Setter Property="Foreground" Value="Red"/> </Trigger> </Style.Triggers> </Style> <Style TargetType="ColumnDefinition"> <Setter Property="Width" Value="Auto"/> </Style> </Grid.Resources> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <TextBlock Text="{Binding Id}" Grid.Column="0"/> <TextBlock Text="{Binding Name}" Grid.Column="1"/> <TextBlock Text="{Binding Author}" Grid.Column="2"/> <TextBlock Text="{Binding Title}" Grid.Column="3"/> <TextBlock Text="{Binding Topic}" Grid.Column="4"/> <TextBlock Text="{Binding ISBN}" Grid.Column="5"/> </Grid> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn>--> </DataGrid.Columns> </DataGrid> </Grid> </Window> //MainWindow.xaml.cs using CommunityToolkit.Mvvm.ComponentModel; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using WpfApp5.Services; namespace WpfApp5 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } public MainWindow(MainVM vm) { InitializeComponent(); RenderOptions.ProcessRenderMode=System.Windows.Interop.RenderMode.Default; this.DataContext = vm; Loaded += async (s, e) => { await vm.InitBooksCollection(); }; } } public class MainVM : INotifyPropertyChanged { private IIDService idService; private INameService nameService; private IISBNService isbnService; private Stopwatch stopwatch; public MainVM(IIDService idServiceValue, INameService nameServiceValue, IISBNService isbnServiceValue) { stopwatch = new Stopwatch(); StatusMsg = $"MainWindow,now is {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")},{GetMemory()},{GetTimeCost()}"; idService = idServiceValue; nameService = nameServiceValue; isbnService = isbnServiceValue; } public async Task InitBooksCollection() { stopwatch.Start(); BooksCollection = new ObservableCollection<Book>(); List<Book> booksList = new List<Book>(); await Task.Run(async () => { for (int i = 1; i < 100000001; i++) { booksList.Add(new Book() { Id = idService.GetID(), Name = nameService.GetName(), ISBN = isbnService.GetISBN(), Author = $"Author_{i}", Title = $"Title_{i}", Topic = $"Topic_{i}" }); if (i < 1001 && i % 100 == 0) { var tempList = booksList.ToList(); booksList.Clear(); await PopulateBooksCollection(tempList); } else if (i > 1000 && i % 1000000 == 0) { var tempList = booksList.ToList(); booksList.Clear(); await PopulateBooksCollection(tempList); } } if (booksList.Any()) { var tempList = booksList.ToList(); booksList.Clear(); await PopulateBooksCollection(tempList); } }); } private async Task PopulateBooksCollection(List<Book> booksList) { await Application.Current.Dispatcher.InvokeAsync(() => { foreach (var bk in booksList) { BooksCollection.Add(bk); } StatusMsg = $"MainWindow,now is {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}," + $"loaded {BooksCollection.Count} items,{GetMemory()},{GetTimeCost()}"; }, System.Windows.Threading.DispatcherPriority.Background); System.Diagnostics.Debug.WriteLine(StatusMsg); } private string GetMemory() { double memory = Process.GetCurrentProcess().PrivateMemorySize64; return $"Memory:{memory.ToString("#,##0.00")}"; } private string GetTimeCost() { return $"Time cost:{stopwatch.Elapsed.TotalSeconds.ToString("#,##0.00")} seconds"; } public event PropertyChangedEventHandler? PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propertyName = "") { var handler = PropertyChanged; if (handler != null) { handler?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } private ObservableCollection<Book> booksCollection; public ObservableCollection<Book> BooksCollection { get { return booksCollection; } set { if (value != booksCollection) { booksCollection = value; OnPropertyChanged(); } } } private string statusMsg; public string StatusMsg { get { return statusMsg; } set { if (value != statusMsg) { statusMsg = value; OnPropertyChanged(); } } } } public class DelCommand : ICommand { private Action<object?> execute; private Predicate<object?> canExecute; public DelCommand(Action<object?> executeValue, Predicate<object?> canExecuteValue) { execute = executeValue; canExecute = canExecuteValue; } public event EventHandler? CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public bool CanExecute(object? parameter) { return canExecute == null ? true : canExecute(parameter); } public void Execute(object? parameter) { execute(parameter); } } public class Book { public int Id { get; set; } public string Name { get; set; } public string Author { get; set; } public string Title { get; set; } public string Topic { get; set; } public string ISBN { get; set; } } } //IDService.cs using System; using System.Collections.Generic; using System.Text; namespace WpfApp5.Services { public interface IIDService { int GetID(); } public class IDService : IIDService { int id = 0; public int GetID() { return Interlocked.Increment(ref id); } } } //ISBNService.cs using System; using System.Collections.Generic; using System.Text; namespace WpfApp5.Services { public interface IISBNService { string GetISBN(); } public class ISBNService : IISBNService { int idx = 0; public string GetISBN() { return $"ISBN_{Interlocked.Increment(ref idx)}_{Guid.NewGuid():N}"; } } } //NameService.cs using System; using System.Collections.Generic; using System.Text; namespace WpfApp5.Services { public interface INameService { string GetName(); } public class NameService : INameService { int idx = 0; public string GetName() { return $"Name_{Interlocked.Increment(ref idx)}"; } } }