WPF datagrid export multiple specified datagridrows into one picture by System.Drawing.Common graphics without blocking ui issues

Install-Package System.Drawing.Common;

 

 

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

private async Task SaveAsJPGSCommandExecutedAsync(object? obj)
{
    int batchIdx = 0;
    string dir = "", groupDir = "";

    try
    {
        var dg = obj as DataGrid;
        if (dg==null)
        {
            return;
        }

        dir = $"JPG_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}";
        if (!Directory.Exists(dir))
        {
            Directory.CreateDirectory(dir);
        }

        groupDir = $"Group_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}";
        if (!Directory.Exists(groupDir))
        {
            Directory.CreateDirectory(groupDir);
        }

        var items = dg.Items;
        int idx = 0;
        foreach (var item in items)
        {
            await Application.Current.Dispatcher.InvokeAsync(() =>
            {
                dg.ScrollIntoView(item);
                dg.UpdateLayout();

                var row = dg.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
                if (row != null)
                {
                    var bmp = SaveDataGridRowAsRenderTargetBitmap(row);
                    string jpgFile = Path.Combine(dir, $"{++idx}.jpg");
                    SaveBitmapSourceAsJPGFile(bmp, jpgFile);
                }
            });
        }
        //MessageBox.Show($"Saved {idx} files in {dir}");

        await Task.Run(() =>
        {
            var imgs = Directory.GetFiles(dir)
            .Select(x => new FileInfo(x))
            .OrderBy(x => x.CreationTime)
            .Select(x => x.FullName);

            var imgsGroups = imgs.Chunk(20);

            batchIdx=0;
            foreach (var imgGroup in imgsGroups)
            {
                List<Bitmap> bitmapsList = new List<Bitmap>();
                foreach (var bitmap in imgGroup)
                {
                    bitmapsList.Add(new Bitmap(bitmap));
                }

                using (var bmp = ImageStitcher.SticthImagesVertical(bitmapsList))
                {
                    string groupedImgFile = System.IO.Path.Combine(groupDir, $"Group_{++batchIdx}.jpg");
                    bmp.Save(groupedImgFile, System.Drawing.Imaging.ImageFormat.Jpeg);
                }

                foreach (var bmp in bitmapsList)
                {
                    bmp.Dispose();
                }
            }
        });
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine(ex.Message);
    }
    finally
    {
        await Task.Run(() =>
        {
            try
            {
                if (Directory.Exists(dir))
                {
                    // Ensure all file handles are closed
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    Thread.Sleep(100);
                    //Directory.Delete(dir, true);
                }
            }
            catch (IOException ex)
            {
                System.Diagnostics.Debug.WriteLine($"Could not delete temp directory: {ex.Message}");
            }
        });

        MessageBox.Show($"Saved {batchIdx} grouped images in {groupDir}");
    }
}

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

private RenderTargetBitmap SaveDataGridRowAsRenderTargetBitmap(DataGridRow row)
{
    var rtb = new RenderTargetBitmap(
        (int)row.ActualWidth*2,
        (int)row.ActualHeight*2,
        96*2,
        96*2,
        PixelFormats.Pbgra32);

    DrawingVisual drawingVisual = new DrawingVisual();
    using (DrawingContext drawingContext = drawingVisual.RenderOpen())
    {
        VisualBrush brush = new VisualBrush(row);
        drawingContext.DrawRectangle(brush, null, new Rect(0, 0, row.ActualWidth, row.ActualHeight));
    }

    rtb.Render(drawingVisual);
    //rtb.Freeze();
    return rtb;
}



public static class ImageStitcher
{
    public static Bitmap SticthImagesVertical(List<Bitmap> imgsList)
    {
        if (imgsList==null && !imgsList.Any())
        {
            return null;
        }

        int width = 0;
        int height = 0;
        foreach (var img in imgsList)
        {
            if (img.Width>width)
            {
                width=img.Width;
            }
            height+=img.Height;
        }

        Bitmap finalImage = new Bitmap(width, height);
        using (Graphics g = Graphics.FromImage(finalImage))
        {
            g.Clear(Color.White);
            int offsetY = 0;
            foreach (var img in imgsList)
            {
                g.DrawImage(img, new System.Drawing.Rectangle(0, offsetY, img.Width, img.Height));
                offsetY+=img.Height;
            }
        }
        return finalImage;
    }
}

 

 

 

 

 

 

image

 

//xaml
<Window x:Class="WpfApp64.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:WpfApp64"
        WindowState="Maximized"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <DataGrid x:Name="dg"
                  ItemsSource="{Binding BooksCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
                  ColumnWidth="Auto"
                  AutoGenerateColumns="False"
                  CanUserAddRows="False">
            <DataGrid.Resources>
                <Style TargetType="DataGridRow">
                    <Setter Property="FontSize" Value="30"/>
                </Style>
            </DataGrid.Resources>
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding Id}"/>
                <DataGridTextColumn Binding="{Binding Name}"/>
                <DataGridTextColumn Binding="{Binding Author}"/>
                <DataGridTextColumn Binding="{Binding Title}"/>
                <DataGridTextColumn Binding="{Binding Topic}"/>
                <DataGridTextColumn Binding="{Binding Summary}"/>
                <DataGridTextColumn Binding="{Binding ISBN}"/>
                <DataGridTextColumn Binding="{Binding Content}"/>
                <DataGridTextColumn Binding="{Binding Chapter}"/>
                <DataGridTextColumn 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.Drawing;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Color = System.Drawing.Color;

