//Window.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"/>
</Grid>
</Window>
//Window.xaml.cs
using Microsoft.Win32;
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;
public MainWindow()
{
InitializeComponent();
}
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);
//Remove
MenuItem 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 triangle
Rect bounds = GetTriangleBounds(triangle);
// Add some padding around the triangle
double 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 only
DrawingVisual drawingVisual = new DrawingVisual();
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
// Draw white background
drawingContext.DrawRectangle(Brushes.White, null, bounds);
// Draw the triangle
DrawTriangleGeometry(drawingContext, triangle, bounds);
}
var dpi = VisualTreeHelper.GetDpi(this);
// Render the visual to bitmap
RenderTargetBitmap rtb = new RenderTargetBitmap(
(int)(bounds.Width * dpi.DpiScaleX),
(int)(bounds.Height * dpi.DpiScaleY),
dpi.PixelsPerInchX,
dpi.PixelsPerInchY,
PixelFormats.Pbgra32);
rtb.Render(drawingVisual);
// Save to file
using (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 triangle
StreamGeometry 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 correctly
TransformGroup 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 original
drawingContext.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;
}
}
}