WPF save datagrid items as jpg pictures page by page in mvvm

private ICommand saveAsJPGSCommand;
public ICommand SaveAsJPGSCommand
{
    get
    {
        if (saveAsJPGSCommand==null)
        {
            saveAsJPGSCommand=new DelCommand(async (obj)=> await SaveAsJPGSCommandExecutedAsync2(obj));
        }
        return saveAsJPGSCommand;
    }
}  

private async Task SaveAsJPGSCommandExecutedAsync2(object? obj)
{
    var dg = obj as DataGrid;
    if (dg==null)
    {
        return;
    }

    await Task.Yield(); // let WPF finish layout

    // Find the headers presenter
    var headersPresenter = GetVisualChild<DataGridColumnHeadersPresenter>(dg);
    double headerHeight = 0;

    if (headersPresenter != null)
    {
        headerHeight = headersPresenter.ActualHeight;

        // fallback: if headers are not measured yet
        if (headerHeight <= 0)
        {
            headersPresenter.UpdateLayout();
            headerHeight = headersPresenter.ActualHeight;
        }
    } 

    string dir = $"Dg_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}";
    Directory.CreateDirectory(dir);

    VirtualizingStackPanel.SetIsVirtualizing(dg, false);
    VirtualizingPanel.SetVirtualizationMode(dg, VirtualizationMode.Standard);
    dg.UpdateLayout();

    double height = 0, width = 0;
    ScrollViewer scrollViewer = GetVisualChild<ScrollViewer>(dg);
    if(scrollViewer==null)
    {
        return;
    }

    width= scrollViewer.ExtentWidth;

    var row = dg.ItemContainerGenerator.ContainerFromIndex(0) as DataGridRow;
    if (row==null)
    {                
        row=dg.ItemContainerGenerator.ContainerFromIndex(0) as DataGridRow;
        dg.UpdateLayout();
    }

    if (row==null)
    {
        return;
    }

    double rowHeight = row.ActualHeight;
    height=rowHeight*dg.Items.Count;
    int rowsPerPage = (int)Math.Floor((double)(scrollViewer.ActualHeight-headerHeight)/rowHeight)-1;
    int totalPages = (int)Math.Ceiling((double)dg.Items.Count/rowsPerPage);

    for (int page = 0; page<totalPages; page++)
    {
        int startIdx = Math.Min((page+1)*rowsPerPage-1,dg.Items.Count-1);
        await dg.Dispatcher.InvokeAsync(() =>
        {
            dg.ScrollIntoView(dg.Items[startIdx]);
            dg.UpdateLayout();
        }, DispatcherPriority.Background);

        var bmp = RenderDataGridPage(dg,width, rowHeight*20);
        string jpgFile = Path.Combine(dir, $"Page_{page+1}.jpg");

        SaveBitmapSourceAsJPG(bmp, jpgFile);
        Debug.WriteLine($"Page {page+1},startIdx {startIdx}");
        //Mock the scroll down effect page by page and pause for a second 
        await Task.Delay(500);
    }
    MessageBox.Show($"Saved {totalPages} pages to {dir}");
}

private RenderTargetBitmap RenderDataGridPage(DataGrid dg,double width,double height)
{
    var rtb = new RenderTargetBitmap((int)width*2,
        (int)height*2,
        96*2,
        96*2,
        PixelFormats.Pbgra32);

    DrawingVisual drawingVisual = new DrawingVisual();
    using (DrawingContext drawingContext = drawingVisual.RenderOpen())
    {
        VisualBrush brush = new VisualBrush(dg);
        drawingContext.DrawRectangle(brush, null, new Rect(0, 0, width, height));
    }

    rtb.Render(drawingVisual);
    return rtb;
}

private void SaveBitmapSourceAsJPG(RenderTargetBitmap bmpSource, string jpgFile)
{
    using (FileStream fileStream = new FileStream(jpgFile, FileMode.Create))
    {
        JpegBitmapEncoder encoder = new JpegBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(bmpSource));
        encoder.Save(fileStream);
    }
}

private T GetVisualChild<T>(DependencyObject dObj) where T : Visual
{
    int childCount = VisualTreeHelper.GetChildrenCount(dObj);
    for (int i = 0; i<childCount; i++)
    {
        var child = VisualTreeHelper.GetChild(dObj, i);
        if (child is T result)
        {
            return result;
        }

        var subChild = GetVisualChild<T>(child);
        if (subChild != null)
        {
            return subChild;
        }
    }
    return null;
}

 

 

 

 

