private void InitCommands()
{
    ExportAsPicturesCommand=new DelCommand(async (obj) => await ExportAsPicturesCommandExecuted(obj));
}
private async Task ExportAsPicturesCommandExecuted(object? obj)
{
    var dg = obj as DataGrid;
    if (dg!=null)
    {
        dg.UpdateLayout();
        string dir = $"JPG_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}";
        if (!Directory.Exists(dir))
        {
            Directory.CreateDirectory(dir);
        }
        try
        {
            var scrollViewer = FindVisualChild<ScrollViewer>(dg);
            if (scrollViewer==null)
            {
                return;
            }
            int idx = 0;
            for (int i = 0; i<dg.Items.Count; i++)
            {
                //Scroll to make the row visible
                dg.ScrollIntoView(dg.Items[i]);
                dg.UpdateLayout();
                var row = dg.ItemContainerGenerator.ContainerFromIndex(i) as DataGridRow;
                if (row==null)
                {
                    continue;
                }
                //Ensure row is fully rendered
                row.UpdateLayout();
                row.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                row.Arrange(new Rect(row.DesiredSize));
                string jpgFileName = Path.Combine(dir, $"{++idx}.jpg");
                SaveFrameworkElementAsJpg(row, jpgFileName);
                await Task.Delay(1);
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex?.StackTrace.ToString());
        }
        finally
        {
        }
    }
}
private void SaveFrameworkElementAsJpg(FrameworkElement fe, string fileName)
{
    RenderTargetBitmap rtb = new RenderTargetBitmap(
        (int)(fe.ActualWidth*dpiFactor),
        (int)(fe.ActualHeight*dpiFactor),
        96*dpiFactor,
        96*dpiFactor,
        PixelFormats.Pbgra32);
    DrawingVisual drawingVisual = new DrawingVisual();
    using (DrawingContext drawingContext = drawingVisual.RenderOpen())
    {
        VisualBrush visualBrush = new VisualBrush(fe);
        drawingContext.DrawRectangle(visualBrush, null,
            new Rect(0, 0, fe.ActualWidth, fe.ActualHeight));
    }
    rtb.Render(drawingVisual);
    using (FileStream fileStream = new FileStream(fileName, FileMode.Create))
    {
        JpegBitmapEncoder jpgBmpEncoder = new JpegBitmapEncoder();
        jpgBmpEncoder.Frames.Add(BitmapFrame.Create(rtb));
        jpgBmpEncoder.Save(fileStream);
        System.Diagnostics.Debug.WriteLine(fileName);
    }
}
private T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
{
    for (int i = 0; i<VisualTreeHelper.GetChildrenCount(parent); i++)
    {
        var child = VisualTreeHelper.GetChild(parent, i);
        if (child is T result)
        {
            return result;
        }
        else
        {
            return FindVisualChild<T>(child);
        }
    }
    return null;
}
 
 
 
 
 
 
 
 
![image]()
 
 
 
![image]()
 
 
![image]()
 
 
 
 
 
 
 
 
 
 
![image]()
 
 
 
 
 
 
 
 
![image]()
 
 
 
 
 
![image]()
 
