WPF Microsoft.Extensions.DependencyInjection Microsoft.Extensions.Hosting;
Install-Package CommunityToolkit.MVVM; Install-Package Microsoft.Extensions.DependencyInjection; Install-Package Microsoft.Extensions.Hosting;
//xaml
<Window x:Class="WpfApp1.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:WpfApp1" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Window.Resources> <DataTemplate x:Key="LBXDataTemplate"> <Grid Width="{Binding DataContext.GridWidth,RelativeSource={RelativeSource AncestorType=Window}}" Height="{Binding DataContext.GridHeight,RelativeSource={RelativeSource AncestorType=Window}}"> <Grid.Resources> <Style TargetType="ColumnDefinition"> <Setter Property="Width" Value="Auto"/> </Style> <Style TargetType="TextBlock"> <Setter Property="FontSize" Value="30"/> <Setter Property="FontWeight" Value="Bold"/> <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.Background> <ImageBrush ImageSource="{Binding ImgSource}"/> </Grid.Background> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="{Binding Id}"/> <TextBlock Grid.Column="1" Text="{Binding Name}"/> <TextBlock Grid.Column="2" Text="{Binding Author}"/> <TextBlock Grid.Column="3" Text="{Binding Comment}"/> <TextBlock Grid.Column="4" Text="{Binding Content}"/> <TextBlock Grid.Column="5" Text="{Binding Summary}"/> <TextBlock Grid.Column="6" Text="{Binding Title}"/> <TextBlock Grid.Column="7" Text="{Binding Topic}"/> <TextBlock Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="8" Text="{Binding ISBN}"/> </Grid> </DataTemplate> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <ListBox Grid.Row="0" ItemTemplate="{StaticResource LBXDataTemplate}" ItemsSource="{Binding BooksCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.IsContainerVirtualizable="True" VirtualizingPanel.VirtualizationMode="Recycling" ScrollViewer.IsDeferredScrollingEnabled="True"/> <StackPanel Orientation="Horizontal" Grid.Row="1" VerticalAlignment="Top" HorizontalAlignment="Left"> <TextBlock Text="{Binding StatusMsg}" HorizontalAlignment="Stretch" Foreground="Blue" FontSize="30"/> </StackPanel> </Grid> </Window>
//App.xaml.cs
using CommunityToolkit.Mvvm.DependencyInjection; using System.Configuration; using System.Data; using System.Windows; namespace WpfApp1 { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); var serviceProvider = IocConfiguration.ConfigureServices(); Ioc.Default.ConfigureServices(serviceProvider); } } }
//cs using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Collections; using System.Collections.Generic; using System.IO; using System.Text; using System.Threading.Tasks; 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 System.Collections.ObjectModel; using CommunityToolkit.Mvvm.DependencyInjection; using Microsoft.Extensions.DependencyInjection; namespace WpfApp1 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext=Ioc.Default.GetService<MainVM>(); this.SizeChanged+=MainWindow_SizeChanged; } private void MainWindow_SizeChanged(object sender, SizeChangedEventArgs e) { if(DataContext is MainVM vm && Content is FrameworkElement fe) { vm.GridWidth=fe.ActualWidth; vm.GridHeight=fe.ActualHeight/2; } } } public partial class MainVM:ObservableObject { private readonly IBookService bookService; private CancellationTokenSource cancellationTokenSource; public MainVM(IBookService bookServiceValue) { bookService = bookServiceValue; //LoadDataCommand=new AsyncRelayCommand(LoadDataCommandExecutedAsync); //CancelCommand=new RelayCommand(CancelLoading); Task task = LoadDataCommandExecutedAsync(); } private void CancelLoading() { cancellationTokenSource?.Cancel(); } private async Task LoadDataCommandExecutedAsync() { if(IsLoading) { return; } IsLoading=true; StatusMsg="Loading data..."; cancellationTokenSource=new CancellationTokenSource(); try { var progress = new Progress<string>(x => StatusMsg=x); var books = await bookService.LoadBooksAsync( 100000, progress, cancellationTokenSource.Token); BooksCollection = books; StatusMsg=$"Loaded {BooksCollection.Count} items successfully!"; } catch(OperationCanceledException ex) { StatusMsg="Loading cancelled!"; } catch (Exception ex) { StatusMsg=$"Error:{ex.Message}"; } finally { IsLoading=false; cancellationTokenSource?.Dispose(); cancellationTokenSource=null; } } [ObservableProperty] private ObservableCollection<Book> booksCollection = new ObservableCollection<Book>(); [ObservableProperty] private double gridWidth; [ObservableProperty] private double gridHeight; [ObservableProperty] private string statusMsg = "Ready to load data"; [ObservableProperty] private bool isLoading; public IAsyncRelayCommand LoadDataCommand { get; } public ICommand CancelCommand { get; } } public static class IocConfiguration { public static IServiceProvider ConfigureServices() { var services = new ServiceCollection(); services.AddTransient<MainVM>(); services.AddTransient<IBookService, BookService>(); services.AddSingleton<IImageService, ImageService>(); return services.BuildServiceProvider(); } } public class ImageService : IImageService { public ImageService() { } public ImageSource GetImgSourceViaUrl(string imgUrl) { BitmapImage bmi = new BitmapImage(); if (!File.Exists(imgUrl)) { return bmi; } bmi.BeginInit(); bmi.UriSource=new Uri(imgUrl, UriKind.RelativeOrAbsolute); bmi.CacheOption=BitmapCacheOption.OnDemand; bmi.EndInit(); if (bmi.CanFreeze) { bmi.Freeze(); } return bmi; } } public interface IImageService { ImageSource GetImgSourceViaUrl(string imgUrl); } public interface IBookService { Task<ObservableCollection<Book>> LoadBooksAsync(int count, IProgress<string> progress,CancellationToken cancelToken); } public class BookService : IBookService { private readonly IImageService imgService; public BookService(IImageService imgServiceValue) { imgService = imgServiceValue; } public async Task<ObservableCollection<Book>> LoadBooksAsync(int count, IProgress<string> progress, CancellationToken cancellationToken) { var books=new ObservableCollection<Book>(); var imgDir = @"../../../Images"; if(!Directory.Exists(imgDir)) { progress.Report("Images directly not found."); return books; } var imgs=Directory.GetFiles(imgDir); if(imgs.Length==0) { progress.Report("No images found in directory!"); return books; } int imgsCount = imgs.Count(); await Task.Run(() => { for (int i = 1; i<count+1; i++) { var book = new Book() { Id=i, Name=$"Name_{i}", Author=$"Author+{i}", Comment=$"Comment_{i}", Content=$"Content_{i}", Summary=$"Summary_{i}", Title=$"Title_{i}", Topic=$"Topic_{i}", ISBN=$"ISBN_{i}_{Guid.NewGuid():N}", ImgSource=imgService.GetImgSourceViaUrl(imgs[i%imgsCount]) }; Application.Current.Dispatcher.Invoke(() => { books.Add(book); }); if (i%10000==0) { progress.Report($"Loaded {i} items..."); } if(cancellationToken.IsCancellationRequested) { break; } } }); return books; } } public class Book { public int Id { get;set; } public string Name { get;set; } public string Author { get;set; } public string Comment { get;set; } public string Content { get; set; } public string Summary { get; set; } public string Title { get; set; } public string Topic { get; set; } public string ISBN { get; set; } public ImageSource ImgSource { get; set; } } }