image

 

 

image

 

 

 

image

 

 

 

 

 

 

 

image

 

 

 

//xaml
<Window x:Class="WpfApp65.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:WpfApp65"
        WindowState="Maximized"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <DataGrid x:Name="dg"
          ItemsSource="{Binding BooksCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
          Width="{Binding DataContext.DgWidth,RelativeSource={RelativeSource Mode=FindAncestor,
            AncestorType={x:Type Window}}}"
          AutoGenerateColumns="False"
          CanUserAddRows="False">
            <DataGrid.Resources>
                <Style TargetType="DataGridRow">
                    <Setter Property="FontSize" Value="30"/>
                </Style>
            </DataGrid.Resources>
            <DataGrid.Columns>
                <DataGridTextColumn Header="Id" Binding="{Binding Id}"/>
                <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
                <DataGridTextColumn Header="Author" Binding="{Binding Author}"/>
                <DataGridTextColumn Header="Title" Binding="{Binding Title}"/>
                <DataGridTextColumn Header="Topic"  Binding="{Binding Topic}"/>
                <DataGridTextColumn Header="Summary"  Binding="{Binding Summary}"/>
                <DataGridTextColumn Header="ISBN"  Binding="{Binding ISBN}"/>
                <DataGridTextColumn Header="Content"  Binding="{Binding Content}"/>
                <DataGridTextColumn Header="Chapter"  Binding="{Binding Chapter}"/>
                <DataGridTextColumn Header="Abstract"  Binding="{Binding Abstract}"/>
            </DataGrid.Columns>
            <DataGrid.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Save As Jpgs"
                      Command="{Binding SaveAsJPGSCommand}"
                      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.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Path = System.IO.Path;

