WPF save canvas as pictures via rendetargetbitmap DrawingVisual DrawingContext VisualBrush
//xaml <Window x:Class="WpfApp27.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:WpfApp27" WindowState="Maximized" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Canvas x:Name="cvs" Background="White"> <Grid> <Grid.RowDefinitions> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Ellipse x:Name="elp" Grid.Row="0" Grid.Column="0" Stroke="Red" StrokeThickness="20" Width="500" Height="500" Fill="Cyan"/> <Rectangle x:Name="rect" Grid.Row="0" Grid.Column="1" Width="500" Height="500" Stroke="Red" StrokeThickness="30" HorizontalAlignment="Stretch"/> <Rectangle x:Name="rect2" Grid.Row="0" Grid.Column="2" Width="500" Height="500" Stroke="Blue" StrokeThickness="30" HorizontalAlignment="Stretch"/> </Grid> <Canvas.ContextMenu> <ContextMenu> <MenuItem Header="Save As Picture" Command="{Binding SaveAsPictureCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ContextMenu}},Path=PlacementTarget}"/> <MenuItem Header="Save Canvas As Image" Command="{Binding SaveCanvasAsImageCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ContextMenu}},Path=PlacementTarget}"/> </ContextMenu> </Canvas.ContextMenu> </Canvas> </Grid> </Window> //cs using Microsoft.Win32; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; 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; namespace WpfApp27 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); var vm = new MainVM(this, cvs); this.DataContext=vm; } } public class MainVM : INotifyPropertyChanged { private Window win; private Canvas cvs; private int dpiFactor = 2; public MainVM(Window winValue, Canvas cvsValue) { this.win=winValue; this.cvs=cvsValue; } public event PropertyChangedEventHandler? PropertyChanged; public void OnPropertyChanged([CallerMemberName] string propName = "") { var handler = PropertyChanged; if (handler!=null) { handler?.Invoke(this, new PropertyChangedEventArgs(propName)); } } private DelCommand saveAsPictureCommand; public DelCommand SaveAsPictureCommand { get { if (saveAsPictureCommand==null) { saveAsPictureCommand=new DelCommand(SaveAsPictureCommandExecuted); } return saveAsPictureCommand; } } private void SaveAsPictureCommandExecuted(object? obj) { SaveFileDialog dialog = new SaveFileDialog(); dialog.Filter="Jpg Files|*.jpg|All Files|*.*"; dialog.FileName=$"Canvas_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}"; if (dialog.ShowDialog()==true && cvs!=null) { SaveCanvasAsPicture(cvs, dialog.FileName); MessageBox.Show($"Saved in {dialog.FileName}", "Save Canvas As Picture"); OpenPicture(dialog.FileName); } } private void SaveCanvasAsPicture(Canvas cvs, string fileName) { Size size = new Size(cvs.ActualWidth, cvs.ActualHeight); cvs.Measure(size); cvs.Arrange(new Rect(size)); RenderTargetBitmap renderBmp = new RenderTargetBitmap((int)size.Width*dpiFactor, (int)size.Height*dpiFactor, 96*dpiFactor, 96*dpiFactor, PixelFormats.Pbgra32); renderBmp.Render(cvs); using (FileStream fs = new FileStream(fileName, FileMode.Create)) { JpegBitmapEncoder encoder = new JpegBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(renderBmp)); encoder.Save(fs); } } private void OpenPicture(string fileName) { try { Process.Start(new ProcessStartInfo { FileName = fileName, UseShellExecute=true }); } catch (Exception ex) { MessageBox.Show($"Error opening image:{ex.Message}"); } } private DelCommand saveCanvasAsImageCommand; public DelCommand SaveCanvasAsImageCommand { get { if (saveCanvasAsImageCommand==null) { saveCanvasAsImageCommand=new DelCommand(SaveCanvasAsImageCommandExecuted); } return saveCanvasAsImageCommand; } } private void SaveCanvasAsImageCommandExecuted(object? obj) { SaveFileDialog dialog = new SaveFileDialog() { Filter="PNG Image|*.png|JPEG Image|*.jpg|Bitmap Image|*.bmp", Title="Save Canvas As Image", FileName = $"Image_{DateTime.Now.ToString("yyyyMMddHHmmssffff")}" }; if (dialog.ShowDialog()==true) { SaveCanvasToImage(cvs, dialog.FileName); MessageBox.Show($"Save canvas in {dialog.FileName}"); OpenPicture(dialog.FileName); } } private void SaveCanvasToImage(Canvas cvs, string fileName) { string ext = System.IO.Path.GetExtension(fileName).ToLower(); Rect bounds = VisualTreeHelper.GetDescendantBounds(cvs); RenderTargetBitmap rtb = new RenderTargetBitmap( (int)bounds.Width*dpiFactor, (int)bounds.Height*dpiFactor, 96*dpiFactor, 96*dpiFactor, PixelFormats.Pbgra32); DrawingVisual dv = new DrawingVisual(); using (DrawingContext dc = dv.RenderOpen()) { VisualBrush vb = new VisualBrush(cvs); dc.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size)); } rtb.Render(dv); BitmapEncoder encoder = null; switch (ext) { case ".jpg": encoder = new JpegBitmapEncoder(); break; case ".bmp": encoder=new BmpBitmapEncoder(); break; case ".png": encoder=new PngBitmapEncoder(); break; } encoder.Frames.Add(BitmapFrame.Create(rtb)); using (FileStream fs = File.Open(fileName, FileMode.Create)) { encoder.Save(fs); } } } 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); } } }