WPF viewmodel retrieve matched view /window
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> <ListBox ItemsSource="{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 children foreach (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}"; } } }