Files
aistudio-wpf-diagram/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/ConnectorViewModel.cs

641 lines
23 KiB
C#
Raw Normal View History

2021-07-23 09:42:22 +08:00
using System;
using System.Collections.Generic;
2023-01-15 11:59:51 +08:00
using System.Collections.ObjectModel;
2021-07-23 09:42:22 +08:00
using System.ComponentModel;
using System.Linq;
2023-01-15 11:59:51 +08:00
using System.Windows;
using System.Windows.Input;
2023-01-08 09:22:37 +08:00
//using System.Windows;
2021-07-23 09:42:22 +08:00
using System.Windows.Media;
2023-01-12 23:02:53 +08:00
using AIStudio.Wpf.DiagramDesigner.Geometrys;
2022-10-28 22:45:39 +08:00
using AIStudio.Wpf.DiagramDesigner.Helpers;
2021-07-23 09:42:22 +08:00
2022-10-28 22:45:39 +08:00
namespace AIStudio.Wpf.DiagramDesigner
2021-07-23 09:42:22 +08:00
{
public class ConnectorViewModel : SelectableDesignerItemViewModelBase
{
2023-01-12 23:02:53 +08:00
public ConnectorViewModel(IDiagramViewModel parent, FullyCreatedConnectorInfo sourceConnectorInfo, ConnectorInfoBase sinkConnectorInfo, DrawMode drawMode, RouterMode routerMode)
2021-07-23 09:42:22 +08:00
{
2023-01-12 23:02:53 +08:00
Parent = parent;
2023-01-14 21:52:05 +08:00
PathMode = drawMode.ToString();
2023-01-12 23:02:53 +08:00
RouterMode = routerMode.ToString();
2021-07-23 09:42:22 +08:00
Init(sourceConnectorInfo, sinkConnectorInfo);
}
2023-01-12 23:02:53 +08:00
public ConnectorViewModel(IDiagramViewModel parent, FullyCreatedConnectorInfo sourceConnectorInfo, FullyCreatedConnectorInfo sinkConnectorInfo, ConnectionItem designer) : base(parent, designer)
2021-07-23 09:42:22 +08:00
{
2023-01-12 23:02:53 +08:00
PathMode = designer.PathMode;
RouterMode = designer.RouterMode;
2021-07-23 09:42:22 +08:00
Init(sourceConnectorInfo, sinkConnectorInfo);
}
2023-01-14 21:52:05 +08:00
public ConnectorViewModel(FullyCreatedConnectorInfo sourceConnectorInfo, ConnectorInfoBase sinkConnectorInfo, DrawMode drawMode, RouterMode routerMode) : this(null, sourceConnectorInfo, sinkConnectorInfo, drawMode, routerMode)
{
2023-01-14 21:52:05 +08:00
}
2023-01-12 23:02:53 +08:00
private void Init(FullyCreatedConnectorInfo sourceConnectorInfo, ConnectorInfoBase sinkConnectorInfo)
{
this.Parent = sourceConnectorInfo.DataItem.Parent;
2023-01-20 22:43:08 +08:00
2023-01-16 22:38:58 +08:00
if (Parent != null && Parent.ColorViewModel != null)
{
this.ColorViewModel = CopyHelper.Mapper(Parent.ColorViewModel);
}
if (sinkConnectorInfo is FullyCreatedConnectorInfo sink && sink.DataItem.ShowArrow == false)
{
this.ColorViewModel.RightArrowPathStyle = ArrowPathStyle.None;
}
2023-01-21 22:01:10 +08:00
ColorViewModel.PropertyChanged += ConnectorViewModel_PropertyChanged;
this.PropertyChanged += ConnectorViewModel_PropertyChanged;
2023-01-12 23:02:53 +08:00
var routetype = GlobalType.AllTypes.Where(p => typeof(IRouter).IsAssignableFrom(p)).FirstOrDefault(p => p.Name == RouterMode);
2023-01-14 21:52:05 +08:00
Router = routetype != null ? (System.Activator.CreateInstance(routetype) as IRouter) : new RouterNormal();
2023-01-12 23:02:53 +08:00
var pathGeneratortype = GlobalType.AllTypes.Where(p => typeof(IPathGenerator).IsAssignableFrom(p)).FirstOrDefault(p => p.Name == PathMode);
2023-01-14 21:52:05 +08:00
PathGenerator = pathGeneratortype != null ? (System.Activator.CreateInstance(pathGeneratortype) as IPathGenerator) : new ConnectingLineSmooth();
2023-01-12 23:02:53 +08:00
this.SourceConnectorInfo = sourceConnectorInfo;
this.SinkConnectorInfo = sinkConnectorInfo;
DeleteConnectionCommand = new SimpleCommand(DeleteConnection);
2023-01-21 22:01:10 +08:00
AddVertexCommand = new SimpleCommand(AddVertex);
2023-01-20 22:43:08 +08:00
}
2023-01-21 22:01:10 +08:00
private void ConnectorViewModel_PropertyChanged1(object sender, PropertyChangedEventArgs e)
2023-01-20 22:43:08 +08:00
{
2023-01-21 22:01:10 +08:00
throw new NotImplementedException();
2023-01-12 23:02:53 +08:00
}
public override SelectableDesignerItemBase ToXmlObject()
{
2023-01-08 09:22:37 +08:00
if (IsFullConnection)
{
2023-01-12 23:02:53 +08:00
ConnectionItem connection = new ConnectionItem(this);
return connection;
}
else
{
return null;
}
}
public override Type ToXmlType()
{
return typeof(ConnectionItem);
2023-01-14 21:52:05 +08:00
}
2021-07-23 09:42:22 +08:00
2023-01-21 22:01:10 +08:00
#region
2023-01-08 09:22:37 +08:00
private PointBase _sourceA;
public PointBase SourceA
2021-07-23 09:42:22 +08:00
{
get
{
return _sourceA;
}
set
{
2023-01-21 22:01:10 +08:00
SetProperty(ref _sourceA, value);
2021-07-23 09:42:22 +08:00
}
}
2023-01-08 09:22:37 +08:00
private PointBase _sourceB;
public PointBase SourceB
2021-07-23 09:42:22 +08:00
{
get
{
return _sourceB;
}
set
{
2023-01-21 22:01:10 +08:00
SetProperty(ref _sourceB, value);
2021-07-23 09:42:22 +08:00
}
}
2023-01-08 09:22:37 +08:00
private PointBase _startPoint;
public PointBase StartPoint
2021-07-23 09:42:22 +08:00
{
get
{
return _startPoint;
}
private set
{
SetProperty(ref _startPoint, value);
}
}
2023-01-08 09:22:37 +08:00
private PointBase _endPoint;
public PointBase EndPoint
2021-07-23 09:42:22 +08:00
{
get
{
return _endPoint;
}
private set
{
SetProperty(ref _endPoint, value);
}
}
2023-01-15 11:59:51 +08:00
private double _startAngle;
public double StartAngle
{
get
{
return _startAngle;
}
private set
{
SetProperty(ref _startAngle, value);
}
}
private double _endAngle;
public double EndAngle
{
get
{
return _endAngle;
}
private set
{
SetProperty(ref _endAngle, value);
}
}
2023-01-08 09:22:37 +08:00
private RectangleBase _area;
public RectangleBase Area
2021-07-23 09:42:22 +08:00
{
get
{
return _area;
}
private set
{
2023-01-21 22:01:10 +08:00
SetProperty(ref _area, value);
2021-07-23 09:42:22 +08:00
}
}
2023-01-12 23:02:53 +08:00
public string PathMode
{
get; set;
}
public string RouterMode
{
get; set;
}
public IRouter Router
2022-11-30 22:28:22 +08:00
{
get; set;
}
2023-01-12 23:02:53 +08:00
public IPathGenerator PathGenerator
{
get; set;
}
private PathGeneratorResult _pathGeneratorResult;
public PathGeneratorResult PathGeneratorResult
{
get
{
return _pathGeneratorResult;
}
private set
{
SetProperty(ref _pathGeneratorResult, value);
}
}
2023-01-15 11:59:51 +08:00
private bool _shouldInsertAnchor;
public bool ShouldInsertAnchor
{
get
{
return _shouldInsertAnchor;
}
set
{
SetProperty(ref _shouldInsertAnchor, value);
}
}
2023-01-08 09:22:37 +08:00
//待完善这两处
2023-01-21 22:01:10 +08:00
private ObservableCollection<LinkVertexModel> _vertices = new ObservableCollection<LinkVertexModel>();
public ObservableCollection<LinkVertexModel> Vertices
{
get
{
return _vertices;
}
private set
{
SetProperty(ref _vertices, value);
}
}
2023-01-08 09:22:37 +08:00
public List<LinkLabelModel> Labels { get; set; } = new List<LinkLabelModel>();
2022-12-06 21:28:42 +08:00
public virtual Dictionary<string, string> PropertiesSetting
{
get
{
return new Dictionary<string, string>()
{
{ "Text","文本" },
};
}
}
2021-07-23 09:42:22 +08:00
private FullyCreatedConnectorInfo _sourceConnectorInfo;
public FullyCreatedConnectorInfo SourceConnectorInfo
{
get
{
return _sourceConnectorInfo;
}
set
{
2023-01-21 22:01:10 +08:00
SetProperty(ref _sourceConnectorInfo, value);
2021-07-23 09:42:22 +08:00
}
}
private ConnectorInfoBase _sinkConnectorInfo;
public ConnectorInfoBase SinkConnectorInfo
{
get
{
return _sinkConnectorInfo;
}
set
{
2023-01-21 22:01:10 +08:00
SetProperty(ref _sinkConnectorInfo, value);
2021-07-23 09:42:22 +08:00
}
}
2023-01-08 09:22:37 +08:00
public FullyCreatedConnectorInfo SinkConnectorInfoFully
{
get
{
return SinkConnectorInfo as FullyCreatedConnectorInfo;
}
}
public PartCreatedConnectionInfo SinkConnectorInfoPart
{
get
{
return SinkConnectorInfo as PartCreatedConnectionInfo;
}
}
2023-01-12 23:02:53 +08:00
public PointBase OnGoingPosition
2023-01-08 09:22:37 +08:00
{
get
{
2023-01-15 20:27:39 +08:00
return SinkConnectorInfoPart?.MiddlePosition ?? PointBase.Zero;
2023-01-08 09:22:37 +08:00
}
}
public bool IsFullConnection
{
get
{
return SinkConnectorInfoFully != null;
}
}
public bool IsPortless => SourceConnectorInfo?.DataItem?.Connectors?.Count() == 0;
2023-01-21 22:01:10 +08:00
#endregion
#region
public SimpleCommand DeleteConnectionCommand
{
get; set;
}
public SimpleCommand AddVertexCommand
{
get; set;
}
#endregion
private void ConnectorViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (sender is ConnectorViewModel)
{
switch (e.PropertyName)
{
case nameof(SourceA):
case nameof(SourceB):
UpdateArea();
break;
case nameof(Area):
UpdatePathGeneratorResult();
if (e is ValuePropertyChangedEventArgs valuePropertyChangedEventArgs)
{
OutTextItemLocation((RectangleBase)valuePropertyChangedEventArgs.OldValue, (RectangleBase)valuePropertyChangedEventArgs.NewValue);
}
break;
case nameof(Vertices):
foreach (var vertice in Vertices)
{
vertice.PropertyChanged += new WeakINPCEventHandler(ConnectorViewModel_PropertyChanged).Handler;
}
break;
case nameof(SourceConnectorInfo):
SourceA = PointHelper.GetPointForConnector(SourceConnectorInfo);
(SourceConnectorInfo.DataItem as INotifyPropertyChanged).PropertyChanged += new WeakINPCEventHandler(ConnectorViewModel_PropertyChanged).Handler;
break;
case nameof(SinkConnectorInfo):
SourceB = SinkConnectorInfo.Position;
if (SinkConnectorInfo is FullyCreatedConnectorInfo)
{
(((FullyCreatedConnectorInfo)SinkConnectorInfo).DataItem as INotifyPropertyChanged).PropertyChanged += new WeakINPCEventHandler(ConnectorViewModel_PropertyChanged).Handler;
}
break;
}
}
else if (sender is ColorViewModel)
{
if (e.PropertyName == nameof(ColorViewModel.LeftArrowPathStyle) ||
e.PropertyName == nameof(ColorViewModel.LeftArrowSizeStyle) ||
e.PropertyName == nameof(ColorViewModel.RightArrowPathStyle) ||
e.PropertyName == nameof(ColorViewModel.RightArrowSizeStyle))
{
UpdatePathGeneratorResult();
}
}
else if (sender is DesignerItemViewModelBase)
{
switch (e.PropertyName)
{
case nameof(DesignerItemViewModelBase.ItemHeight):
case nameof(DesignerItemViewModelBase.ItemWidth):
case nameof(DesignerItemViewModelBase.Left):
case nameof(DesignerItemViewModelBase.Top):
SourceA = PointHelper.GetPointForConnector(this.SourceConnectorInfo);
if (IsFullConnection)
{
SourceB = PointHelper.GetPointForConnector(this.SinkConnectorInfoFully);
}
break;
}
}
else if (sender is LinkVertexModel)
{
switch (e.PropertyName)
{
case nameof(ConnectorPoint.Left):
case nameof(ConnectorPoint.Top):
UpdatePathGeneratorResult();
break;
}
}
}
2023-01-08 09:22:37 +08:00
2021-07-23 09:42:22 +08:00
private void UpdateArea()
{
2023-01-08 09:22:37 +08:00
Area = new RectangleBase(SourceA, SourceB);
2021-07-23 09:42:22 +08:00
}
2023-01-20 22:43:08 +08:00
private void UpdatePathGeneratorResult()
2021-07-23 09:42:22 +08:00
{
2023-01-14 21:52:05 +08:00
if (SourceConnectorInfo == null || SinkConnectorInfo == null)
return;
2021-07-23 09:42:22 +08:00
2023-01-12 23:02:53 +08:00
var route = Router.Get(Parent, this);
2023-01-14 21:52:05 +08:00
2023-01-12 23:02:53 +08:00
(var source, var target) = FindConnectionPoints(route);
2023-01-20 22:43:08 +08:00
if (source == null || target == null)
return;
2023-01-08 09:22:37 +08:00
2023-01-12 23:02:53 +08:00
PathGeneratorResult = PathGenerator.Get(Parent, this, route, source.Value, target.Value);
2023-01-20 22:43:08 +08:00
//修正旋转
switch (SourceConnectorInfo.Orientation)
{
case ConnectorOrientation.Left:
{
StartPoint = new PointBase(PathGeneratorResult.SourceMarkerPosition.X, PathGeneratorResult.SourceMarkerPosition.Y - ColorViewModel.LeftArrowSize / 2);
break;
}
case ConnectorOrientation.Top:
{
StartPoint = new PointBase(PathGeneratorResult.SourceMarkerPosition.X - ColorViewModel.LeftArrowSize / 2, PathGeneratorResult.SourceMarkerPosition.Y);
break;
}
case ConnectorOrientation.Right:
{
StartPoint = new PointBase(PathGeneratorResult.SourceMarkerPosition.X - ColorViewModel.LeftArrowSize, PathGeneratorResult.SourceMarkerPosition.Y - ColorViewModel.LeftArrowSize / 2);
break;
}
case ConnectorOrientation.Bottom:
{
StartPoint = new PointBase(PathGeneratorResult.SourceMarkerPosition.X - ColorViewModel.LeftArrowSize / 2, PathGeneratorResult.SourceMarkerPosition.Y - ColorViewModel.LeftArrowSize);
break;
}
default:
{
StartPoint = PathGeneratorResult.SourceMarkerPosition;
break;
}
}
//修正旋转
switch (SinkConnectorInfo.Orientation)
{
case ConnectorOrientation.Left:
{
EndPoint = new PointBase(PathGeneratorResult.TargetMarkerPosition.X, PathGeneratorResult.TargetMarkerPosition.Y - ColorViewModel.RightArrowSize / 2);
2023-01-21 22:01:10 +08:00
break;
}
2023-01-20 22:43:08 +08:00
case ConnectorOrientation.Top:
{
EndPoint = new PointBase(PathGeneratorResult.TargetMarkerPosition.X - ColorViewModel.RightArrowSize / 2, PathGeneratorResult.TargetMarkerPosition.Y);
break;
2023-01-21 22:01:10 +08:00
}
2023-01-20 22:43:08 +08:00
case ConnectorOrientation.Right:
{
EndPoint = new PointBase(PathGeneratorResult.TargetMarkerPosition.X - ColorViewModel.RightArrowSize, PathGeneratorResult.TargetMarkerPosition.Y - ColorViewModel.RightArrowSize / 2);
break;
2023-01-21 22:01:10 +08:00
}
2023-01-20 22:43:08 +08:00
case ConnectorOrientation.Bottom:
{
EndPoint = new PointBase(PathGeneratorResult.TargetMarkerPosition.X - ColorViewModel.RightArrowSize / 2, PathGeneratorResult.TargetMarkerPosition.Y - ColorViewModel.RightArrowSize);
break;
}
default:
{
EndPoint = PathGeneratorResult.TargetMarkerPosition;
break;
}
}
2023-01-15 11:59:51 +08:00
StartAngle = PathGeneratorResult.SourceMarkerAngle;
EndAngle = PathGeneratorResult.TargetMarkerAngle;
2023-01-14 21:52:05 +08:00
}
2021-07-23 09:42:22 +08:00
private void DeleteConnection(object args)
{
if (this.Parent is IDiagramViewModel)
{
var diagramVM = this.Parent as IDiagramViewModel;
diagramVM.RemoveItemCommand.Execute(this);
}
}
2023-01-15 11:59:51 +08:00
private void AddVertex(object parameter)
{
MouseButtonEventArgs mosueArg = ((EventToCommandArgs)parameter).EventArgs as MouseButtonEventArgs;
var position = mosueArg.GetPosition(((EventToCommandArgs)parameter).Sender as IInputElement);
2023-01-20 22:43:08 +08:00
2023-01-21 22:01:10 +08:00
var vertice = new LinkVertexModel(this, new PointBase(position.X, position.Y));
vertice.PropertyChanged += new WeakINPCEventHandler(ConnectorViewModel_PropertyChanged).Handler;
Vertices.Add(vertice);
UpdatePathGeneratorResult();
2023-01-15 11:59:51 +08:00
if (!((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control))
{
ShouldInsertAnchor = false;
}
}
2023-01-12 23:02:53 +08:00
private (PointBase? source, PointBase? target) FindConnectionPoints(PointBase[] route)
{
if (IsPortless) // Portless
{
if (SourceConnectorInfo.DataItem == null || (IsFullConnection && SinkConnectorInfoFully.DataItem == null))
return (null, null);
var sourceCenter = SourceConnectorInfo.DataItem.GetBounds().Center;
var targetCenter = SinkConnectorInfoFully?.DataItem?.GetBounds().Center ?? OnGoingPosition;
var firstPt = route.Length > 0 ? route[0] : targetCenter;
var secondPt = route.Length > 0 ? route[0] : sourceCenter;
var sourceLine = new LineBase(firstPt, sourceCenter);
var targetLine = new LineBase(secondPt, targetCenter);
var sourceIntersections = SourceConnectorInfo.DataItem.GetShape().GetIntersectionsWithLine(sourceLine);
var targetIntersections = SinkConnectorInfoFully.DataItem.GetShape()?.GetIntersectionsWithLine(targetLine) ?? new PointBase[] { OnGoingPosition };
var sourceIntersection = GetClosestPointTo(sourceIntersections, firstPt);
var targetIntersection = GetClosestPointTo(targetIntersections, secondPt);
return (sourceIntersection ?? sourceCenter, targetIntersection ?? targetCenter);
}
else
{
var source = SourceConnectorInfo.MiddlePosition;//GetPortPositionBasedOnAlignment(SourceConnectorInfo, ColorViewModel.LeftArrowSizeStyle);
var target = SinkConnectorInfo.MiddlePosition;// GetPortPositionBasedOnAlignment(SinkConnectorInfoFully, ColorViewModel.RightArrowSizeStyle);
return (source, target);
2023-01-12 23:02:53 +08:00
}
}
private PointBase? GetPortPositionBasedOnAlignment(ConnectorInfoBase port, ArrowSizeStyle marker)
{
if (port == null)
return null;
if (marker == null)
2023-01-15 20:27:39 +08:00
return port.MiddlePosition;
2023-01-12 23:02:53 +08:00
2023-01-15 20:27:39 +08:00
var pt = port.Position;
2023-01-12 23:02:53 +08:00
switch (port.Orientation)
{
case ConnectorOrientation.Top:
return new PointBase(pt.X + port.ConnectorWidth / 2, pt.Y);
case ConnectorOrientation.TopRight:
return new PointBase(pt.X + port.ConnectorWidth, pt.Y);
case ConnectorOrientation.Right:
return new PointBase(pt.X + port.ConnectorWidth, pt.Y + port.ConnectorHeight / 2);
case ConnectorOrientation.BottomRight:
return new PointBase(pt.X + port.ConnectorWidth, pt.Y + port.ConnectorHeight);
case ConnectorOrientation.Bottom:
return new PointBase(pt.X + port.ConnectorWidth / 2, pt.Y + port.ConnectorHeight);
case ConnectorOrientation.BottomLeft:
return new PointBase(pt.X, pt.Y + port.ConnectorHeight);
case ConnectorOrientation.Left:
return new PointBase(pt.X, pt.Y + port.ConnectorHeight / 2);
default:
return pt;
}
}
private PointBase? GetClosestPointTo(IEnumerable<PointBase> points, PointBase point)
{
var minDist = double.MaxValue;
PointBase? minPoint = null;
foreach (var pt in points)
{
var dist = pt.DistanceTo(point);
if (dist < minDist)
{
minDist = dist;
minPoint = pt;
}
}
return minPoint;
}
2023-01-21 22:01:10 +08:00
#region
protected override void ExecuteEditCommand(object param)
{
if (this.OutTextItem != null) return;
AddText("");
}
public void AddText(string text)
{
if (this.Parent is IDiagramViewModel)
{
var diagramVM = this.Parent as IDiagramViewModel;
TextDesignerItemViewModel textitem = new TextDesignerItemViewModel();
textitem.ItemWidth = Double.NaN;
textitem.ItemHeight = double.NaN;
//if (this.PathMode == DrawMode.ConnectingLineBoundary.ToString())
//{
// var mid = (int)(ConnectionPoints.Count / 2);
// var p = PathGenerators.SegmentMiddlePoint(ConnectionPoints[mid - 1], ConnectionPoints[mid]);
// textitem.Left = this.Area.Left + p.X + 2;
// textitem.Top = this.Area.Top + p.Y - 15;
//}
//else
{
textitem.Left = this.Area.Left + this.Area.Width / 2 - 16;
textitem.Top = this.Area.Top + this.Area.Height / 2 - 5;
}
textitem.Watermark = null;
textitem.ZIndex = diagramVM.Items.Count;
textitem.ParentId = this.Id;
textitem.ParentItem = this;
textitem.ColorViewModel.FillColor = new ColorObject() { Color = Colors.White };
textitem.Text = text;
diagramVM.DirectAddItemCommand.Execute(textitem);
2023-01-12 23:02:53 +08:00
2023-01-21 22:01:10 +08:00
this.OutTextItem = textitem;
}
}
2023-01-12 23:02:53 +08:00
2023-01-21 22:01:10 +08:00
public void OutTextItemLocation(RectangleBase oldArea, RectangleBase newArea)
{
if (this.OutTextItem is TextDesignerItemViewModel text)
{
var oldpoint = new PointBase(oldArea.Left + oldArea.Width / 2, oldArea.Top + oldArea.Height / 2);
var newpoint = new PointBase(newArea.Left + newArea.Width / 2, newArea.Top + newArea.Height / 2);
text.Left = text.Left + newpoint.X - oldpoint.X;
text.Top = text.Top + newpoint.Y - oldpoint.Y;
}
}
#endregion
2021-07-23 09:42:22 +08:00
}
}