WPF Datagrid loaded 79M items in mvvm , Microsoft.Extensions.DependencyInjection

 

Install-Package Microsoft.Extensions.DependencyInjection;

 

 

image

 

        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";
        }

 

 

 

 

 

 

 

 

 

 

image

 

 

 

 

 

 

 

image

 

 

//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)}";
        }
    }
}

 

posted @ 2025-09-24 21:09  FredGrit  阅读(7)  评论(0)    收藏  举报