Install-Package CommunityToolkit.Mvvm;
//xaml
<Window x:Class="WpfApp69.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:WpfApp69"
mc:Ignorable="d"
Title="{Binding JPGFile,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Height="450" Width="800">
<Grid>
<DataGrid ItemsSource="{Binding BooksCollection,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
VirtualizingPanel.IsVirtualizing="False"
VirtualizingPanel.IsContainerVirtualizable="False"
AutoGenerateColumns="False"
CanUserAddRows="False">
<DataGrid.ItemContainerStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Height" Value="{Binding DataContext.RowHeight,
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"/>
<Setter Property="Width" Value="{Binding DataContext.RowWidth,
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"/>
<Setter Property="FontSize" Value="30"/>
<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>
</DataGrid.ItemContainerStyle>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid Width="{Binding DataContext.RowWidth,
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}">
<Grid.Background>
<ImageBrush ImageSource="{Binding ImgSource}"/>
</Grid.Background>
<Grid.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Id}" Grid.Column="0" VerticalAlignment="Center" />
<TextBlock Text="{Binding Author}" Grid.Column="1"/>
<TextBlock Text="{Binding Name}" Grid.Column="2" />
<TextBlock Text="{Binding Chapter}" Grid.Column="3" />
<TextBlock Text="{Binding Summary}" Grid.Column="4" />
<TextBlock Text="{Binding Title}" Grid.Column="5" />
<TextBlock Text="{Binding Topic}" Grid.Column="6"/>
<TextBlock Text="{Binding Press}" Grid.Column="7" />
<TextBlock Text="{Binding ISBN}" Grid.Column="8" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Export As Jpg"
Command="{Binding ExportAsJpgCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ContextMenu}},Path=PlacementTarget}"/>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
</Grid>
</Window>
//cs
using CommunityToolkit.Mvvm.ComponentModel;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Text;
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 Path = System.IO.Path;
namespace WpfApp69
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var vm = new MainVM(this);
this.DataContext=vm;
}
}
public partial class MainVM : ObservableObject
{
Window win;
public ICommand ExportAsJpgCommand { get; set; }
public MainVM(Window winValue)
{
win= winValue;
if (win!=null)
{
win.SizeChanged+=Win_SizeChanged;
}
InitBooksCollection();
InitCommands();
}
private void InitCommands()
{
ExportAsJpgCommand=new DelCommand(ExportAsJpgCommandExecuted);
}
private async void ExportAsJpgCommandExecuted(object? obj)
{
var dg = obj as DataGrid;
if (dg!=null && dg.Items!=null && dg.Items.Count>0)
{
string dir = $"Row_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}";
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
int idx = 0;
foreach (var item in dg.Items)
{
var itemFe = dg.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
if (itemFe!=null)
{
itemFe.UpdateLayout();
string rowJpg = Path.Combine(dir, $"Row_{++idx}.jpg");
JPGFile=rowJpg;
// Render synchronously on UI thread
RenderTargetBitmap bmpSource = RenderItemAsBmpSource(itemFe);
// Save in background
await Task.Run(() =>
{
SaveBmpSourceAsJpgFile(bmpSource, rowJpg);
});
//Yield to UI → prevents freezing during long loops
await Task.Yield();
}
}
MessageBox.Show($"Finished export datagridrow as pictures in {dir}");
}
}
private void SaveBmpSourceAsJpgFile(RenderTargetBitmap bmpSource, string rowJpg)
{
using (FileStream fileStream = new FileStream(rowJpg, FileMode.Create))
{
JpegBitmapEncoder jpgEncoder = new JpegBitmapEncoder();
jpgEncoder.Frames.Add(BitmapFrame.Create(bmpSource));
jpgEncoder.Save(fileStream);
System.Diagnostics.Debug.WriteLine(rowJpg);
}
}
private RenderTargetBitmap RenderItemAsBmpSource(FrameworkElement fe)
{
var rtb = new RenderTargetBitmap((int)fe.ActualWidth*2,
(int)fe.ActualHeight*2,
96*2,
96*2,
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);
if(rtb.CanFreeze)
{
rtb.Freeze();
}
return rtb;
}
private void InitBooksCollection()
{
Task.Run(() =>
{
string dir = @"../../../Images";
if (!Directory.Exists(dir))
{
return;
}
var imgs = Directory.GetFiles(dir);
int imgsCount = imgs.Count();
var booksList = new List<Book>();
for (int i = 1; i<=1000; i++)
{
booksList.Add(new Book()
{
Id=i,
Name=$"Name_{i}",
Author=$"Author_{i}",
Chapter=$"Chapter_{i}",
Summary=$"Summary_{i}",
Title=$"Title_{i}",
Topic=$"Topic_{i}",
Press=$"Press_{i}",
ISBN=$"ISBN_{i}",
ImgSource=GetImgSourceFromImgUrl(imgs[i%imgsCount])
});
}
BooksCollection=new ObservableCollection<Book>(booksList);
});
}
private ImageSource GetImgSourceFromImgUrl(string imgUrl)
{
BitmapImage bmi = new BitmapImage();
bmi.BeginInit();
bmi.UriSource=new Uri(imgUrl, UriKind.RelativeOrAbsolute);
bmi.EndInit();
if (bmi.CanFreeze)
{
bmi.Freeze();
}
return bmi;
}
private void Win_SizeChanged(object sender, SizeChangedEventArgs e)
{
var fe = win.Content as FrameworkElement;
if (fe!=null)
{
RowHeight=fe.ActualHeight/2;
RowWidth=fe.ActualWidth;
}
}
[ObservableProperty]
private ObservableCollection<Book> booksCollection;
[ObservableProperty]
private double rowHeight;
[ObservableProperty]
private double rowWidth;
[ObservableProperty]
private string jPGFile;
}
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);
}
}
public class Book
{
public int Id { get; set; }
public string Author { get; set; }
public string Name { get; set; }
public string Chapter { get; set; }
public string Summary { get; set; }
public string Title { get; set; }
public string Topic { get; set; }
public string Press { get; set; }
public string ISBN { get; set; }
public ImageSource ImgSource { get; set; }
}
}
![image]()