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);
}
}
}