|
@@ -3,17 +3,86 @@ using Avalonia.Controls;
|
|
|
using Avalonia.Controls.Shapes;
|
|
|
using Avalonia.Data;
|
|
|
using Avalonia.Input;
|
|
|
+using Avalonia.Interactivity;
|
|
|
using Avalonia.Markup.Xaml;
|
|
|
using Avalonia.Media;
|
|
|
+using Avalonia.Media.Imaging;
|
|
|
+using Avalonia.Skia.Helpers;
|
|
|
using CommunityToolkit.Mvvm.Input;
|
|
|
+using FluentResults;
|
|
|
using InABox.Avalonia.Components.ImageEditing;
|
|
|
+using InABox.Avalonia.Converters;
|
|
|
+using InABox.Core;
|
|
|
+using SkiaSharp;
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
|
|
namespace InABox.Avalonia.Components;
|
|
|
|
|
|
public enum ImageEditingMode
|
|
|
{
|
|
|
- Polyline
|
|
|
+ Polyline,
|
|
|
+ Rectangle,
|
|
|
+ Ellipse
|
|
|
+}
|
|
|
+
|
|
|
+public class ImageEditorModeButton(ImageEditingMode mode, Canvas? canvas)
|
|
|
+{
|
|
|
+ public ImageEditingMode Mode { get; set; } = mode;
|
|
|
+
|
|
|
+ public Canvas? Canvas { get; set; } = canvas;
|
|
|
+}
|
|
|
+
|
|
|
+public class ImageEditorTransparentImageBrushConverter : AbstractConverter<IBrush?, IBrush?>
|
|
|
+{
|
|
|
+ public static readonly ImageEditorTransparentImageBrushConverter Instance = new ImageEditorTransparentImageBrushConverter();
|
|
|
+
|
|
|
+ protected override IBrush? Convert(IBrush? value, object? parameter = null)
|
|
|
+ {
|
|
|
+ if (value is SolidColorBrush solid && solid.Color.A == 255) return solid;
|
|
|
+
|
|
|
+ var brush = new VisualBrush
|
|
|
+ {
|
|
|
+ TileMode = TileMode.Tile,
|
|
|
+ DestinationRect = new(0, 0, 10, 10, RelativeUnit.Absolute)
|
|
|
+ };
|
|
|
+ var canvas = new Canvas
|
|
|
+ {
|
|
|
+ Width = 10,
|
|
|
+ Height = 10
|
|
|
+ };
|
|
|
+ var rect1 = new Rectangle { Width = 5, Height = 5 };
|
|
|
+ var rect2 = new Rectangle { Width = 5, Height = 5 };
|
|
|
+
|
|
|
+ Canvas.SetLeft(rect2, 5);
|
|
|
+ Canvas.SetTop(rect2, 5);
|
|
|
+
|
|
|
+ rect1.Fill = new SolidColorBrush(Colors.LightGray);
|
|
|
+ rect2.Fill = new SolidColorBrush(Colors.LightGray);
|
|
|
+
|
|
|
+ var rect3 = new Rectangle { Width = 10, Height = 10 };
|
|
|
+ rect3.Fill = value;
|
|
|
+
|
|
|
+ canvas.Children.Add(rect1);
|
|
|
+ canvas.Children.Add(rect2);
|
|
|
+ canvas.Children.Add(rect3);
|
|
|
+
|
|
|
+ brush.Visual = canvas;
|
|
|
+ return brush;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+public class ImageEditorRemoveOpacityConverter : AbstractConverter<IBrush?, IBrush?>
|
|
|
+{
|
|
|
+ public static readonly ImageEditorRemoveOpacityConverter Instance = new();
|
|
|
+
|
|
|
+ protected override IBrush? Convert(IBrush? value, object? parameter = null)
|
|
|
+ {
|
|
|
+ if (value is SolidColorBrush solid)
|
|
|
+ {
|
|
|
+ return new SolidColorBrush(new Color(255, solid.Color.R, solid.Color.G, solid.Color.B));
|
|
|
+ }
|
|
|
+ return value;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// TODO: Make it so we don't re-render everything everytime 'Objects' changes.
|
|
@@ -26,15 +95,55 @@ public partial class ImageEditor : UserControl
|
|
|
public static readonly StyledProperty<IBrush?> PrimaryBrushProperty =
|
|
|
AvaloniaProperty.Register<ImageEditor, IBrush?>(nameof(PrimaryBrush), new SolidColorBrush(Colors.Black));
|
|
|
|
|
|
+ public static readonly StyledProperty<IBrush?> SecondaryBrushProperty =
|
|
|
+ AvaloniaProperty.Register<ImageEditor, IBrush?>(nameof(SecondaryBrush), new SolidColorBrush(Colors.White));
|
|
|
+
|
|
|
+ public static readonly StyledProperty<double> LineThicknessProperty =
|
|
|
+ AvaloniaProperty.Register<ImageEditor, double>(nameof(LineThickness), 3.0);
|
|
|
+
|
|
|
+ public static readonly StyledProperty<int> ImageWidthProperty =
|
|
|
+ AvaloniaProperty.Register<ImageEditor, int>(nameof(ImageWidth), 100);
|
|
|
+
|
|
|
+ public static readonly StyledProperty<int> ImageHeightProperty =
|
|
|
+ AvaloniaProperty.Register<ImageEditor, int>(nameof(ImageHeight), 100);
|
|
|
+
|
|
|
+ public static readonly StyledProperty<ImageEditingMode> ModeProperty =
|
|
|
+ AvaloniaProperty.Register<ImageEditor, ImageEditingMode>(nameof(Mode), ImageEditingMode.Polyline);
|
|
|
+
|
|
|
+ public static readonly StyledProperty<bool> ShowButtonsProperty =
|
|
|
+ AvaloniaProperty.Register<ImageEditor, bool>(nameof(ShowButtons), true);
|
|
|
+
|
|
|
public IImage? Source
|
|
|
{
|
|
|
get => GetValue(SourceProperty);
|
|
|
set => SetValue(SourceProperty, value);
|
|
|
}
|
|
|
|
|
|
+ public int ImageWidth
|
|
|
+ {
|
|
|
+ get => GetValue(ImageWidthProperty);
|
|
|
+ set => SetValue(ImageWidthProperty, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ public int ImageHeight
|
|
|
+ {
|
|
|
+ get => GetValue(ImageHeightProperty);
|
|
|
+ set => SetValue(ImageHeightProperty, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ public bool ShowButtons
|
|
|
+ {
|
|
|
+ get => GetValue(ShowButtonsProperty);
|
|
|
+ set => SetValue(ShowButtonsProperty, value);
|
|
|
+ }
|
|
|
+
|
|
|
#region Editing Properties
|
|
|
|
|
|
- public ImageEditingMode Mode { get; set; } = ImageEditingMode.Polyline;
|
|
|
+ public ImageEditingMode Mode
|
|
|
+ {
|
|
|
+ get => GetValue(ModeProperty);
|
|
|
+ set => SetValue(ModeProperty, value);
|
|
|
+ }
|
|
|
|
|
|
public IBrush? PrimaryBrush
|
|
|
{
|
|
@@ -42,50 +151,173 @@ public partial class ImageEditor : UserControl
|
|
|
set => SetValue(PrimaryBrushProperty, value);
|
|
|
}
|
|
|
|
|
|
- public double LineThickness { get; set; } = 1.0;
|
|
|
+ public IBrush? SecondaryBrush
|
|
|
+ {
|
|
|
+ get => GetValue(SecondaryBrushProperty);
|
|
|
+ set => SetValue(SecondaryBrushProperty, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ public double LineThickness
|
|
|
+ {
|
|
|
+ get => GetValue(LineThicknessProperty);
|
|
|
+ set => SetValue(LineThicknessProperty, value);
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Events
|
|
|
+
|
|
|
+ public event EventHandler? Changed;
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
+ #region Private Properties
|
|
|
+
|
|
|
+ public ObservableCollection<ImageEditorModeButton> ModeButtons { get; set; } = new();
|
|
|
+
|
|
|
private ObservableCollection<IImageEditorObject> Objects = new();
|
|
|
|
|
|
private IImageEditorObject? CurrentObject;
|
|
|
|
|
|
private Stack<IImageEditorObject> RedoStack = new();
|
|
|
|
|
|
+ private double ScaleFactor = 1.0;
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ static ImageEditor()
|
|
|
+ {
|
|
|
+ SourceProperty.Changed.AddClassHandler<ImageEditor>(Source_Changed);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void Source_Changed(ImageEditor editor, AvaloniaPropertyChangedEventArgs args)
|
|
|
+ {
|
|
|
+ if(editor.Source is not null)
|
|
|
+ {
|
|
|
+ editor.ImageWidth = (int)Math.Floor(editor.Source.Size.Width);
|
|
|
+ editor.ImageHeight = (int)Math.Floor(editor.Source.Size.Height);
|
|
|
+ editor.PositionImage();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
public ImageEditor()
|
|
|
{
|
|
|
InitializeComponent();
|
|
|
|
|
|
Objects.CollectionChanged += Objects_CollectionChanged;
|
|
|
|
|
|
+ AddModeButtons();
|
|
|
+
|
|
|
SetMode(Mode);
|
|
|
+
|
|
|
+ OuterCanvas.LayoutUpdated += OuterCanvas_LayoutUpdated;
|
|
|
}
|
|
|
|
|
|
+ #region Layout
|
|
|
+
|
|
|
+ private void OuterCanvas_LayoutUpdated(object? sender, EventArgs e)
|
|
|
+ {
|
|
|
+ PositionImage();
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override void OnLoaded(RoutedEventArgs e)
|
|
|
+ {
|
|
|
+ base.OnLoaded(e);
|
|
|
+ PositionImage();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void PositionImage()
|
|
|
+ {
|
|
|
+ var canvasWidth = OuterCanvas.Bounds.Width;
|
|
|
+ var canvasHeight = OuterCanvas.Bounds.Height;
|
|
|
+
|
|
|
+ var scaleFactor = Math.Min(canvasWidth / ImageWidth, canvasHeight / ImageHeight);
|
|
|
+ ScaleFactor = scaleFactor;
|
|
|
+
|
|
|
+ var imageWidth = ImageWidth * scaleFactor;
|
|
|
+ var imageHeight = ImageHeight * scaleFactor;
|
|
|
+
|
|
|
+ ImageBorder.Width = imageWidth;
|
|
|
+ ImageBorder.Height = imageHeight;
|
|
|
+
|
|
|
+ Canvas.SetLeft(ImageBorder, OuterCanvas.Bounds.Width / 2 - imageWidth / 2);
|
|
|
+ Canvas.SetTop(ImageBorder, OuterCanvas.Bounds.Height / 2 - imageHeight / 2);
|
|
|
+
|
|
|
+ Canvas.RenderTransform = new ScaleTransform(ScaleFactor, ScaleFactor);
|
|
|
+ Canvas.Width = ImageWidth;
|
|
|
+ Canvas.Height = ImageHeight;
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
#region Editing Commands
|
|
|
|
|
|
+ private void UpdateUndoRedoButtons()
|
|
|
+ {
|
|
|
+ UndoButton.IsEnabled = Objects.Count > 0;
|
|
|
+ RedoButton.IsEnabled = RedoStack.Count > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ [RelayCommand]
|
|
|
private void Undo()
|
|
|
{
|
|
|
+ if (Objects.Count == 0) return;
|
|
|
+
|
|
|
RedoStack.Push(Objects[^1]);
|
|
|
Objects.RemoveAt(Objects.Count - 1);
|
|
|
+ UpdateUndoRedoButtons();
|
|
|
+ Changed?.Invoke(this, new EventArgs());
|
|
|
}
|
|
|
|
|
|
+ [RelayCommand]
|
|
|
private void Redo()
|
|
|
{
|
|
|
if (!RedoStack.TryPop(out var top)) return;
|
|
|
|
|
|
Objects.Add(top);
|
|
|
+ UpdateUndoRedoButtons();
|
|
|
+ Changed?.Invoke(this, new EventArgs());
|
|
|
}
|
|
|
|
|
|
private void AddObject(IImageEditorObject obj)
|
|
|
{
|
|
|
Objects.Add(obj);
|
|
|
RedoStack.Clear();
|
|
|
+ UpdateUndoRedoButtons();
|
|
|
+ Changed?.Invoke(this, new EventArgs());
|
|
|
}
|
|
|
|
|
|
[RelayCommand]
|
|
|
private void SetMode(ImageEditingMode mode)
|
|
|
{
|
|
|
Mode = mode;
|
|
|
+ ShapeButton.Content = CreateModeButtonContent(mode);
|
|
|
+ SecondaryColour.IsVisible = HasSecondaryColour();
|
|
|
+ }
|
|
|
+
|
|
|
+ private bool HasSecondaryColour()
|
|
|
+ {
|
|
|
+ return Mode == ImageEditingMode.Rectangle || Mode == ImageEditingMode.Ellipse;
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Mode Buttons
|
|
|
+
|
|
|
+ private void AddModeButtons()
|
|
|
+ {
|
|
|
+ AddModeButton(ImageEditingMode.Polyline);
|
|
|
+ AddModeButton(ImageEditingMode.Rectangle);
|
|
|
+ AddModeButton(ImageEditingMode.Ellipse);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void AddModeButton(ImageEditingMode mode)
|
|
|
+ {
|
|
|
+ ModeButtons.Add(new(mode, CreateModeButtonContent(mode)));
|
|
|
+ }
|
|
|
+
|
|
|
+ private Canvas? CreateModeButtonContent(ImageEditingMode mode, bool bindColour = false)
|
|
|
+ {
|
|
|
switch (mode)
|
|
|
{
|
|
|
case ImageEditingMode.Polyline:
|
|
@@ -95,18 +327,139 @@ public partial class ImageEditor : UserControl
|
|
|
var line2 = new Polyline { Points = points, Width = 25, Height = 25 };
|
|
|
line1.StrokeThickness = 4;
|
|
|
line1.StrokeLineCap = PenLineCap.Round;
|
|
|
- line2.StrokeThickness = 1.5;
|
|
|
+ line1.StrokeJoin = PenLineJoin.Round;
|
|
|
line1.Stroke = new SolidColorBrush(Colors.Black);
|
|
|
- line2.Bind(Polyline.StrokeProperty, new Binding(nameof(PrimaryBrush)) { Source = this });
|
|
|
canvas.Children.Add(line1);
|
|
|
- canvas.Children.Add(line2);
|
|
|
- ShapeButton.Content = canvas;
|
|
|
- break;
|
|
|
+
|
|
|
+ if (bindColour)
|
|
|
+ {
|
|
|
+ line1.StrokeThickness = 5;
|
|
|
+ line2.StrokeThickness = 4;
|
|
|
+ line2.StrokeLineCap = PenLineCap.Round;
|
|
|
+ line2.StrokeJoin = PenLineJoin.Round;
|
|
|
+ line2.Bind(Polyline.StrokeProperty, new Binding(nameof(PrimaryBrush))
|
|
|
+ {
|
|
|
+ Source = this,
|
|
|
+ Converter = ImageEditorRemoveOpacityConverter.Instance
|
|
|
+ });
|
|
|
+ canvas.Children.Add(line2);
|
|
|
+ }
|
|
|
+ return canvas;
|
|
|
+ case ImageEditingMode.Rectangle:
|
|
|
+ canvas = new Canvas();
|
|
|
+ canvas.Width = 25;
|
|
|
+ canvas.Height = 25;
|
|
|
+
|
|
|
+ var rectangle = new Rectangle();
|
|
|
+ if (bindColour)
|
|
|
+ {
|
|
|
+ rectangle.Bind(Rectangle.StrokeProperty, new Binding(nameof(PrimaryBrush))
|
|
|
+ {
|
|
|
+ Source = this,
|
|
|
+ Converter = ImageEditorRemoveOpacityConverter.Instance
|
|
|
+ });
|
|
|
+ rectangle.Bind(Rectangle.FillProperty, new Binding(nameof(SecondaryBrush))
|
|
|
+ {
|
|
|
+ Source = this,
|
|
|
+ Converter = ImageEditorTransparentImageBrushConverter.Instance
|
|
|
+ });
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ rectangle.Stroke = new SolidColorBrush(Colors.Black);
|
|
|
+ rectangle.Fill = new SolidColorBrush(Colors.White);
|
|
|
+ }
|
|
|
+ rectangle.StrokeThickness = 1.0;
|
|
|
+ rectangle.Width = 25;
|
|
|
+ rectangle.Height = 25;
|
|
|
+ canvas.Children.Add(rectangle);
|
|
|
+
|
|
|
+ return canvas;
|
|
|
+ case ImageEditingMode.Ellipse:
|
|
|
+ canvas = new Canvas();
|
|
|
+ canvas.Width = 25;
|
|
|
+ canvas.Height = 25;
|
|
|
+
|
|
|
+ var ellipse = new Ellipse();
|
|
|
+ if (bindColour)
|
|
|
+ {
|
|
|
+ ellipse.Bind(Rectangle.StrokeProperty, new Binding(nameof(PrimaryBrush))
|
|
|
+ {
|
|
|
+ Source = this,
|
|
|
+ Converter = ImageEditorRemoveOpacityConverter.Instance
|
|
|
+ });
|
|
|
+ ellipse.Bind(Rectangle.FillProperty, new Binding(nameof(SecondaryBrush))
|
|
|
+ {
|
|
|
+ Source = this,
|
|
|
+ Converter = ImageEditorTransparentImageBrushConverter.Instance
|
|
|
+ });
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ ellipse.Stroke = new SolidColorBrush(Colors.Black);
|
|
|
+ ellipse.Fill = new SolidColorBrush(Colors.White);
|
|
|
+ }
|
|
|
+ ellipse.StrokeThickness = 1.0;
|
|
|
+ ellipse.Width = 25;
|
|
|
+ ellipse.Height = 25;
|
|
|
+ canvas.Children.Add(ellipse);
|
|
|
+
|
|
|
+ return canvas;
|
|
|
+ default:
|
|
|
+ return null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
+ #region Public Interface
|
|
|
+
|
|
|
+ public void Reset()
|
|
|
+ {
|
|
|
+ Objects.Clear();
|
|
|
+ RedoStack.Clear();
|
|
|
+ UpdateUndoRedoButtons();
|
|
|
+ Changed?.Invoke(this, new EventArgs());
|
|
|
+ }
|
|
|
+
|
|
|
+ public Bitmap GetImage()
|
|
|
+ {
|
|
|
+ var renderBitmap = new RenderTargetBitmap(new PixelSize(ImageWidth, ImageHeight));
|
|
|
+ renderBitmap.Render(Image);
|
|
|
+ using var context = renderBitmap.CreateDrawingContext();
|
|
|
+ if(Source is not null)
|
|
|
+ {
|
|
|
+ context.DrawImage(Source, new(0, 0, ImageWidth, ImageHeight));
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (var obj in Objects)
|
|
|
+ {
|
|
|
+ var control = obj.GetControl();
|
|
|
+ var left = Canvas.GetLeft(control);
|
|
|
+ var top = Canvas.GetTop(control);
|
|
|
+ if (double.IsNaN(left)) left = 0;
|
|
|
+ if (double.IsNaN(top)) top = 0;
|
|
|
+ using (context.PushTransform(Matrix.CreateTranslation(new(left, top))))
|
|
|
+ {
|
|
|
+ control.Render(context);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return renderBitmap;
|
|
|
+ }
|
|
|
+
|
|
|
+ public byte[] SaveImage()
|
|
|
+ {
|
|
|
+ var bitmap = GetImage();
|
|
|
+
|
|
|
+ var stream = new MemoryStream();
|
|
|
+ bitmap.Save(stream);
|
|
|
+ return stream.ToArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Editing
|
|
|
+
|
|
|
private void Objects_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
|
|
{
|
|
|
Canvas.Children.Clear();
|
|
@@ -119,7 +472,7 @@ public partial class ImageEditor : UserControl
|
|
|
|
|
|
Point ConvertToImageCoordinates(Point canvasCoordinates)
|
|
|
{
|
|
|
- return canvasCoordinates;
|
|
|
+ return canvasCoordinates;// new(canvasCoordinates.X / ScaleFactor, canvasCoordinates.Y / ScaleFactor);
|
|
|
}
|
|
|
|
|
|
private void Canvas_PointerPressed(object? sender, PointerPressedEventArgs e)
|
|
@@ -136,28 +489,80 @@ public partial class ImageEditor : UserControl
|
|
|
};
|
|
|
AddObject(CurrentObject);
|
|
|
break;
|
|
|
+ case ImageEditingMode.Rectangle:
|
|
|
+ CurrentObject = new RectangleObject
|
|
|
+ {
|
|
|
+ Point1 = position,
|
|
|
+ Point2 = position,
|
|
|
+ PrimaryBrush = PrimaryBrush,
|
|
|
+ SecondaryBrush = SecondaryBrush,
|
|
|
+ Thickness = LineThickness
|
|
|
+ };
|
|
|
+ AddObject(CurrentObject);
|
|
|
+ break;
|
|
|
+ case ImageEditingMode.Ellipse:
|
|
|
+ CurrentObject = new EllipseObject
|
|
|
+ {
|
|
|
+ Point1 = position,
|
|
|
+ Point2 = position,
|
|
|
+ PrimaryBrush = PrimaryBrush,
|
|
|
+ SecondaryBrush = SecondaryBrush,
|
|
|
+ Thickness = LineThickness
|
|
|
+ };
|
|
|
+ AddObject(CurrentObject);
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void Canvas_PointerMoved(object? sender, PointerEventArgs e)
|
|
|
{
|
|
|
var position = ConvertToImageCoordinates(e.GetPosition(Canvas));
|
|
|
- if(CurrentObject is PolylineObject polyline)
|
|
|
+ switch (CurrentObject)
|
|
|
{
|
|
|
- polyline.Points.Add(position);
|
|
|
- polyline.Update();
|
|
|
+ case PolylineObject polyline:
|
|
|
+ polyline.Points.Add(position);
|
|
|
+ polyline.Update();
|
|
|
+ Changed?.Invoke(this, new EventArgs());
|
|
|
+ break;
|
|
|
+ case RectangleObject rectangle:
|
|
|
+ rectangle.Point2 = position;
|
|
|
+ rectangle.Update();
|
|
|
+ Changed?.Invoke(this, new EventArgs());
|
|
|
+ break;
|
|
|
+ case EllipseObject ellipse:
|
|
|
+ ellipse.Point2 = position;
|
|
|
+ ellipse.Update();
|
|
|
+ Changed?.Invoke(this, new EventArgs());
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private void Canvas_PointerReleased(object? sender, PointerReleasedEventArgs e)
|
|
|
{
|
|
|
var position = ConvertToImageCoordinates(e.GetPosition(Canvas));
|
|
|
- if(CurrentObject is PolylineObject polyline)
|
|
|
+ switch (CurrentObject)
|
|
|
{
|
|
|
- polyline.Points.Add(position);
|
|
|
- polyline.Update();
|
|
|
- CurrentObject = null;
|
|
|
+ case PolylineObject polyline:
|
|
|
+ polyline.Points.Add(position);
|
|
|
+ polyline.Update();
|
|
|
+ CurrentObject = null;
|
|
|
+ Changed?.Invoke(this, new EventArgs());
|
|
|
+ break;
|
|
|
+ case RectangleObject rectangle:
|
|
|
+ rectangle.Point2 = position;
|
|
|
+ rectangle.Update();
|
|
|
+ CurrentObject = null;
|
|
|
+ Changed?.Invoke(this, new EventArgs());
|
|
|
+ break;
|
|
|
+ case EllipseObject ellipse:
|
|
|
+ ellipse.Point2 = position;
|
|
|
+ ellipse.Update();
|
|
|
+ CurrentObject = null;
|
|
|
+ Changed?.Invoke(this, new EventArgs());
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
+
|
|
|
+ #endregion
|
|
|
}
|