//xaml
<Window x:Class="WpfApp81.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:WpfApp81"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <DataGrid
            ItemsSource="{Binding BooksCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"            
            AutoGenerateColumns="False"
            CanUserAddRows="False">
            <DataGrid.Columns>
                <DataGridTemplateColumn>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Grid Width="{Binding DataContext.GridWidth,RelativeSource={RelativeSource AncestorType=Window}}"
                                  Height="{Binding DataContext.GridHeight,RelativeSource={RelativeSource AncestorType=Window}}">
                                <Grid.Background>
                                    <!--<ImageBrush ImageSource="{Binding ImgPath, Converter={StaticResource PathToImageConverter}}"/>-->
                                    <ImageBrush ImageSource="{Binding ImgSource,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
                                </Grid.Background>
                                <Grid.Resources>
                                    <Style TargetType="ColumnDefinition">
                                        <Setter Property="Width" Value="Auto"/>
                                    </Style>
                                    <Style TargetType="TextBlock">
                                        <Setter Property="FontSize" Value="50"/>
                                        <Setter Property="HorizontalAlignment" Value="Center"/>
                                        <Setter Property="VerticalAlignment" Value="Center"/>
                                        <Style.Triggers>
                                            <Trigger Property="IsMouseOver" Value="True">
                                                <Setter Property="FontSize" Value="80"/>
                                                <Setter Property="Foreground" Value="Red"/>
                                            </Trigger>
                                        </Style.Triggers>
                                    </Style>
                                </Grid.Resources>
                                <Grid.RowDefinitions>
                                    <RowDefinition/>
                                    <RowDefinition/>
                                </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition/>
                                    <ColumnDefinition/>
                                    <ColumnDefinition/>
                                    <ColumnDefinition/>
                                    <ColumnDefinition/>
                                    <ColumnDefinition/>
                                    <ColumnDefinition/>
                                    <ColumnDefinition/>
                                    <ColumnDefinition/>
                                </Grid.ColumnDefinitions>
                                <TextBlock Text="{Binding Id}" Grid.Row="0" Grid.Column="0"/>
                                <TextBlock Text="{Binding Name}" Grid.Row="0" Grid.Column="1"/>
                                <TextBlock Text="{Binding Author}" Grid.Row="0" Grid.Column="2"/>
                                <TextBlock Text="{Binding Abstract}" Grid.Row="0" Grid.Column="3"/>
                                <TextBlock Text="{Binding Comment}" Grid.Row="0" Grid.Column="4"/>
                                <TextBlock Text="{Binding Content}" Grid.Row="0" Grid.Column="5"/>
                                <TextBlock Text="{Binding Description}" Grid.Row="0" Grid.Column="6"/>
                                <TextBlock Text="{Binding Title}" Grid.Row="0" Grid.Column="7"/>
                                <TextBlock Text="{Binding Topic}" Grid.Row="0" Grid.Column="8"/>
                                <TextBlock Text="{Binding ISBN}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="9">
                                    <TextBlock.Style>
                                        <Style TargetType="TextBlock">
                                            <Setter Property="FontSize" Value="50"/>
                                            <Style.Triggers>
                                                <Trigger Property="IsMouseOver" Value="True">
                                                    <Setter Property="Foreground" Value="Red"/>
                                                    <Setter Property="FontSize" Value="80"/>
                                                </Trigger>
                                            </Style.Triggers>
                                        </Style>
                                    </TextBlock.Style>
                                </TextBlock>
                            </Grid>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
            <DataGrid.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Export As Pictures"
                              Command="{Binding ExportAsPicturesCommand}"
                              CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ContextMenu}},
                        Path=PlacementTarget}"/>
                </ContextMenu>
            </DataGrid.ContextMenu>
        </DataGrid>
    </Grid>
