diff --git a/AIStudio.Wpf.DiagramDesigner/Controls/DesignerCanvas.cs b/AIStudio.Wpf.DiagramDesigner/Controls/DesignerCanvas.cs index 589e330..2d2dda6 100644 --- a/AIStudio.Wpf.DiagramDesigner/Controls/DesignerCanvas.cs +++ b/AIStudio.Wpf.DiagramDesigner/Controls/DesignerCanvas.cs @@ -410,8 +410,8 @@ namespace AIStudio.Wpf.DiagramDesigner if (EnableSnapping) { - var nearPort = FindNearPortToAttachTo(); - if (nearPort != null || partialConnection.SinkConnectorInfoFully != null) + var nearPort = _viewModel.FindNearPortToAttachTo(partialConnection); + if (nearPort != null) { partialConnection.SinkConnectorInfo = nearPort; } @@ -632,20 +632,6 @@ namespace AIStudio.Wpf.DiagramDesigner e.Handled = true; this.Focus(); - } - - #region 自动依附节点 - private FullyCreatedConnectorInfo FindNearPortToAttachTo() - { - foreach (var port in _viewModel.Items.OfType().ToList().SelectMany(n => n.Connectors)) - { - if (partialConnection.OnGoingPosition.DistanceTo(port.Position) < SnappingRadius && - partialConnection.SourceConnectorInfoFully?.CanAttachTo(port) == true) - return port; - } - - return null; - } - #endregion + } } } \ No newline at end of file diff --git a/AIStudio.Wpf.DiagramDesigner/Controls/PointDragThumb.cs b/AIStudio.Wpf.DiagramDesigner/Controls/PointDragThumb.cs index 258e735..fe38b97 100644 --- a/AIStudio.Wpf.DiagramDesigner/Controls/PointDragThumb.cs +++ b/AIStudio.Wpf.DiagramDesigner/Controls/PointDragThumb.cs @@ -21,12 +21,18 @@ namespace AIStudio.Wpf.DiagramDesigner.Controls private void DragThumb_DragStarted(object sender, DragStartedEventArgs e) { - + if (this.DataContext is ConnectorPointModel point) + { + point.DragStart = true; + } } private void DragThumb_DragCompleted(object sender, DragCompletedEventArgs e) { - + if (this.DataContext is ConnectorPointModel point) + { + point.DragStart = false; + } } void DragThumb_DragDelta(object sender, DragDeltaEventArgs e) diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/ConnectionViewModel.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/ConnectionViewModel.cs index 8aec2d7..12876af 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/ConnectionViewModel.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/ConnectionViewModel.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Windows; +using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using AIStudio.Wpf.DiagramDesigner.Controls; @@ -480,7 +481,7 @@ namespace AIStudio.Wpf.DiagramDesigner protected override void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) { - if (IsLoaded == false) return; + if (IsLoaded == false || IsInternalChanged == true ) return; if (sender is ConnectionViewModel) { @@ -513,10 +514,6 @@ namespace AIStudio.Wpf.DiagramDesigner { SourceConnectorInfoFully.DataItem.PropertyChanged += new WeakINPCEventHandler(Item_PropertyChanged).Handler; } - //else if (SourceConnectorInfoPart != null) - //{ - // SourceConnectorInfoPart.PropertyChanged += new WeakINPCEventHandler(Item_PropertyChanged).Handler; - //} } break; case nameof(SinkConnectorInfo): @@ -527,10 +524,6 @@ namespace AIStudio.Wpf.DiagramDesigner { SinkConnectorInfoFully.DataItem.PropertyChanged += new WeakINPCEventHandler(Item_PropertyChanged).Handler; } - //else if (SinkConnectorInfoPart != null) - //{ - // SinkConnectorInfoPart.PropertyChanged += new WeakINPCEventHandler(Item_PropertyChanged).Handler; - //} } break; case nameof(IsSelected): @@ -580,15 +573,48 @@ namespace AIStudio.Wpf.DiagramDesigner break; } } - else if (sender is ConnectorVertexModel) + else if (sender is ConnectorVertexModel connectorVertexModel) { switch (e.PropertyName) { case nameof(ConnectorPointModel.X): case nameof(ConnectorPointModel.Y): - UpdatePathGeneratorResult(); + if (connectorVertexModel.ConnectorVertexType == ConnectorVertexType.None) + { + UpdatePathGeneratorResult(); + } + else if(connectorVertexModel.ConnectorVertexType == ConnectorVertexType.Start) + { + SetSourcePort(new PartCreatedConnectorInfo(connectorVertexModel.Position.X, connectorVertexModel.Position.Y)); + this.ZIndex = -1; + } + else if (connectorVertexModel.ConnectorVertexType == ConnectorVertexType.End) + { + SetSinkPort(new PartCreatedConnectorInfo(connectorVertexModel.Position.X, connectorVertexModel.Position.Y)); + this.ZIndex = -1; + } + break; + case nameof(ConnectorPointModel.DragStart): + if (connectorVertexModel.DragStart == false) + { + if (connectorVertexModel.ConnectorVertexType == ConnectorVertexType.Start) + { + var nearPort = Root.FindNearPortToAttachTo(this, ConnectorVertexType.Start); + if (nearPort != null) + { + SetSourcePort(nearPort); + } + } + else if (connectorVertexModel.ConnectorVertexType == ConnectorVertexType.End) + { + var nearPort = Root.FindNearPortToAttachTo(this, ConnectorVertexType.End); + if (nearPort != null) + { + SetSinkPort(nearPort); + } + } + } break; - } } else if (sender is ConnectorLabelModel linkLabelModel) @@ -613,21 +639,6 @@ namespace AIStudio.Wpf.DiagramDesigner } } } - //else if (sender is PartCreatedConnectorInfo) - //{ - // if (e.PropertyName == "Position") - // { - // if (sender == SourceConnectorInfoPart) - // { - // SourceA = SourceConnectorInfo.Position; - // } - // else if (sender == SinkConnectorInfoPart) - // { - // SourceB = SinkConnectorInfo.Position; - // } - // UpdatePathGeneratorResult(); - // } - //} else if (sender is ColorViewModel) { @@ -679,29 +690,31 @@ namespace AIStudio.Wpf.DiagramDesigner var startVertice = Vertices.FirstOrDefault(p => p.ConnectorVertexType == ConnectorVertexType.Start); if (startVertice == null) - { - startVertice = new ConnectorVertexModel(this, StartPoint) { ConnectorVertexType = ConnectorVertexType.Start }; - startVertice.ColorViewModel.FillColor.Color = Colors.DarkRed; - startVertice.ColorViewModel.LineColor.Color = Colors.DarkRed; - Vertices.Add(startVertice); + { + startVertice = AddEndsVertex(StartPoint, ConnectorVertexType.Start); } else { + IsInternalChanged = true; startVertice.SetPosition(StartPoint); + IsInternalChanged = true; } + startVertice.ColorViewModel.FillColor.Color = SourceConnectorInfoFully != null? Colors.DarkRed : Colors.Blue; + startVertice.ColorViewModel.LineColor.Color = SourceConnectorInfoFully != null ? Colors.DarkRed : Colors.Blue; var endVertice = Vertices.FirstOrDefault(p => p.ConnectorVertexType == ConnectorVertexType.End); if (endVertice == null) { - endVertice = new ConnectorVertexModel(this, EndPoint) { ConnectorVertexType = ConnectorVertexType.End }; - endVertice.ColorViewModel.FillColor.Color = Colors.DarkRed; - endVertice.ColorViewModel.LineColor.Color = Colors.DarkRed; - Vertices.Add(endVertice); + endVertice = AddEndsVertex(EndPoint, ConnectorVertexType.End); } else { + IsInternalChanged = true; endVertice.SetPosition(EndPoint); + IsInternalChanged = false; } + endVertice.ColorViewModel.FillColor.Color = SinkConnectorInfoFully != null ? Colors.DarkRed : Colors.Blue; + endVertice.ColorViewModel.LineColor.Color = SinkConnectorInfoFully != null ? Colors.DarkRed : Colors.Blue; var paths = Labels.Count > 0 ? PathGeneratorResult.Paths.Select(p => new SvgPath(p)).ToArray() : Array.Empty(); foreach (var label in Labels) @@ -809,12 +822,12 @@ namespace AIStudio.Wpf.DiagramDesigner return minPoint; } - public void SetSourcePort(FullyCreatedConnectorInfo port) + public void SetSourcePort(ConnectorInfoBase port) { SourceConnectorInfo = port; } - public void SetSinkPort(FullyCreatedConnectorInfo port) + public void SetSinkPort(ConnectorInfoBase port) { SinkConnectorInfo = port; } @@ -943,6 +956,16 @@ namespace AIStudio.Wpf.DiagramDesigner UpdatePathGeneratorResult(); } + public ConnectorVertexModel AddEndsVertex(PointBase pointBase, ConnectorVertexType connectorVertexType) + { + var vertice = new ConnectorVertexModel(this, pointBase); + vertice.ConnectorVertexType= connectorVertexType; + vertice.PropertyChanged += new WeakINPCEventHandler(Item_PropertyChanged).Handler; + Vertices.Add(vertice); + + return vertice; + } + public void RemoveVertex(ConnectorVertexModel vertice) { Vertices.Remove(vertice); diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/ConnectorInfoBase.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/ConnectorInfoBase.cs index 5a9e991..7261b14 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/ConnectorInfoBase.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/ConnectorInfoBase.cs @@ -143,7 +143,10 @@ namespace AIStudio.Wpf.DiagramDesigner { ConnectorHeight = ScreenHelper.MmToWidth(value); } - } + } + + public virtual bool CanAttachTo(ConnectorInfoBase port) + => port != this && !port.IsReadOnly; #endregion } } diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/ConnectorPointModel.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/ConnectorPointModel.cs index 088b693..2b28373 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/ConnectorPointModel.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/ConnectorPointModel.cs @@ -144,29 +144,48 @@ namespace AIStudio.Wpf.DiagramDesigner } public virtual PointBase MiddlePosition => new PointBase(X, Y); - private double connectorWidth = 8; + private double _connectorWidth = 8; public double ConnectorWidth { get { - return connectorWidth; + return _connectorWidth; } set { - connectorWidth = value; + _connectorWidth = value; } } - private double connectorHeight = 8; + private double _connectorHeight = 8; public double ConnectorHeight { get { - return connectorHeight; + return _connectorHeight; } set { - connectorHeight = value; + _connectorHeight = value; + } + } + + public void SetPosition(PointBase position) + { + X = position.X; + Y = position.Y; + } + + private bool _dragStart; + public bool DragStart + { + get + { + return _dragStart; + } + set + { + SetProperty(ref _dragStart, value); } } diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/ConnectorVertexModel.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/ConnectorVertexModel.cs index 0e0f810..abdc049 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/ConnectorVertexModel.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/ConnectorVertexModel.cs @@ -86,13 +86,6 @@ namespace AIStudio.Wpf.DiagramDesigner Connector.RemoveVertex(this); } } - - public void SetPosition(PointBase position) - { - X = position.X; - Y = position.Y; - } - } public enum ConnectorVertexType diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/FullyCreatedConnectorInfo.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/FullyCreatedConnectorInfo.cs index d48facb..bdc5a5b 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/FullyCreatedConnectorInfo.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/FullyCreatedConnectorInfo.cs @@ -316,8 +316,17 @@ namespace AIStudio.Wpf.DiagramDesigner } } - public virtual bool CanAttachTo(FullyCreatedConnectorInfo port) - => port != this && !port.IsReadOnly && DataItem != port.DataItem; + public override bool CanAttachTo(ConnectorInfoBase port) + { + if (port is FullyCreatedConnectorInfo fullyCreatedConnectorInfo) + { + return port != this && !port.IsReadOnly && DataItem != fullyCreatedConnectorInfo.DataItem; + } + else + { + return base.CanAttachTo(port); + } + } public override string ToString() { diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/LogicalConnectorInfo.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/LogicalConnectorInfo.cs index 4223d14..988f0b1 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/LogicalConnectorInfo.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/Connector/LogicalConnectorInfo.cs @@ -136,7 +136,7 @@ namespace AIStudio.Wpf.DiagramDesigner } - public override bool CanAttachTo(FullyCreatedConnectorInfo port) + public override bool CanAttachTo(ConnectorInfoBase port) { if (!base.CanAttachTo(port)) { diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramOption.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramOption.cs index 52dcf63..1a0e29f 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramOption.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramOption.cs @@ -17,6 +17,11 @@ namespace AIStudio.Wpf.DiagramDesigner { get; set; } = new ShortcutOption(); + + public SnappingOption SnappingOption + { + get; set; + } = new SnappingOption(); } public class LayoutOption @@ -24,6 +29,22 @@ namespace AIStudio.Wpf.DiagramDesigner } + public class SnappingOption + { + public bool EnableSnapping + { + get; set; + } + public double SnappingRadius + { + get; set; + } = 50; + public double HittingRadius + { + get; set; + } = 20; + } + public class ShortcutOption { [Description("Select All shortcut (CTRL+A by default)")] diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramViewModel.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramViewModel.cs index 6279e73..6cbe5ac 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramViewModel.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/DiagramViewModel.cs @@ -3350,5 +3350,46 @@ namespace AIStudio.Wpf.DiagramDesigner } } #endregion + + #region 自动依附节点 + public FullyCreatedConnectorInfo FindNearPortToAttachTo(ConnectionViewModel partialConnection, ConnectorVertexType connectorVertexType) + { + if (partialConnection == null) + return null; + + foreach (var port in Items.OfType().ToList().SelectMany(n => n.Connectors)) + { + if (connectorVertexType == ConnectorVertexType.Start) + { + if (partialConnection.SourceConnectorInfo.Position.DistanceTo(port.Position) < DiagramOption.SnappingOption.HittingRadius && + partialConnection.SinkConnectorInfo?.CanAttachTo(port) == true) + return port; + } + else if (connectorVertexType == ConnectorVertexType.End) + { + if (partialConnection.SinkConnectorInfo.Position.DistanceTo(port.Position) < DiagramOption.SnappingOption.HittingRadius && + partialConnection.SourceConnectorInfo?.CanAttachTo(port) == true) + return port; + } + } + + return null; + } + + public FullyCreatedConnectorInfo FindNearPortToAttachTo(ConnectionViewModel partialConnection) + { + if (partialConnection == null) + return null; + + foreach (var port in Items.OfType().ToList().SelectMany(n => n.Connectors)) + { + if (partialConnection.OnGoingPosition.DistanceTo(port.Position) < DiagramOption.SnappingOption.SnappingRadius && + partialConnection.SourceConnectorInfoFully?.CanAttachTo(port) == true) + return port; + } + + return null; + } + #endregion } } diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/SelectableViewModelBase.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/SelectableViewModelBase.cs index 000fb1e..d9febc2 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/SelectableViewModelBase.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/SelectableViewModelBase.cs @@ -147,6 +147,11 @@ namespace AIStudio.Wpf.DiagramDesigner get;set; } + public bool IsInternalChanged + { + get; set; + } + public IDiagramViewModel Root { get; set; @@ -461,12 +466,12 @@ namespace AIStudio.Wpf.DiagramDesigner protected virtual void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) { - if (IsLoaded == false) { return; } + if (IsLoaded == false || IsInternalChanged == true) { return; } } protected void FontViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { - if (IsLoaded == false) { return; } + if (IsLoaded == false || IsInternalChanged == true) { return; } if (e.PropertyName == nameof(FontViewModel.FontCase)) { @@ -478,21 +483,21 @@ namespace AIStudio.Wpf.DiagramDesigner protected void ColorViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { - if (IsLoaded == false) { return; } + if (IsLoaded == false || IsInternalChanged == true ) { return; } RaisePropertyChanged(sender, e); } protected void ShapeViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { - if (IsLoaded == false) { return; } + if (IsLoaded == false || IsInternalChanged == true) { return; } RaisePropertyChanged(sender, e); } protected void AnimationViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { - if (IsLoaded == false) { return; } + if (IsLoaded == false || IsInternalChanged == true) { return; } RaisePropertyChanged(sender, e); } diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/IDiagramViewModel.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/IDiagramViewModel.cs index 62ff4e5..4cd8cd0 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/IDiagramViewModel.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/IDiagramViewModel.cs @@ -405,6 +405,10 @@ namespace AIStudio.Wpf.DiagramDesigner void LockAction(LockObject lockObject, string propertyName, List items); #endregion + #region 公共方法 + FullyCreatedConnectorInfo FindNearPortToAttachTo(ConnectionViewModel partialConnection, ConnectorVertexType connectorVertexType); + FullyCreatedConnectorInfo FindNearPortToAttachTo(ConnectionViewModel partialConnection); + #endregion event PropertyChangedEventHandler PropertyChanged; } diff --git a/Extensions/AIStudio.Wpf.Logical/AIStudio.Wpf.Logical_yry4miqx_wpftmp.csproj b/Extensions/AIStudio.Wpf.Logical/AIStudio.Wpf.Logical_yry4miqx_wpftmp.csproj new file mode 100644 index 0000000..4c1b7c7 --- /dev/null +++ b/Extensions/AIStudio.Wpf.Logical/AIStudio.Wpf.Logical_yry4miqx_wpftmp.csproj @@ -0,0 +1,163 @@ + + + AIStudio.Wpf.Logical + obj\Debug\ + obj\ + F:\aistudio.-wpf.-diagram\Extensions\AIStudio.Wpf.Logical\obj\ + <_TargetAssemblyProjectName>AIStudio.Wpf.Logical + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file