namespace WpfApp64
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            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)
        {
            await Task.Run(() =>
             {
                 InitData();
             });
        }

        private void InitData()
        {
            int idx = 0;
            var tempList = new List<Book>();
            for (int i = 0; i<1000; 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 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 SaveAsJPGSCommandExecutedAsync(obj));
                }
                return saveAsJPGSCommand;
            }
        }

        private async Task SaveAsJPGSCommandExecutedAsync(object? obj)
        {
            int batchIdx = 0;
            string dir = "", groupDir = "";

            try
            {
                var dg = obj as DataGrid;
                if (dg==null)
                {
                    return;
                }

                dir = $"JPG_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}";
                if (!Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }

                groupDir = $"Group_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}";
                if (!Directory.Exists(groupDir))
                {
                    Directory.CreateDirectory(groupDir);
                }

                var items = dg.Items;
                int idx = 0;
                foreach (var item in items)
                {
                    await Application.Current.Dispatcher.InvokeAsync(() =>
                    {
                        dg.ScrollIntoView(item);
                        dg.UpdateLayout();

                        var row = dg.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
                        if (row != null)
                        {
                            var bmp = SaveDataGridRowAsRenderTargetBitmap(row);
                            string jpgFile = Path.Combine(dir, $"{++idx}.jpg");
                            SaveBitmapSourceAsJPGFile(bmp, jpgFile);
                        }
                    });
                }
                //MessageBox.Show($"Saved {idx} files in {dir}");

                await Task.Run(() =>
                {
                    var imgs = Directory.GetFiles(dir)
                    .Select(x => new FileInfo(x))
                    .OrderBy(x => x.CreationTime)
                    .Select(x => x.FullName);

                    var imgsGroups = imgs.Chunk(20);

                    batchIdx=0;
                    foreach (var imgGroup in imgsGroups)
                    {
                        List<Bitmap> bitmapsList = new List<Bitmap>();
                        foreach (var bitmap in imgGroup)
                        {
                            bitmapsList.Add(new Bitmap(bitmap));
                        }

                        using (var bmp = ImageStitcher.SticthImagesVertical(bitmapsList))
                        {
                            string groupedImgFile = System.IO.Path.Combine(groupDir, $"Group_{++batchIdx}.jpg");
                            bmp.Save(groupedImgFile, System.Drawing.Imaging.ImageFormat.Jpeg);
                        }

                        foreach (var bmp in bitmapsList)
                        {
                            bmp.Dispose();
                        }
                    }
                });
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }
            finally
            {
                await Task.Run(() =>
                {
                    try
                    {
                        if (Directory.Exists(dir))
                        {
                            // Ensure all file handles are closed
                            GC.Collect();
                            GC.WaitForPendingFinalizers();
                            Thread.Sleep(100);
                            //Directory.Delete(dir, true);
                        }
                    }
                    catch (IOException ex)
                    {
                        System.Diagnostics.Debug.WriteLine($"Could not delete temp directory: {ex.Message}");
                    }
                });

                MessageBox.Show($"Saved {batchIdx} grouped images in {groupDir}");
            }
        }

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

        private RenderTargetBitmap SaveDataGridRowAsRenderTargetBitmap(DataGridRow row)
        {
            var rtb = new RenderTargetBitmap(
                (int)row.ActualWidth*2,
                (int)row.ActualHeight*2,
                96*2,
                96*2,
                PixelFormats.Pbgra32);

            DrawingVisual drawingVisual = new DrawingVisual();
            using (DrawingContext drawingContext = drawingVisual.RenderOpen())
            {
                VisualBrush brush = new VisualBrush(row);
                drawingContext.DrawRectangle(brush, null, new Rect(0, 0, row.ActualWidth, row.ActualHeight));
            }

            rtb.Render(drawingVisual);
            //rtb.Freeze();
            return rtb;
        }

    }

    public static class ImageStitcher
    {
        public static Bitmap SticthImagesVertical(List<Bitmap> imgsList)
        {
            if (imgsList==null && !imgsList.Any())
            {
                return null;
            }

            int width = 0;
            int height = 0;
            foreach (var img in imgsList)
            {
                if (img.Width>width)
                {
                    width=img.Width;
                }
                height+=img.Height;
            }

            Bitmap finalImage = new Bitmap(width, height);
            using (Graphics g = Graphics.FromImage(finalImage))
            {
                g.Clear(Color.White);
                int offsetY = 0;
                foreach (var img in imgsList)
                {
                    g.DrawImage(img, new System.Drawing.Rectangle(0, offsetY, img.Width, img.Height));
                    offsetY+=img.Height;
                }
            }
            return finalImage;
        }
    }

    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-08-30 21:15  FredGrit  阅读(2)  评论(0)    收藏  举报