</Window>
//cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace WpfApp81
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        MainVM vm;
        public MainWindow()
        {
            InitializeComponent();
            RenderOptions.ProcessRenderMode=System.Windows.Interop.RenderMode.Default;
            vm = new MainVM();
            this.SizeChanged+=MainWindow_SizeChanged;
            this.DataContext=vm;
        }
        private void MainWindow_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            var fe = this.Content as FrameworkElement;
            if (fe==null)
            {
                return;
            }
            vm.GridWidth=fe.ActualWidth;
            vm.GridHeight=fe.ActualHeight/2;
        }
    }
    public class MainVM : INotifyPropertyChanged
    {
        private double dpiFactor = 2.0;
        public MainVM()
        {
            InitData();
            InitCommands();
        }
        private void InitCommands()
        {
            ExportAsPicturesCommand=new DelCommand(async (obj) => await ExportAsPicturesCommandExecuted(obj));
        }
        private async Task ExportAsPicturesCommandExecuted(object? obj)
        {
            var dg = obj as DataGrid;
            if (dg!=null)
            {
                dg.UpdateLayout();
                string dir = $"JPG_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}";
                if (!Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }
                try
                {
                    var scrollViewer = FindVisualChild<ScrollViewer>(dg);
                    if (scrollViewer==null)
                    {
                        return;
                    }
                    int idx = 0;
                    for (int i = 0; i<dg.Items.Count; i++)
                    {
                        //Scroll to make the row visible
                        dg.ScrollIntoView(dg.Items[i]);
                        dg.UpdateLayout();
                        var row = dg.ItemContainerGenerator.ContainerFromIndex(i) as DataGridRow;
                        if (row==null)
                        {
                            continue;
                        }
                        //Ensure row is fully rendered
                        row.UpdateLayout();
                        row.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                        row.Arrange(new Rect(row.DesiredSize));
                        string jpgFileName = Path.Combine(dir, $"{++idx}.jpg");
                        SaveFrameworkElementAsJpg(row, jpgFileName);
                        await Task.Delay(1);
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex?.StackTrace.ToString());
                }
                finally
                {
                }
            }
        }
        private void SaveFrameworkElementAsJpg(FrameworkElement fe, string fileName)
        {
            RenderTargetBitmap rtb = new RenderTargetBitmap(
                (int)(fe.ActualWidth*dpiFactor),
                (int)(fe.ActualHeight*dpiFactor),
                96*dpiFactor,
                96*dpiFactor,
                PixelFormats.Pbgra32);
            DrawingVisual drawingVisual = new DrawingVisual();
            using (DrawingContext drawingContext = drawingVisual.RenderOpen())
            {
                VisualBrush visualBrush = new VisualBrush(fe);
                drawingContext.DrawRectangle(visualBrush, null,
                    new Rect(0, 0, fe.ActualWidth, fe.ActualHeight));
            }
            rtb.Render(drawingVisual);
            using (FileStream fileStream = new FileStream(fileName, FileMode.Create))
            {
                JpegBitmapEncoder jpgBmpEncoder = new JpegBitmapEncoder();
                jpgBmpEncoder.Frames.Add(BitmapFrame.Create(rtb));
                jpgBmpEncoder.Save(fileStream);
                System.Diagnostics.Debug.WriteLine(fileName);
            }
        }
        private T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
        {
            for (int i = 0; i<VisualTreeHelper.GetChildrenCount(parent); i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);
                if (child is T result)
                {
                    return result;
                }
                else
                {
                    return FindVisualChild<T>(child);
                }
            }
            return null;
        }
        private async Task InitData()
        {
            string dir = @"../../../Images";
            if (!Directory.Exists(dir))
            {
                return;
            }
            var imgs = Directory.GetFiles(dir);
            if (imgs==null|| !imgs.Any())
            {
                return;
            }
            int imgsCount = imgs.Count();
            List<Book> booksList = new List<Book>();
            BooksCollection=new ObservableCollection<Book>();
            await Task.Run(async () =>
            {
                for (int i = 1; i<1000001; i++)
                {
                    booksList.Add(new Book()
                    {
                        Id=i,
                        Name=$"Name_{i}",
                        Author=$"Author_{i}",
                        Comment=$"Comment_{i}",
                        Content=$"Content_{i}",
                        Abstract=$"Abstract_{i}",
                        Description=$"Description_{i}",
                        Title=$"Title_{i}",
                        Topic=$"Topic_{i}",
                        ISBN=$"ISBN_{i}_{Guid.NewGuid().ToString("N")}",
                        ImgSource=GetImgSourceViaImgUrl(imgs[i%imgsCount])
                    });
                    if (i%1000==0)
                    {
                        var tempList = booksList.ToList();
                        booksList.Clear();
                        Application.Current?.Dispatcher.InvokeAsync(() =>
                        {
                            foreach (var bk in tempList)
                            {
                                BooksCollection.Add(bk);
                            }
                            System.Diagnostics.Debug.WriteLine($"Idx:{i}");
                        });
                    }
                }
            });
        }
        private ImageSource GetImgSourceViaImgUrl(string imgUrl)
        {
            BitmapImage bmi = new BitmapImage();
            if (!File.Exists(imgUrl))
            {
                return bmi;
            }
            bmi.BeginInit();
            bmi.UriSource=new Uri(imgUrl, UriKind.RelativeOrAbsolute);
            bmi.EndInit();
            if (bmi.CanFreeze)
            {
                bmi.Freeze();
            }
            return bmi;
        }
        public event PropertyChangedEventHandler? PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propName = "")
        {
            var handler = PropertyChanged;
            if (handler!=null)
            {
                handler?.Invoke(this, new PropertyChangedEventArgs(propName));
            }
        }
        private double gridWidth;
        public double GridWidth
        {
            get
            {
                return gridWidth;
            }
            set
            {
                if (value!=gridWidth)
                {
                    gridWidth = value;
                    OnPropertyChanged();
                }
            }
        }
        private double gridHeight;
        public double GridHeight
        {
            get
            {
                return gridHeight;
            }
            set
            {
                if (value!=gridHeight)
                {
                    gridHeight=value;
                    OnPropertyChanged();
                }
            }
        }
        private ObservableCollection<Book> booksCollection;
        public ObservableCollection<Book> BooksCollection
        {
            get
            {
                return booksCollection;
            }
            set
            {
                if (value!=booksCollection)
                {
                    booksCollection = value;
                    OnPropertyChanged();
                }
            }
        }
        public ICommand ExportAsPicturesCommand { get; set; }
    }
    public class Book
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Author { get; set; }
        public string Abstract { get; set; }
        public string Comment { get; set; }
        public string Content { get; set; }
        public string Description { get; set; }
        public string Title { get; set; }
        public string Topic { get; set; }
        public string ISBN { get; set; }
        public ImageSource ImgSource { get; set; }
    }
    public class DelCommand : ICommand
    {
        private Action<object?> execute;
        private Predicate<object?> canExecute;
        public DelCommand(Action<object?> executeValue, Predicate<object?> canExecuteValue = null)
        {
            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);
        }
    }
}