diff --git a/AIStudio.Wpf.DiagramDesigner.Demo/ViewModels/Links/PathGeneratorsViewModel.cs b/AIStudio.Wpf.DiagramDesigner.Demo/ViewModels/Links/PathGeneratorsViewModel.cs
new file mode 100644
index 0000000..22a7e5e
--- /dev/null
+++ b/AIStudio.Wpf.DiagramDesigner.Demo/ViewModels/Links/PathGeneratorsViewModel.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+
+namespace AIStudio.Wpf.DiagramDesigner.Demo.ViewModels
+{
+ class PathGeneratorsViewModel : BaseViewModel
+ {
+ public PathGeneratorsViewModel()
+ {
+ Title = "Link Path Generators";
+ Info = "Path generators are functions that take as input the calculated route and output SVG paths, " +
+ "alongside the markers positions and their angles. There are currently two generators: Straight and Smooth.";
+
+ _service.ColorViewModel.FillColor.Color = System.Windows.Media.Colors.Orange;
+
+ DiagramViewModel = new DiagramViewModel();
+ DiagramViewModel.PageSizeType = PageSizeType.Custom;
+ DiagramViewModel.PageSize = new Size(double.NaN, double.NaN);
+
+ DefaultDesignerItemViewModel node1 = new DefaultDesignerItemViewModel() { Left = 50, Top = 80, Text = "1" };
+ DiagramViewModel.DirectAddItemCommand.Execute(node1);
+
+ DefaultDesignerItemViewModel node2 = new DefaultDesignerItemViewModel() { Left = 300, Top = 350, Text = "2" };
+ DiagramViewModel.DirectAddItemCommand.Execute(node2);
+
+ DefaultDesignerItemViewModel node3 = new DefaultDesignerItemViewModel() { Left = 400, Top = 100, Text = "3" };
+ DiagramViewModel.DirectAddItemCommand.Execute(node3);
+
+ ConnectionViewModel connector1 = new ConnectionViewModel(node1.RightConnector, node2.LeftConnector, DrawMode.ConnectingLineStraight, RouterMode.RouterNormal);
+ connector1.AddLabel("Straight");
+ DiagramViewModel.DirectAddItemCommand.Execute(connector1);
+
+ ConnectionViewModel connector2 = new ConnectionViewModel(node2.RightConnector, node3.LeftConnector, DrawMode.ConnectingLineSmooth, RouterMode.RouterNormal);
+ connector2.AddLabel("Smooth");
+ DiagramViewModel.DirectAddItemCommand.Execute(connector2);
+ }
+ }
+}
diff --git a/AIStudio.Wpf.DiagramDesigner.Demo/ViewModels/Links/RoutersViewModel.cs b/AIStudio.Wpf.DiagramDesigner.Demo/ViewModels/Links/RoutersViewModel.cs
new file mode 100644
index 0000000..406bb2d
--- /dev/null
+++ b/AIStudio.Wpf.DiagramDesigner.Demo/ViewModels/Links/RoutersViewModel.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+
+namespace AIStudio.Wpf.DiagramDesigner.Demo.ViewModels
+{
+ class RoutersViewModel : BaseViewModel
+ {
+ public RoutersViewModel()
+ {
+ Title = "Link Routers";
+ Info = "Routers are functions that take as input the link's vertices and can add points in between. " +
+ "There are currently two routers: Normal and Orthogonal.";
+
+ _service.ColorViewModel.FillColor.Color = System.Windows.Media.Colors.Orange;
+
+ DiagramViewModel = new DiagramViewModel();
+ DiagramViewModel.PageSizeType = PageSizeType.Custom;
+ DiagramViewModel.PageSize = new Size(double.NaN, double.NaN);
+
+ DefaultDesignerItemViewModel node1 = new DefaultDesignerItemViewModel() { Left = 50, Top = 80, Text = "1" };
+ DiagramViewModel.DirectAddItemCommand.Execute(node1);
+
+ DefaultDesignerItemViewModel node2 = new DefaultDesignerItemViewModel() { Left = 300, Top = 350, Text = "2" };
+ DiagramViewModel.DirectAddItemCommand.Execute(node2);
+
+ DefaultDesignerItemViewModel node3 = new DefaultDesignerItemViewModel() { Left = 350, Top = 100, Text = "3" };
+ DiagramViewModel.DirectAddItemCommand.Execute(node3);
+
+ ConnectionViewModel connector1 = new ConnectionViewModel(node1.RightConnector, node2.LeftConnector, DrawMode.ConnectingLineSmooth, RouterMode.RouterNormal);
+ connector1.AddLabel("Normal");
+ DiagramViewModel.DirectAddItemCommand.Execute(connector1);
+
+ ConnectionViewModel connector2 = new ConnectionViewModel(node2.RightConnector, node3.LeftConnector, DrawMode.ConnectingLineStraight, RouterMode.RouterOrthogonal);
+ connector2.AddLabel("Orthogonal");
+ DiagramViewModel.DirectAddItemCommand.Execute(connector2);
+ }
+ }
+}
\ No newline at end of file
diff --git a/AIStudio.Wpf.DiagramDesigner.Demo/ViewModels/Links/SnappingViewModel.cs b/AIStudio.Wpf.DiagramDesigner.Demo/ViewModels/Links/SnappingViewModel.cs
new file mode 100644
index 0000000..4962f2b
--- /dev/null
+++ b/AIStudio.Wpf.DiagramDesigner.Demo/ViewModels/Links/SnappingViewModel.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+
+namespace AIStudio.Wpf.DiagramDesigner.Demo.ViewModels
+{
+ class SnappingViewModel : BaseViewModel
+ {
+ public SnappingViewModel()
+ {
+ Title = "Link Snapping";
+ Info = "While dragging a new link, it will try to find (and link) to the closest target within a radius.";
+
+ _service.ColorViewModel.FillColor.Color = System.Windows.Media.Colors.Orange;
+
+ DiagramViewModel = new DiagramViewModel();
+ DiagramViewModel.PageSizeType = PageSizeType.Custom;
+ DiagramViewModel.PageSize = new Size(double.NaN, double.NaN);
+ DiagramViewModel.EnableSnapping = true;
+
+ DefaultDesignerItemViewModel node1 = new DefaultDesignerItemViewModel() { Left = 50, Top = 50, Text = "1" };
+ DiagramViewModel.DirectAddItemCommand.Execute(node1);
+
+ DefaultDesignerItemViewModel node2 = new DefaultDesignerItemViewModel() { Left = 300, Top = 300, Text = "2" };
+ DiagramViewModel.DirectAddItemCommand.Execute(node2);
+
+ DefaultDesignerItemViewModel node3 = new DefaultDesignerItemViewModel() { Left = 300, Top = 50, Text = "3" };
+ DiagramViewModel.DirectAddItemCommand.Execute(node3);
+ }
+ }
+}
diff --git a/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/PathGeneratorsView.xaml b/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/PathGeneratorsView.xaml
new file mode 100644
index 0000000..725637f
--- /dev/null
+++ b/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/PathGeneratorsView.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
diff --git a/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/PathGeneratorsView.xaml.cs b/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/PathGeneratorsView.xaml.cs
new file mode 100644
index 0000000..5dbbc52
--- /dev/null
+++ b/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/PathGeneratorsView.xaml.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+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 AIStudio.Wpf.DiagramDesigner.Demo.Views
+{
+ ///
+ /// PathGeneratorsView.xaml 的交互逻辑
+ ///
+ public partial class PathGeneratorsView : UserControl
+ {
+ public PathGeneratorsView()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/RoutersView.xaml b/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/RoutersView.xaml
new file mode 100644
index 0000000..ec7b57f
--- /dev/null
+++ b/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/RoutersView.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
diff --git a/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/RoutersView.xaml.cs b/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/RoutersView.xaml.cs
new file mode 100644
index 0000000..adc09aa
--- /dev/null
+++ b/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/RoutersView.xaml.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+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 AIStudio.Wpf.DiagramDesigner.Demo.Views
+{
+ ///
+ /// RoutersView.xaml 的交互逻辑
+ ///
+ public partial class RoutersView : UserControl
+ {
+ public RoutersView()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/SnappingView.xaml b/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/SnappingView.xaml
new file mode 100644
index 0000000..ee7e670
--- /dev/null
+++ b/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/SnappingView.xaml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
diff --git a/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/SnappingView.xaml.cs b/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/SnappingView.xaml.cs
new file mode 100644
index 0000000..4fddc73
--- /dev/null
+++ b/AIStudio.Wpf.DiagramDesigner.Demo/Views/Links/SnappingView.xaml.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+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 AIStudio.Wpf.DiagramDesigner.Demo.Views
+{
+ ///
+ /// SnappingView.xaml 的交互逻辑
+ ///
+ public partial class SnappingView : UserControl
+ {
+ public SnappingView()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/AIStudio.Wpf.DiagramDesigner/Controls/DesignerCanvas.cs b/AIStudio.Wpf.DiagramDesigner/Controls/DesignerCanvas.cs
index 2f07c50..e5c9ac0 100644
--- a/AIStudio.Wpf.DiagramDesigner/Controls/DesignerCanvas.cs
+++ b/AIStudio.Wpf.DiagramDesigner/Controls/DesignerCanvas.cs
@@ -17,8 +17,20 @@ namespace AIStudio.Wpf.DiagramDesigner
{
public class DesignerCanvas : Canvas
{
- private IDiagramViewModel _viewModel { get { return DataContext as IDiagramViewModel; } }
- private IDiagramServiceProvider _service { get { return DiagramServicesProvider.Instance.Provider; } }
+ private IDiagramViewModel _viewModel
+ {
+ get
+ {
+ return DataContext as IDiagramViewModel;
+ }
+ }
+ private IDiagramServiceProvider _service
+ {
+ get
+ {
+ return DiagramServicesProvider.Instance.Provider;
+ }
+ }
private ConnectionViewModel partialConnection;
private List connectorsHit = new List();
@@ -54,6 +66,22 @@ namespace AIStudio.Wpf.DiagramDesigner
}
}
+ private bool EnableSnapping
+ {
+ get
+ {
+ return _viewModel.EnableSnapping;
+ }
+ }
+
+ private double SnappingRadius
+ {
+ get
+ {
+ return _viewModel.SnappingRadius;
+ }
+ }
+
#region GridCellSize
public static readonly DependencyProperty GridCellSizeProperty =
@@ -64,8 +92,14 @@ namespace AIStudio.Wpf.DiagramDesigner
public Size GridCellSize
{
- get { return (Size)GetValue(GridCellSizeProperty); }
- set { SetValue(GridCellSizeProperty, value); }
+ get
+ {
+ return (Size)GetValue(GridCellSizeProperty);
+ }
+ set
+ {
+ SetValue(GridCellSizeProperty, value);
+ }
}
#endregion
@@ -80,8 +114,14 @@ namespace AIStudio.Wpf.DiagramDesigner
public bool ShowGrid
{
- get { return (bool)GetValue(ShowGridProperty); }
- set { SetValue(ShowGridProperty, value); }
+ get
+ {
+ return (bool)GetValue(ShowGridProperty);
+ }
+ set
+ {
+ SetValue(ShowGridProperty, value);
+ }
}
#endregion
@@ -96,8 +136,14 @@ namespace AIStudio.Wpf.DiagramDesigner
public Color GridColor
{
- get { return (Color)GetValue(GridColorProperty); }
- set { SetValue(GridColorProperty, value); }
+ get
+ {
+ return (Color)GetValue(GridColorProperty);
+ }
+ set
+ {
+ SetValue(GridColorProperty, value);
+ }
}
#endregion
@@ -112,8 +158,14 @@ namespace AIStudio.Wpf.DiagramDesigner
public Size GridMarginSize
{
- get { return (Size)GetValue(GridMarginSizeProperty); }
- set { SetValue(GridMarginSizeProperty, value); }
+ get
+ {
+ return (Size)GetValue(GridMarginSizeProperty);
+ }
+ set
+ {
+ SetValue(GridMarginSizeProperty, value);
+ }
}
#endregion
@@ -204,7 +256,10 @@ namespace AIStudio.Wpf.DiagramDesigner
private Connector sourceConnector;
public Connector SourceConnector
{
- get { return sourceConnector; }
+ get
+ {
+ return sourceConnector;
+ }
set
{
if (sourceConnector != value)
@@ -218,7 +273,7 @@ namespace AIStudio.Wpf.DiagramDesigner
Point point = new Point(rectangleBounds.Left + (rectangleBounds.Width / 2),
rectangleBounds.Bottom + (rectangleBounds.Height / 2));
partialConnection = new ConnectionViewModel(_viewModel, sourceDataItem, new PartCreatedConnectorInfo(point.X, point.Y), DrawMode, RouterMode);
-
+
_viewModel.DirectAddItemCommand.Execute(partialConnection);
}
}
@@ -227,7 +282,10 @@ namespace AIStudio.Wpf.DiagramDesigner
private FullyCreatedConnectorInfo sourceConnectorInfo;
public FullyCreatedConnectorInfo SourceConnectorInfo
{
- get { return sourceConnectorInfo; }
+ get
+ {
+ return sourceConnectorInfo;
+ }
set
{
if (sourceConnectorInfo != value)
@@ -288,7 +346,7 @@ namespace AIStudio.Wpf.DiagramDesigner
{
if (connectorsHit.Count == 0)
{
- LinkPointDesignerItemViewModel pointItemView = new LinkPointDesignerItemViewModel(rubberbandSelectionStartPoint.Value);
+ LinkPointDesignerItemViewModel pointItemView = new LinkPointDesignerItemViewModel(rubberbandSelectionStartPoint.Value);
_viewModel.DirectAddItemCommand.Execute(pointItemView);
SourceConnectorInfo = pointItemView.TopConnector;
}
@@ -311,8 +369,7 @@ namespace AIStudio.Wpf.DiagramDesigner
if (_service.DrawModeViewModel.CursorMode == CursorMode.Move)
{
- _viewModel.SelectedItems.OfType().ToList().ForEach(p =>
- {
+ _viewModel.SelectedItems.OfType().ToList().ForEach(p => {
p.Left = currentPoint.X;
p.Top = currentPoint.Y;
});
@@ -325,6 +382,19 @@ namespace AIStudio.Wpf.DiagramDesigner
{
partialConnection.SinkConnectorInfo = new PartCreatedConnectorInfo(currentPoint.X, currentPoint.Y);
HitTesting(currentPoint);
+
+ if (EnableSnapping)
+ {
+ var nearPort = FindNearPortToAttachTo();
+ if (nearPort != null || partialConnection.SinkConnectorInfoFully != null)
+ {
+ //var oldPort = _ongoingLink.TargetPort;
+ //_ongoingLink.SetTargetPort(nearPort);
+ //oldPort?.Refresh();
+ //nearPort?.Refresh();
+ partialConnection.SinkConnectorInfo = nearPort;
+ }
+ }
}
}
else
@@ -377,6 +447,10 @@ namespace AIStudio.Wpf.DiagramDesigner
sinkDataItem.DataItem.Root.DirectRemoveItemCommand.Execute(
sinkDataItem.DataItem.Root.Items[indexOfLastTempConnection]);
sinkDataItem.DataItem.Root.AddItemCommand.Execute(new ConnectionViewModel(_viewModel, sourceDataItem, sinkDataItem, DrawMode, RouterMode));
+ }
+ else if (partialConnection.IsFullConnection)
+ {
+
}
else if (_service.DrawModeViewModel.GetDrawMode() == DrawMode.DirectLine && connectorsHit.Count() == 1)
{
@@ -403,7 +477,7 @@ namespace AIStudio.Wpf.DiagramDesigner
}
}
-
+
connectorsHit = new List();
sourceConnector = null;
sourceConnectorInfo = null;
@@ -508,7 +582,7 @@ namespace AIStudio.Wpf.DiagramDesigner
itemBase.Suffix = System.IO.Path.GetExtension(itemBase.Icon).ToLower();
itemBase.InitWidthAndHeight();
itemBase.AutoSize();
-
+
itemBase.Left = Math.Max(0, position.X - itemBase.ItemWidth / 2);
itemBase.Top = Math.Max(0, position.Y - itemBase.ItemHeight / 2);
(DataContext as IDiagramViewModel).AddItemCommand.Execute(itemBase);
@@ -516,5 +590,17 @@ namespace AIStudio.Wpf.DiagramDesigner
}
e.Handled = true;
}
+
+ public FullyCreatedConnectorInfo FindNearPortToAttachTo()
+ {
+ foreach (var port in _viewModel.Items.OfType().ToList().SelectMany(n => n.Connectors))
+ {
+ if (partialConnection.OnGoingPosition.DistanceTo(port.Position) < SnappingRadius &&
+ partialConnection.SourceConnectorInfo.CanAttachTo(port))
+ return port;
+ }
+
+ return null;
+ }
}
-}
+}
\ No newline at end of file
diff --git a/AIStudio.Wpf.DiagramDesigner/UserControls/LineControl.xaml b/AIStudio.Wpf.DiagramDesigner/UserControls/LineControl.xaml
index d39c732..08da5df 100644
--- a/AIStudio.Wpf.DiagramDesigner/UserControls/LineControl.xaml
+++ b/AIStudio.Wpf.DiagramDesigner/UserControls/LineControl.xaml
@@ -44,8 +44,7 @@
port != this && !port.IsReadOnly && DataItem != port.DataItem;
}
diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramViewModel.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramViewModel.cs
index e3fceeb..daf8d92 100644
--- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramViewModel.cs
+++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramViewModel.cs
@@ -366,6 +366,31 @@ namespace AIStudio.Wpf.DiagramDesigner
}
}
+ private bool _enableSnapping;
+ public bool EnableSnapping
+ {
+ get
+ {
+ return _enableSnapping;
+ }
+ set
+ {
+ SetProperty(ref _enableSnapping, value);
+ }
+ }
+
+ private double _snappingRadius = 50;
+ public double SnappingRadius
+ {
+ get
+ {
+ return _snappingRadius;
+ }
+ set
+ {
+ SetProperty(ref _snappingRadius, value);
+ }
+ }
private bool _isEditName;
[Browsable(false)]
@@ -1089,7 +1114,7 @@ namespace AIStudio.Wpf.DiagramDesigner
});
}
}
-
+
private void ExecuteBringForwardCommand(object parameter)
{
@@ -1391,7 +1416,7 @@ namespace AIStudio.Wpf.DiagramDesigner
SelectedItems.OfType().ToList();
List selectedConnections =
- SelectedItems.OfType().ToList();
+ SelectedItems.OfType().ToList();
foreach (ConnectionViewModel connection in Items.OfType())
{
@@ -1444,7 +1469,7 @@ namespace AIStudio.Wpf.DiagramDesigner
foreach (var diagramItemData in copyitem.DesignerItems)
{
DesignerItemViewModelBase newItem = null;
-
+
Type type = TypeHelper.GetType(diagramItemData.ModelTypeName);
DesignerItemViewModelBase itemBase = Activator.CreateInstance(type, this, diagramItemData, ".json") as DesignerItemViewModelBase;
@@ -1460,7 +1485,7 @@ namespace AIStudio.Wpf.DiagramDesigner
{
items.Add(newItem);
}
- }
+ }
DirectAddItemCommand.Execute(items);
OffsetX += 10;
@@ -1751,7 +1776,7 @@ namespace AIStudio.Wpf.DiagramDesigner
{
}
-
+
diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/IDiagramViewModel.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/IDiagramViewModel.cs
index fc613a9..a594274 100644
--- a/AIStudio.Wpf.DiagramDesigner/ViewModels/IDiagramViewModel.cs
+++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/IDiagramViewModel.cs
@@ -228,6 +228,14 @@ namespace AIStudio.Wpf.DiagramDesigner
{
get; set;
}
+ bool EnableSnapping
+ {
+ get; set;
+ }
+ double SnappingRadius
+ {
+ get; set;
+ }
Size GridMarginSize
{
get; set;