namespace WpfApp65
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            VirtualizingStackPanel.SetIsVirtualizing(dg, false);
            VirtualizingPanel.SetVirtualizationMode(dg, VirtualizationMode.Standard);
            var vm = new MainVM(this);
            this.DataContext = vm;
        }
    }

    public class MainVM : INotifyPropertyChanged
    {
        private Window win;
        public MainVM(Window winValue)
        {
            win= winValue;
            if (win!=null)
            {
                win.Loaded+=Win_Loaded;
            }
        }

        private async void Win_Loaded(object sender, RoutedEventArgs e)
        {
            var fe = win.Content as FrameworkElement;
            if (fe!=null)
            {
                DgWidth= fe.ActualWidth;
            }
            await Task.Run(() =>
            {
                InitData();
            });
        }

        private void InitData()
        {
            int idx = 0;
            var tempList = new List<Book>();
            for (int i = 0; i<2000; i++)
            {
                ++idx;
                tempList.Add(new Book()
                {
                    Id=idx,
                    Author=$"Author_{idx}",
                    Description=$"Description_{idx}",
                    Name=$"Name_{idx}",
                    Title=$"Title_{idx}",
                    Topic=$"Topic_{idx}",
                    Summary=$"Summary_{idx}",
                    Content=$"Content_{idx}",
                    Chapter=$"Chapter_{idx}",
                    Abstract=$"Abstract_{idx}",
                    ISBN=$"ISBN_{idx}_{Guid.NewGuid().ToString("N")}"
                });
            }

            Application.Current.Dispatcher.BeginInvoke(() =>
            {
                BooksCollection=new ObservableCollection<Book>(tempList);
            });
        }

        public event PropertyChangedEventHandler? PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propName = "")
        {
            var handler = PropertyChanged;
            if (handler!=null)
            {
                handler?.Invoke(this, new PropertyChangedEventArgs(propName));
            }
        }

        private double dgWidth;
        public double DgWidth
        {
            get
            {
                return dgWidth;
            }
            set
            {
                if (value!=dgWidth)
                {
                    dgWidth = value;
                    OnPropertyChanged();
                }
            }
        }

        private ObservableCollection<Book> booksCollection;
        public ObservableCollection<Book> BooksCollection
        {
            get
            {
                return booksCollection;
            }
            set
            {
                if (value!= booksCollection)
                {
                    booksCollection = value;
                    OnPropertyChanged(nameof(BooksCollection));
                }
            }
        }

        private ICommand saveAsJPGSCommand;
        public ICommand SaveAsJPGSCommand
        {
            get
            {
                if (saveAsJPGSCommand==null)
                {
                    saveAsJPGSCommand=new DelCommand(async (obj)=> await SaveAsJPGSCommandExecutedAsync2(obj));
                }
                return saveAsJPGSCommand;
            }
        }  

        private async Task SaveAsJPGSCommandExecutedAsync2(object? obj)
        {
            var dg = obj as DataGrid;
            if (dg==null)
            {
                return;
            }

            await Task.Yield(); // let WPF finish layout

            // Find the headers presenter
            var headersPresenter = GetVisualChild<DataGridColumnHeadersPresenter>(dg);
            double headerHeight = 0;

            if (headersPresenter != null)
            {
                headerHeight = headersPresenter.ActualHeight;

                // fallback: if headers are not measured yet
                if (headerHeight <= 0)
                {
                    headersPresenter.UpdateLayout();
                    headerHeight = headersPresenter.ActualHeight;
                }
            } 

            string dir = $"Dg_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}";
            Directory.CreateDirectory(dir);

            VirtualizingStackPanel.SetIsVirtualizing(dg, false);
            VirtualizingPanel.SetVirtualizationMode(dg, VirtualizationMode.Standard);
            dg.UpdateLayout();

            double height = 0, width = 0;
            ScrollViewer scrollViewer = GetVisualChild<ScrollViewer>(dg);
            if(scrollViewer==null)
            {
                return;
            }

            width= scrollViewer.ExtentWidth;

            var row = dg.ItemContainerGenerator.ContainerFromIndex(0) as DataGridRow;
            if (row==null)
            {                
                row=dg.ItemContainerGenerator.ContainerFromIndex(0) as DataGridRow;
                dg.UpdateLayout();
            }

            if (row==null)
            {
                return;
            }

            double rowHeight = row.ActualHeight;
            height=rowHeight*dg.Items.Count;
            int rowsPerPage = (int)Math.Floor((double)(scrollViewer.ActualHeight-headerHeight)/rowHeight)-1;
            int totalPages = (int)Math.Ceiling((double)dg.Items.Count/rowsPerPage);

            for (int page = 0; page<totalPages; page++)
            {
                int startIdx = Math.Min((page+1)*rowsPerPage-1,dg.Items.Count-1);
                await dg.Dispatcher.InvokeAsync(() =>
                {
                    dg.ScrollIntoView(dg.Items[startIdx]);
                    dg.UpdateLayout();
                }, DispatcherPriority.Background);

                var bmp = RenderDataGridPage(dg,width, rowHeight*20);
                string jpgFile = Path.Combine(dir, $"Page_{page+1}.jpg");

                SaveBitmapSourceAsJPG(bmp, jpgFile);
                Debug.WriteLine($"Page {page+1},startIdx {startIdx}");
                //Mock the scroll down effect page by page and pause for a second 
                await Task.Delay(500);
            }
            MessageBox.Show($"Saved {totalPages} pages to {dir}");
        }

        private RenderTargetBitmap RenderDataGridPage(DataGrid dg,double width,double height)
        {
            var rtb = new RenderTargetBitmap((int)width*2,
                (int)height*2,
                96*2,
                96*2,
                PixelFormats.Pbgra32);

            DrawingVisual drawingVisual = new DrawingVisual();
            using (DrawingContext drawingContext = drawingVisual.RenderOpen())
            {
                VisualBrush brush = new VisualBrush(dg);
                drawingContext.DrawRectangle(brush, null, new Rect(0, 0, width, height));
            }

            rtb.Render(drawingVisual);
            return rtb;
        }

        private void SaveBitmapSourceAsJPG(RenderTargetBitmap bmpSource, string jpgFile)
        {
            using (FileStream fileStream = new FileStream(jpgFile, FileMode.Create))
            {
                JpegBitmapEncoder encoder = new JpegBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(bmpSource));
                encoder.Save(fileStream);
            }
        }

        private T GetVisualChild<T>(DependencyObject dObj) where T : Visual
        {
            int childCount = VisualTreeHelper.GetChildrenCount(dObj);
            for (int i = 0; i<childCount; i++)
            {
                var child = VisualTreeHelper.GetChild(dObj, i);
                if (child is T result)
                {
                    return result;
                }

                var subChild = GetVisualChild<T>(child);
                if (subChild != null)
                {
                    return subChild;
                }
            }
            return null;
        }
    }

    public class Book
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string Author { get; set; }
        public string Abstract { get; set; }
        public string Topic { get; set; }
        public string Summary { get; set; }
        public string Content { get; set; }
        public string Chapter { get; set; }
        public string ISBN { 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);
        }
    }

}

 

posted @ 2025-09-01 19:49  FredGrit  阅读(5)  评论(0)    收藏  举报