WPF Canvas mark triangle, circle, and retangle, then save the whole canvas as jpg file

WPF Canvas mark triangle, circle, and retangle, then save the whole canvas as jpg file
Install-Package Wpf.Prism;

 

 

 private void SaveAsJpgCommandExecuted(){var dpi=VisualTreeHelper.GetDpi(this);RenderTargetBitmap rtb=new RenderTargetBitmap((int)(cvs.ActualWidth*dpi.DpiScaleY), (int)(cvs.ActualHeight*dpi.DpiScaleY),dpi.PixelsPerInchX,dpi.PixelsPerInchY,PixelFormats.Pbgra32);rtb.Render(cvs);SaveFileDialog dialog = new SaveFileDialog();dialog.Filter = $"JPG Files|*.jpg";dialog.FileName = $"Canvas{DateTime.Now.ToString("yyyyMMddHHmmssffff")}.jpg";if(dialog.ShowDialog()==true){using (FileStream fs = new FileStream(dialog.FileName, FileMode.Create)){JpegBitmapEncoder encoder = new JpegBitmapEncoder();encoder.Frames.Add(BitmapFrame.Create(rtb));encoder.Save(fs);MessageBox.Show($"Saved in {dialog.FileName}!");}}            }

 

 

 

 

 

 

 

image

 

 

 

 

 

 

image

 

 

 

 

 

 

 

 

 

 

