private Window? GetWindow() {foreach (Window win in Application.Current.Windows){if (win.DataContext==this){return win;}}return null; }
Install-Package CommunityToolkit.mvvm;
Install-Package Micorosoft.Extensions.DependencyInjection;
//app.xaml <Application x:Class="WpfApp28.App"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:WpfApp28"><Application.Resources></Application.Resources> </Application>//app.xaml.cs using Microsoft.Extensions.DependencyInjection; using System.Configuration; using System.Data; using System.Windows;namespace WpfApp28 {/// <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 static void ConfigureServices(ServiceCollection services){services.AddSingleton<IIDService, IDService>();services.AddSingleton<INameService, NameService>();services.AddSingleton<IISBNService, ISBNService>();services.AddSingleton<MainVM>();services.AddSingleton<MainWindow>();}protected override void OnExit(ExitEventArgs e){base.OnExit(e);serviceProvider?.Dispose();}}}//mainwindow.xaml <Window x:Class="WpfApp28.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"xmlns:local="clr-namespace:WpfApp28"WindowState="Maximized"mc:Ignorable="d"Title="{Binding StatusMsg,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Height="450" Width="800"><Grid><ListBoxItemsSource="{Binding BooksCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"VirtualizingPanel.IsVirtualizing="True"VirtualizingPanel.VirtualizationMode="Recycling"ScrollViewer.IsDeferredScrollingEnabled="True"><ListBox.ItemTemplate><DataTemplate><Grid Width="{Binding DataContext.GridWidth,RelativeSource={RelativeSource AncestorType=Window}}"Height="{Binding DataContext.GridHeight,RelativeSource={RelativeSource AncestorType=Window}}"><Grid.Background><ImageBrush ImageSource="{Binding ImgSource}"/></Grid.Background><Grid.Resources><Style TargetType="TextBlock"><Setter Property="FontSize" Value="30"/><Setter Property="HorizontalAlignment" Value="Center"/><Setter Property="VerticalAlignment" Value="Center"/><Style.Triggers><Trigger Property="IsMouseOver" Value="True"><Setter Property="FontSize" Value="50"/><Setter Property="Foreground" Value="Red"/></Trigger></Style.Triggers></Style></Grid.Resources><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition/><RowDefinition/></Grid.RowDefinitions><TextBlock Text="{Binding Id}" Grid.Column="0" Grid.Row="0"/><TextBlock Text="{Binding Name}" Grid.Column="1" Grid.Row="0"/><TextBlock Text="{Binding ISBN}" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1"/></Grid></DataTemplate></ListBox.ItemTemplate></ListBox> </Grid> </Window>//window.xaml.cs using CommunityToolkit.Mvvm.ComponentModel; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; 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;namespace WpfApp28 {/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow : Window{public MainWindow(){InitializeComponent();}public MainWindow(MainVM vm){InitializeComponent();this.DataContext=vm;this.SizeChanged+=MainWindow_SizeChanged;this.Loaded+=async (s, e) =>{await LoadDataAsync();};}private async Task LoadDataAsync(){if (DataContext is MainVM vm){await vm.InitDataAsync();}}private void MainWindow_SizeChanged(object sender, SizeChangedEventArgs e){if (DataContext is MainVM vm){var fe = this.Content as FrameworkElement;if (fe==null){return;}vm.GridWidth=fe.ActualWidth;vm.GridHeight=fe.ActualHeight/2;}}}public partial class MainVM : ObservableObject{IIDService idService;INameService nameService;IISBNService isbnService;private Stopwatch watch;public MainVM(IIDService idServiceValue,INameService nameServiceValue,IISBNService isbnServiceValue){idService=idServiceValue;nameService=nameServiceValue;isbnService=isbnServiceValue;watch=Stopwatch.StartNew();}private void InitVisualTreesList(){VisualChildrenCollection=new ObservableCollection<string>();Window mainWin = GetWindow();if (mainWin!=null){var visualsList = GetVisualChildren<Visual>(mainWin);foreach (var visual in visualsList){VisualChildrenCollection.Add(visual.GetType().Name);}}string visualChildrenStr = string.Join("\n", VisualChildrenCollection.ToArray());Debug.WriteLine($"Visual Tree children:\n{visualChildrenStr}\n\n\n");}public async Task InitDataAsync(){string imgDir = @"../../../Images";if (!Directory.Exists(imgDir)){return;}var imgs = Directory.GetFiles(imgDir);if (imgs==null ||!imgs.Any()){return;}int imgsCount = imgs.Count();BooksCollection=new ObservableCollection<Book>();List<Book> booksList = new List<Book>();await Task.Run(async () =>{for (int i = 0; i<1000001; i++){booksList.Add(new Book(){Id=idService.GetID(),Name=nameService.GetName(),ISBN=isbnService.GetISBN(),ImgSource=GetImgSourceViaUrl(imgs[i%imgsCount])});if (i<1000&& i%100==0){await PopulateBooksCollectionAsync(booksList);}else if (i>=1000 && i%1000000==0){await PopulateBooksCollectionAsync(booksList);}}if (booksList.Any()){await PopulateBooksCollectionAsync(booksList);}});InitVisualTreesList();InitLogicalTreesList();}private void InitLogicalTreesList(){Window mainWin = GetWindow();var logicalChildren = GetAllLogicalChildren(mainWin);string logicalTreesStr = string.Join("\n", logicalChildren.Select(x => x.GetType().Name));Debug.WriteLine(logicalTreesStr);}private ImageSource GetImgSourceViaUrl(string imgUrl){BitmapImage bmi = new BitmapImage();bmi.BeginInit();bmi.UriSource=new Uri(imgUrl, UriKind.RelativeOrAbsolute);bmi.CacheOption=BitmapCacheOption.OnDemand;bmi.EndInit();bmi.Freeze();return bmi;}private async Task PopulateBooksCollectionAsync(List<Book> booksList){var tempList = booksList.ToList();booksList.Clear();await Application.Current.Dispatcher.InvokeAsync(() =>{foreach (var bk in tempList){BooksCollection.Add(bk);}StatusMsg=$"Loaded {BooksCollection.Count} items,{GetMemory()},{GetTimeCost()}";}, System.Windows.Threading.DispatcherPriority.Background);}private string GetMemory(){var procMemory = Process.GetCurrentProcess().PrivateMemorySize64/1024.0d/1024.0d;return $"Memory:{procMemory.ToString("#,##0.00")} M";}private string GetTimeCost(){return $"Time cost: {watch.Elapsed.TotalSeconds} seconds";}private static IEnumerable<T> GetVisualChildren<T>(DependencyObject parent) where T : DependencyObject{if (parent == null){yield break;}int childrenCount = VisualTreeHelper.GetChildrenCount(parent);for (int i = 0; i<childrenCount; i++){DependencyObject child = VisualTreeHelper.GetChild(parent, i);if (child is T tChild){yield return tChild;}foreach (var descedant in GetVisualChildren<T>(child)){yield return descedant;}}}public static IEnumerable<DependencyObject> GetAllLogicalChildren(DependencyObject parent){if (parent == null){yield break;}foreach (var child in LogicalTreeHelper.GetChildren(parent)){if (child is DependencyObject depChild){yield return depChild;// Recurse down into this child's childrenforeach (var descendant in GetAllLogicalChildren(depChild)){yield return descendant;}}}}private Window? GetWindow(){foreach (Window win in Application.Current.Windows){if (win.DataContext==this){return win;}}return null;}[ObservableProperty]private double gridWidth;[ObservableProperty]private double gridHeight;[ObservableProperty]private ObservableCollection<Book> booksCollection;[ObservableProperty]private string statusMsg;[ObservableProperty]private ObservableCollection<string> visualChildrenCollection;}public class Book{public int Id { get; set; }public string Name { get; set; }public string ISBN { get; set; }public ImageSource ImgSource { get; set; }}public interface IIDService{int GetID();}public class IDService : IIDService{int id = 0;public int GetID(){return Interlocked.Increment(ref id);}}public interface INameService{string GetName();}public class NameService : INameService{int idx = 0;public string GetName(){return $"Name_{Interlocked.Increment(ref idx)}";}}public interface IISBNService{string GetISBN();}public class ISBNService : IISBNService{int idx = 0;public string GetISBN(){return $"ISBN_{Interlocked.Increment(ref idx)}_{Guid.NewGuid():N}";}} }