diff --git a/AIStudio.Wpf.DiagramDesigner/Controls/DesignerCanvas.cs b/AIStudio.Wpf.DiagramDesigner/Controls/DesignerCanvas.cs
index 715a65a..44f2743 100644
--- a/AIStudio.Wpf.DiagramDesigner/Controls/DesignerCanvas.cs
+++ b/AIStudio.Wpf.DiagramDesigner/Controls/DesignerCanvas.cs
@@ -5,6 +5,7 @@ using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
+using System.Windows.Media.Media3D;
using System.Windows.Resources;
using AIStudio.Wpf.DiagramDesigner.Helpers;
using AIStudio.Wpf.DiagramDesigner.Models;
@@ -220,6 +221,48 @@ namespace AIStudio.Wpf.DiagramDesigner
#endregion
+ #region AutoGrowth
+
+ public static readonly DependencyProperty AutoGrowthProperty =
+ DependencyProperty.Register(nameof(AutoGrowth),
+ typeof(bool),
+ typeof(DesignerCanvas),
+ new FrameworkPropertyMetadata(true));
+
+ public bool AutoGrowth
+ {
+ get
+ {
+ return (bool)GetValue(AutoGrowthProperty);
+ }
+ set
+ {
+ SetValue(AutoGrowthProperty, value);
+ }
+ }
+ #endregion
+
+ #region CornerRadius
+
+ public static readonly DependencyProperty CornerRadiusProperty =
+ DependencyProperty.Register(nameof(CornerRadius),
+ typeof(CornerRadius),
+ typeof(DesignerCanvas),
+ new FrameworkPropertyMetadata(new CornerRadius()));
+
+ public CornerRadius CornerRadius
+ {
+ get
+ {
+ return (CornerRadius)GetValue(CornerRadiusProperty);
+ }
+ set
+ {
+ SetValue(CornerRadiusProperty, value);
+ }
+ }
+ #endregion
+
#region GridMarginSize 单位mm
public static readonly DependencyProperty GridMarginSizeProperty =
@@ -291,6 +334,164 @@ namespace AIStudio.Wpf.DiagramDesigner
DrawGrid(dc, rect);
}
+ public static Geometry GetRoundRectangle(Rect baseRect, Thickness borderThickness, CornerRadius cornerRadius)
+ {
+ // Normalizing the corner radius
+ if (cornerRadius.TopLeft < double.Epsilon)
+ {
+ cornerRadius.TopLeft = 0.0;
+ }
+
+ if (cornerRadius.TopRight < double.Epsilon)
+ {
+ cornerRadius.TopRight = 0.0;
+ }
+
+ if (cornerRadius.BottomLeft < double.Epsilon)
+ {
+ cornerRadius.BottomLeft = 0.0;
+ }
+
+ if (cornerRadius.BottomRight < double.Epsilon)
+ {
+ cornerRadius.BottomRight = 0.0;
+ }
+
+ // Taking the border thickness into account
+ var leftHalf = borderThickness.Left * 0.5;
+ if (leftHalf < double.Epsilon)
+ {
+ leftHalf = 0.0;
+ }
+
+ var topHalf = borderThickness.Top * 0.5;
+ if (topHalf < double.Epsilon)
+ {
+ topHalf = 0.0;
+ }
+
+ var rightHalf = borderThickness.Right * 0.5;
+ if (rightHalf < double.Epsilon)
+ {
+ rightHalf = 0.0;
+ }
+
+ var bottomHalf = borderThickness.Bottom * 0.5;
+ if (bottomHalf < double.Epsilon)
+ {
+ bottomHalf = 0.0;
+ }
+
+ // Create the rectangles for the corners that needs to be curved in the base rectangle
+ // TopLeft Rectangle
+ var topLeftRect = new Rect(
+ baseRect.Location.X,
+ baseRect.Location.Y,
+ Math.Max(0.0, cornerRadius.TopLeft - leftHalf),
+ Math.Max(0.0, cornerRadius.TopLeft - rightHalf));
+
+ // TopRight Rectangle
+ var topRightRect = new Rect(
+ baseRect.Location.X + baseRect.Width - cornerRadius.TopRight + rightHalf,
+ baseRect.Location.Y,
+ Math.Max(0.0, cornerRadius.TopRight - rightHalf),
+ Math.Max(0.0, cornerRadius.TopRight - topHalf));
+
+ // BottomRight Rectangle
+ var bottomRightRect = new Rect(
+ baseRect.Location.X + baseRect.Width - cornerRadius.BottomRight + rightHalf,
+ baseRect.Location.Y + baseRect.Height - cornerRadius.BottomRight + bottomHalf,
+ Math.Max(0.0, cornerRadius.BottomRight - rightHalf),
+ Math.Max(0.0, cornerRadius.BottomRight - bottomHalf));
+
+ // BottomLeft Rectangle
+ var bottomLeftRect = new Rect(
+ baseRect.Location.X,
+ baseRect.Location.Y + baseRect.Height - cornerRadius.BottomLeft + bottomHalf,
+ Math.Max(0.0, cornerRadius.BottomLeft - leftHalf),
+ Math.Max(0.0, cornerRadius.BottomLeft - bottomHalf));
+
+ // Adjust the _width of the TopLeft and TopRight rectangles so that they are proportional to the _width of the baseRect
+ if (topLeftRect.Right > topRightRect.Left)
+ {
+ var newWidth = (topLeftRect.Width / (topLeftRect.Width + topRightRect.Width)) * baseRect.Width;
+ topLeftRect = new Rect(topLeftRect.Location.X, topLeftRect.Location.Y, newWidth, topLeftRect.Height);
+ topRightRect = new Rect(
+ baseRect.Left + newWidth,
+ topRightRect.Location.Y,
+ Math.Max(0.0, baseRect.Width - newWidth),
+ topRightRect.Height);
+ }
+
+ // Adjust the height of the TopRight and BottomRight rectangles so that they are proportional to the height of the baseRect
+ if (topRightRect.Bottom > bottomRightRect.Top)
+ {
+ var newHeight = (topRightRect.Height / (topRightRect.Height + bottomRightRect.Height)) * baseRect.Height;
+ topRightRect = new Rect(topRightRect.Location.X, topRightRect.Location.Y, topRightRect.Width, newHeight);
+ bottomRightRect = new Rect(
+ bottomRightRect.Location.X,
+ baseRect.Top + newHeight,
+ bottomRightRect.Width,
+ Math.Max(0.0, baseRect.Height - newHeight));
+ }
+
+ // Adjust the _width of the BottomLeft and BottomRight rectangles so that they are proportional to the _width of the baseRect
+ if (bottomRightRect.Left < bottomLeftRect.Right)
+ {
+ var newWidth = (bottomLeftRect.Width / (bottomLeftRect.Width + bottomRightRect.Width)) * baseRect.Width;
+ bottomLeftRect = new Rect(bottomLeftRect.Location.X, bottomLeftRect.Location.Y, newWidth, bottomLeftRect.Height);
+ bottomRightRect = new Rect(
+ baseRect.Left + newWidth,
+ bottomRightRect.Location.Y,
+ Math.Max(0.0, baseRect.Width - newWidth),
+ bottomRightRect.Height);
+ }
+
+ // Adjust the height of the TopLeft and BottomLeft rectangles so that they are proportional to the height of the baseRect
+ if (bottomLeftRect.Top < topLeftRect.Bottom)
+ {
+ var newHeight = (topLeftRect.Height / (topLeftRect.Height + bottomLeftRect.Height)) * baseRect.Height;
+ topLeftRect = new Rect(topLeftRect.Location.X, topLeftRect.Location.Y, topLeftRect.Width, newHeight);
+ bottomLeftRect = new Rect(
+ bottomLeftRect.Location.X,
+ baseRect.Top + newHeight,
+ bottomLeftRect.Width,
+ Math.Max(0.0, baseRect.Height - newHeight));
+ }
+
+ var roundedRectGeometry = new StreamGeometry();
+
+ using (var context = roundedRectGeometry.Open())
+ {
+ // Begin from the Bottom of the TopLeft Arc and proceed clockwise
+ context.BeginFigure(topLeftRect.BottomLeft, true, true);
+
+ // TopLeft Arc
+ context.ArcTo(topLeftRect.TopRight, topLeftRect.Size, 0, false, SweepDirection.Clockwise, true, true);
+
+ // Top Line
+ context.LineTo(topRightRect.TopLeft, true, true);
+
+ // TopRight Arc
+ context.ArcTo(topRightRect.BottomRight, topRightRect.Size, 0, false, SweepDirection.Clockwise, true, true);
+
+ // Right Line
+ context.LineTo(bottomRightRect.TopRight, true, true);
+
+ // BottomRight Arc
+ context.ArcTo(bottomRightRect.BottomLeft, bottomRightRect.Size, 0, false, SweepDirection.Clockwise, true, true);
+
+ // Bottom Line
+ context.LineTo(bottomLeftRect.BottomRight, true, true);
+
+ // BottomLeft Arc
+ context.ArcTo(bottomLeftRect.TopLeft, bottomLeftRect.Size, 0, false, SweepDirection.Clockwise, true, true);
+ }
+
+ return roundedRectGeometry;
+ }
+ #endregion
+
protected virtual void DrawGrid(DrawingContext dc, Rect rect)
{
//using .5 forces wpf to draw a single pixel line
@@ -302,7 +503,6 @@ namespace AIStudio.Wpf.DiagramDesigner
dc.DrawLine(new Pen(new SolidColorBrush(GridColor), 1), new Point(i, GridMarginSize.Height), new Point(i, rect.Height - GridMarginSize.Height));
dc.DrawLine(new Pen(new SolidColorBrush(GridColor), 1), new Point(rect.Width - GridMarginSize.Width, GridMarginSize.Height), new Point(rect.Width - GridMarginSize.Width, rect.Height - GridMarginSize.Height));
}
- #endregion
#region Format/Move
private void _service_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
@@ -616,29 +816,36 @@ namespace AIStudio.Wpf.DiagramDesigner
protected override Size MeasureOverride(Size constraint)
{
- Size size = new Size();
- foreach (UIElement element in this.InternalChildren)
+ if (AutoGrowth)
{
- double left = Canvas.GetLeft(element);
- double top = Canvas.GetTop(element);
- left = double.IsNaN(left) ? 0 : left;
- top = double.IsNaN(top) ? 0 : top;
-
- //measure desired size for each child
- element.Measure(constraint);
-
- Size desiredSize = element.DesiredSize;
- if (!double.IsNaN(desiredSize.Width) && !double.IsNaN(desiredSize.Height))
+ Size size = new Size();
+ foreach (UIElement element in this.InternalChildren)
{
- size.Width = Math.Max(size.Width, left + desiredSize.Width);
- size.Height = Math.Max(size.Height, top + desiredSize.Height);
- }
- }
- // add margin
- size.Width += 10;
- size.Height += 10;
+ double left = Canvas.GetLeft(element);
+ double top = Canvas.GetTop(element);
+ left = double.IsNaN(left) ? 0 : left;
+ top = double.IsNaN(top) ? 0 : top;
- return size;
+ //measure desired size for each child
+ element.Measure(constraint);
+
+ Size desiredSize = element.DesiredSize;
+ if (!double.IsNaN(desiredSize.Width) && !double.IsNaN(desiredSize.Height))
+ {
+ size.Width = Math.Max(size.Width, left + desiredSize.Width);
+ size.Height = Math.Max(size.Height, top + desiredSize.Height);
+ }
+ }
+ // add margin
+ size.Width += 10;
+ size.Height += 10;
+
+ return size;
+ }
+ else
+ {
+ return base.MeasureOverride(constraint);
+ }
}
private Connector HitTesting(Point hitPoint)
diff --git a/AIStudio.Wpf.DiagramDesigner/Converters/BorderClipConverter.cs b/AIStudio.Wpf.DiagramDesigner/Converters/BorderClipConverter.cs
new file mode 100644
index 0000000..688b15b
--- /dev/null
+++ b/AIStudio.Wpf.DiagramDesigner/Converters/BorderClipConverter.cs
@@ -0,0 +1,203 @@
+using System;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Data;
+using System.Windows.Media;
+
+namespace AIStudio.Wpf.DiagramDesigner.Converters
+{
+ public class BorderClipConverter : IMultiValueConverter
+ {
+ public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (values.Length > 1 && values[0] is double width && values[1] is double height)
+ {
+ if (width < 1.0 || height < 1.0)
+ {
+ return Geometry.Empty;
+ }
+
+ CornerRadius cornerRadius = default;
+ Thickness borderThickness = default;
+ if (values.Length > 2 && values[2] is CornerRadius radius)
+ {
+ cornerRadius = radius;
+ if (values.Length > 3 && values[3] is Thickness thickness)
+ {
+ borderThickness = thickness;
+ }
+ }
+
+ var geometry = GetRoundRectangle(new Rect(0, 0, width, height), borderThickness, cornerRadius);
+ geometry.Freeze();
+
+ return geometry;
+ }
+
+ return DependencyProperty.UnsetValue;
+ }
+
+ public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+
+ // https://wpfspark.wordpress.com/2011/06/08/clipborder-a-wpf-border-that-clips/
+ public static Geometry GetRoundRectangle(Rect baseRect, Thickness borderThickness, CornerRadius cornerRadius)
+ {
+ // Normalizing the corner radius
+ if (cornerRadius.TopLeft < double.Epsilon)
+ {
+ cornerRadius.TopLeft = 0.0;
+ }
+
+ if (cornerRadius.TopRight < double.Epsilon)
+ {
+ cornerRadius.TopRight = 0.0;
+ }
+
+ if (cornerRadius.BottomLeft < double.Epsilon)
+ {
+ cornerRadius.BottomLeft = 0.0;
+ }
+
+ if (cornerRadius.BottomRight < double.Epsilon)
+ {
+ cornerRadius.BottomRight = 0.0;
+ }
+
+ // Taking the border thickness into account
+ var leftHalf = borderThickness.Left * 0.5;
+ if (leftHalf < double.Epsilon)
+ {
+ leftHalf = 0.0;
+ }
+
+ var topHalf = borderThickness.Top * 0.5;
+ if (topHalf < double.Epsilon)
+ {
+ topHalf = 0.0;
+ }
+
+ var rightHalf = borderThickness.Right * 0.5;
+ if (rightHalf < double.Epsilon)
+ {
+ rightHalf = 0.0;
+ }
+
+ var bottomHalf = borderThickness.Bottom * 0.5;
+ if (bottomHalf < double.Epsilon)
+ {
+ bottomHalf = 0.0;
+ }
+
+ // Create the rectangles for the corners that needs to be curved in the base rectangle
+ // TopLeft Rectangle
+ var topLeftRect = new Rect(
+ baseRect.Location.X,
+ baseRect.Location.Y,
+ Math.Max(0.0, cornerRadius.TopLeft - leftHalf),
+ Math.Max(0.0, cornerRadius.TopLeft - rightHalf));
+
+ // TopRight Rectangle
+ var topRightRect = new Rect(
+ baseRect.Location.X + baseRect.Width - cornerRadius.TopRight + rightHalf,
+ baseRect.Location.Y,
+ Math.Max(0.0, cornerRadius.TopRight - rightHalf),
+ Math.Max(0.0, cornerRadius.TopRight - topHalf));
+
+ // BottomRight Rectangle
+ var bottomRightRect = new Rect(
+ baseRect.Location.X + baseRect.Width - cornerRadius.BottomRight + rightHalf,
+ baseRect.Location.Y + baseRect.Height - cornerRadius.BottomRight + bottomHalf,
+ Math.Max(0.0, cornerRadius.BottomRight - rightHalf),
+ Math.Max(0.0, cornerRadius.BottomRight - bottomHalf));
+
+ // BottomLeft Rectangle
+ var bottomLeftRect = new Rect(
+ baseRect.Location.X,
+ baseRect.Location.Y + baseRect.Height - cornerRadius.BottomLeft + bottomHalf,
+ Math.Max(0.0, cornerRadius.BottomLeft - leftHalf),
+ Math.Max(0.0, cornerRadius.BottomLeft - bottomHalf));
+
+ // Adjust the _width of the TopLeft and TopRight rectangles so that they are proportional to the _width of the baseRect
+ if (topLeftRect.Right > topRightRect.Left)
+ {
+ var newWidth = (topLeftRect.Width / (topLeftRect.Width + topRightRect.Width)) * baseRect.Width;
+ topLeftRect = new Rect(topLeftRect.Location.X, topLeftRect.Location.Y, newWidth, topLeftRect.Height);
+ topRightRect = new Rect(
+ baseRect.Left + newWidth,
+ topRightRect.Location.Y,
+ Math.Max(0.0, baseRect.Width - newWidth),
+ topRightRect.Height);
+ }
+
+ // Adjust the height of the TopRight and BottomRight rectangles so that they are proportional to the height of the baseRect
+ if (topRightRect.Bottom > bottomRightRect.Top)
+ {
+ var newHeight = (topRightRect.Height / (topRightRect.Height + bottomRightRect.Height)) * baseRect.Height;
+ topRightRect = new Rect(topRightRect.Location.X, topRightRect.Location.Y, topRightRect.Width, newHeight);
+ bottomRightRect = new Rect(
+ bottomRightRect.Location.X,
+ baseRect.Top + newHeight,
+ bottomRightRect.Width,
+ Math.Max(0.0, baseRect.Height - newHeight));
+ }
+
+ // Adjust the _width of the BottomLeft and BottomRight rectangles so that they are proportional to the _width of the baseRect
+ if (bottomRightRect.Left < bottomLeftRect.Right)
+ {
+ var newWidth = (bottomLeftRect.Width / (bottomLeftRect.Width + bottomRightRect.Width)) * baseRect.Width;
+ bottomLeftRect = new Rect(bottomLeftRect.Location.X, bottomLeftRect.Location.Y, newWidth, bottomLeftRect.Height);
+ bottomRightRect = new Rect(
+ baseRect.Left + newWidth,
+ bottomRightRect.Location.Y,
+ Math.Max(0.0, baseRect.Width - newWidth),
+ bottomRightRect.Height);
+ }
+
+ // Adjust the height of the TopLeft and BottomLeft rectangles so that they are proportional to the height of the baseRect
+ if (bottomLeftRect.Top < topLeftRect.Bottom)
+ {
+ var newHeight = (topLeftRect.Height / (topLeftRect.Height + bottomLeftRect.Height)) * baseRect.Height;
+ topLeftRect = new Rect(topLeftRect.Location.X, topLeftRect.Location.Y, topLeftRect.Width, newHeight);
+ bottomLeftRect = new Rect(
+ bottomLeftRect.Location.X,
+ baseRect.Top + newHeight,
+ bottomLeftRect.Width,
+ Math.Max(0.0, baseRect.Height - newHeight));
+ }
+
+ var roundedRectGeometry = new StreamGeometry();
+
+ using (var context = roundedRectGeometry.Open())
+ {
+ // Begin from the Bottom of the TopLeft Arc and proceed clockwise
+ context.BeginFigure(topLeftRect.BottomLeft, true, true);
+
+ // TopLeft Arc
+ context.ArcTo(topLeftRect.TopRight, topLeftRect.Size, 0, false, SweepDirection.Clockwise, true, true);
+
+ // Top Line
+ context.LineTo(topRightRect.TopLeft, true, true);
+
+ // TopRight Arc
+ context.ArcTo(topRightRect.BottomRight, topRightRect.Size, 0, false, SweepDirection.Clockwise, true, true);
+
+ // Right Line
+ context.LineTo(bottomRightRect.TopRight, true, true);
+
+ // BottomRight Arc
+ context.ArcTo(bottomRightRect.BottomLeft, bottomRightRect.Size, 0, false, SweepDirection.Clockwise, true, true);
+
+ // Bottom Line
+ context.LineTo(bottomLeftRect.BottomRight, true, true);
+
+ // BottomLeft Arc
+ context.ArcTo(bottomLeftRect.TopLeft, bottomLeftRect.Size, 0, false, SweepDirection.Clockwise, true, true);
+ }
+
+ return roundedRectGeometry;
+ }
+ }
+}
\ No newline at end of file
diff --git a/AIStudio.Wpf.DiagramDesigner/Themes/DiagramControl.xaml b/AIStudio.Wpf.DiagramDesigner/Themes/DiagramControl.xaml
index 98d13ff..c8b9410 100644
--- a/AIStudio.Wpf.DiagramDesigner/Themes/DiagramControl.xaml
+++ b/AIStudio.Wpf.DiagramDesigner/Themes/DiagramControl.xaml
@@ -859,16 +859,6 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/AIStudio.Wpf.DiagramDesigner/UserControls/DiagramControl.xaml b/AIStudio.Wpf.DiagramDesigner/UserControls/DiagramControl.xaml
index 8387b0f..5c2adf3 100644
--- a/AIStudio.Wpf.DiagramDesigner/UserControls/DiagramControl.xaml
+++ b/AIStudio.Wpf.DiagramDesigner/UserControls/DiagramControl.xaml
@@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dd="clr-namespace:AIStudio.Wpf.DiagramDesigner"
+ xmlns:converter="clr-namespace:AIStudio.Wpf.DiagramDesigner.Converters"
xmlns:c="clr-namespace:AIStudio.Wpf.DiagramDesigner.Controls"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
@@ -14,6 +15,9 @@
+
+
+
@@ -32,6 +36,7 @@
+ AllowDrop="{Binding DiagramOption.LayoutOption.AllowDrop}"
+ ClipToBounds="{Binding DiagramOption.LayoutOption.ClipToBounds}"
+ AutoGrowth="{Binding DiagramOption.LayoutOption.AutoGrowth}"
+ CornerRadius="{Binding DiagramOption.LayoutOption.CornerRadius}">
+
+
+
+
+
+
+
@@ -67,6 +82,7 @@
+ AllowDrop="{Binding DiagramOption.LayoutOption.AllowDrop}"
+ ClipToBounds="{Binding DiagramOption.LayoutOption.ClipToBounds}"
+ AutoGrowth="{Binding DiagramOption.LayoutOption.AutoGrowth}"
+ CornerRadius="{Binding DiagramOption.LayoutOption.CornerRadius}">
+
+
+
+
+
+
+
diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramOption.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramOption.cs
index 779d324..d1f3957 100644
--- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramOption.cs
+++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramOption.cs
@@ -370,7 +370,20 @@ namespace AIStudio.Wpf.DiagramDesigner
{
get; set;
} = true;
-
+
+ public bool ClipToBounds
+ {
+ get; set;
+ }
+ public bool AutoGrowth
+ {
+ get; set;
+ } = true;
+
+ public CornerRadius CornerRadius
+ {
+ get; set;
+ } = new CornerRadius();
}
public class SnappingOption