//xaml
<Window x:Class="WpfApp15.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:WpfApp15"mc:Ignorable="d"WindowState="Maximized"Title="MainWindow" Height="450" Width="800"><Grid><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="*"/></Grid.RowDefinitions><StackPanel Grid.Row="0"Orientation="Horizontal"><StackPanel.Resources><Style TargetType="CheckBox"><Setter Property="FontSize" Value="50"/></Style></StackPanel.Resources><CheckBox Content="Triangle" x:Name="triangleChx"                      Checked="triangleCbx_Checked"/><CheckBox Content="Circle" x:Name="circleChx" Checked="circleCbx_Checked"/><CheckBox Content="Rectangle" x:Name="rectangleChx"Checked="rectangleChx_Checked"/></StackPanel><Canvas Grid.Row="1"x:Name="cvs"HorizontalAlignment="Stretch"VerticalAlignment="Stretch"Background="Transparent"MouseLeftButtonDown="cvs_MouseLeftButtonDown"><Canvas.ContextMenu><ContextMenu><MenuItem Header="Save As Picture"Command="{Binding SaveAsJpgCommand}"/></ContextMenu></Canvas.ContextMenu></Canvas></Grid>
</Window>//cs
using Microsoft.Win32;
using System.Diagnostics.Tracing;
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;namespace WpfApp15
{/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow : Window{private double sideLength = 100.0d;int idx = 0;Image img;public DelegateCommand SaveAsJpgCommand { get; set; }public MainWindow(){InitializeComponent();this.DataContext = this;this.Loaded += MainWindow_Loaded;this.SizeChanged += MainWindow_SizeChanged;SaveAsJpgCommand = new DelegateCommand(SaveAsJpgCommandExecuted);}private void SaveAsJpgCommandExecuted(){var dpi=VisualTreeHelper.GetDpi(this);RenderTargetBitmap rtb=new RenderTargetBitmap((int)(cvs.ActualWidth*dpi.DpiScaleY), (int)(cvs.ActualHeight*dpi.DpiScaleY),dpi.PixelsPerInchX,dpi.PixelsPerInchY,PixelFormats.Pbgra32);rtb.Render(cvs);SaveFileDialog dialog = new SaveFileDialog();dialog.Filter = $"JPG Files|*.jpg";dialog.FileName = $"Canvas{DateTime.Now.ToString("yyyyMMddHHmmssffff")}.jpg";if(dialog.ShowDialog()==true){using (FileStream fs = new FileStream(dialog.FileName, FileMode.Create)){JpegBitmapEncoder encoder = new JpegBitmapEncoder();encoder.Frames.Add(BitmapFrame.Create(rtb));encoder.Save(fs);MessageBox.Show($"Saved in {dialog.FileName}!");}}            }private void MainWindow_SizeChanged(object sender, SizeChangedEventArgs e){if (img != null && cvs.ActualWidth > 0 && cvs.ActualHeight > 0){img.Width = cvs.ActualWidth;img.Height = cvs.ActualHeight;}}private void MainWindow_Loaded(object sender, RoutedEventArgs e){InitImg();}private void InitImg(){BitmapImage bmi = new BitmapImage();bmi.BeginInit();bmi.UriSource = new Uri(@"../../../Images/1.jpg", UriKind.RelativeOrAbsolute);bmi.EndInit();bmi.Freeze();img = new Image();img.Stretch = Stretch.UniformToFill;img.Source = bmi;img.Width = cvs.ActualWidth;img.Height = cvs.ActualHeight;// Add to canvas at position (0,0)Canvas.SetLeft(img, 0);Canvas.SetTop(img, 0);// Ensure image is at the back (z-index)Panel.SetZIndex(img, -1);cvs.Children.Add(img);}private void cvs_MouseLeftButtonDown(object sender, MouseButtonEventArgs e){var pt = Mouse.GetPosition(cvs);if (triangleChx.IsChecked == true){DrawTriangle(pt);}else if (circleChx.IsChecked == true){DrawCircle(pt);}else if (rectangleChx.IsChecked == true){DrawRectangle(pt);}}private void DrawRectangle(Point pt){Rectangle rect = new Rectangle();rect.Fill = new SolidColorBrush(Colors.Blue);rect.Stroke = Brushes.Black;rect.StrokeThickness = 2;rect.Width = sideLength;rect.Height = sideLength;rect.ToolTip = $"{++idx},Center.X:{pt.X},Center.Y:{pt.Y}";Canvas.SetLeft(rect, pt.X - sideLength / 2);Canvas.SetTop(rect, pt.Y - sideLength / 2);cvs.Children.Add(rect);}private void DrawCircle(Point pt){Ellipse elp = new Ellipse();elp.Width = sideLength;elp.Height = sideLength;elp.Stroke = Brushes.Black;elp.StrokeThickness = 2;elp.Fill = new SolidColorBrush(Colors.Cyan);elp.ToolTip = $"{++idx},Center.X:{pt.X},Center.Y:{pt.Y}";Canvas.SetLeft(elp, pt.X - sideLength / 2);Canvas.SetTop(elp, pt.Y - sideLength / 2);cvs.Children.Add(elp);}private void DrawTriangle(Point centerPt){try{double centerX = centerPt.X;double centerY = centerPt.Y;double height = (Math.Sqrt(3) / 2) * sideLength;Point topPt = new Point(centerX, centerY - height / 2);Point leftPt = new Point(centerX - sideLength / 2, centerY + height / 2);Point rightPt = new Point(centerX + sideLength / 2, centerY + height / 2);var triangle = new Polygon(){Stroke = Brushes.Black,StrokeThickness = 2,Fill = Brushes.Red};triangle.Points.Add(topPt);triangle.Points.Add(leftPt);triangle.Points.Add(rightPt);triangle.ToolTip = $"Id:{++idx},Center.X:{centerPt.X},Center.Y:{centerPt.Y}";CreateTriagnleWithContextMenu(triangle);cvs.Children.Add(triangle);}catch (Exception ex){MessageBox.Show(ex.Message);}}private void CreateTriagnleWithContextMenu(Polygon triangle){ContextMenu contextMenu = new ContextMenu();//Save As Picture menu item.MenuItem saveMenuItem = new MenuItem();saveMenuItem.Header = "Save As Picture";saveMenuItem.Tag = triangle;saveMenuItem.Click += (s, e) => SaveTriangleAsPicture(s, e);//RemoveMenuItem removeItem = new MenuItem();removeItem.Header = "Remove Triangle";removeItem.Tag = triangle;removeItem.Click += (s, e) => RemoveTriangle(s, e);contextMenu.Items.Add(saveMenuItem);contextMenu.Items.Add(removeItem);triangle.ContextMenu = contextMenu;triangle.MouseRightButtonDown += (s, e) =>{triangle.ContextMenu.IsOpen = true;e.Handled = true;};}private void RemoveTriangle(object sender, RoutedEventArgs e){try{var triangle = (sender as MenuItem)?.Tag as Polygon;if (triangle != null){cvs.Children.Remove(triangle);triangle = null;MessageBox.Show("Triangle has been removed!");}}catch (Exception ex){MessageBox.Show(ex.Message);}}private void SaveTriangleAsPicture(object sender, RoutedEventArgs e){var triangle = (sender as MenuItem)?.Tag as Polygon;if (triangle == null){MessageBox.Show("No triangle to save!");return;}try{SaveFileDialog dialog = new SaveFileDialog(){Filter = "PNG Image|*.png|Jpeg Image|*.jpg|BMP Image|*.bmp",Title = "Save Triangle as Picture",FileName = $"Triangle_{DateTime.Now:yyyyMMddHHmmssffff}.png"};if (dialog.ShowDialog() == true){BitmapEncoder bmpEncoder = System.IO.Path.GetExtension(dialog.FileName).ToLower() switch{".jpg" or ".jpeg" => new JpegBitmapEncoder(),".bmp" => new BmpBitmapEncoder(),_ => new PngBitmapEncoder()};// Calculate the bounds of the triangleRect bounds = GetTriangleBounds(triangle);// Add some padding around the triangledouble padding = 10;bounds = new Rect(bounds.X - padding,bounds.Y - padding,bounds.Width + padding * 2,bounds.Height + padding * 2);// Create a visual for the triangle onlyDrawingVisual drawingVisual = new DrawingVisual();using (DrawingContext drawingContext = drawingVisual.RenderOpen()){// Draw white backgrounddrawingContext.DrawRectangle(Brushes.White, null, bounds);// Draw the triangle
                        DrawTriangleGeometry(drawingContext, triangle, bounds);}var dpi = VisualTreeHelper.GetDpi(this);// Render the visual to bitmapRenderTargetBitmap rtb = new RenderTargetBitmap((int)(bounds.Width * dpi.DpiScaleX),(int)(bounds.Height * dpi.DpiScaleY),dpi.PixelsPerInchX,dpi.PixelsPerInchY,PixelFormats.Pbgra32);rtb.Render(drawingVisual);// Save to fileusing (FileStream fileStream = new FileStream(dialog.FileName, FileMode.Create)){bmpEncoder.Frames.Add(BitmapFrame.Create(rtb));bmpEncoder.Save(fileStream);}MessageBox.Show($"Triangle saved as {dialog.FileName} successfully!");}}catch (Exception ex){MessageBox.Show(ex.Message);}}private Rect GetTriangleBounds(Polygon triangle){if (triangle.Points.Count == 0){return new Rect(0, 0, sideLength, sideLength);}double minX = triangle.Points[0].X;double maxX = triangle.Points[0].X;double minY = triangle.Points[0].Y;double maxY = triangle.Points[0].Y;foreach (Point point in triangle.Points){if (point.X < minX){minX = point.X;}if (point.X > maxX){maxX = point.X;}if (point.Y < minY){minY = point.Y;}if (point.Y > maxY){maxY = point.Y;}}return new Rect(minX, minY, maxX - minX, maxY - minY);}private void DrawTriangleGeometry(DrawingContext drawingContext,Polygon triangle, Rect bounds){// Create a stream geometry for the triangleStreamGeometry geometry = new StreamGeometry();using (StreamGeometryContext streamGeoContext = geometry.Open()){streamGeoContext.BeginFigure(triangle.Points[0], true, true);streamGeoContext.PolyLineTo(new List<Point>(triangle.Points), true, true);}// Apply translation to position the triangle correctlyTransformGroup transformGroup = new TransformGroup();transformGroup.Children.Add(new TranslateTransform(-bounds.X, -bounds.Y));geometry.Transform = transformGroup;geometry.Freeze();// Draw the triangle with the same properties as the originaldrawingContext.DrawGeometry(triangle.Fill, new Pen(triangle.Stroke,triangle.StrokeThickness), geometry);}private void triangleCbx_Checked(object sender, RoutedEventArgs e){circleChx.IsChecked = false;rectangleChx.IsChecked = false;}private void circleCbx_Checked(object sender, RoutedEventArgs e){triangleChx.IsChecked = false;rectangleChx.IsChecked = false;}private void rectangleChx_Checked(object sender, RoutedEventArgs e){circleChx.IsChecked = false;triangleChx.IsChecked = false;}}
}