diff --git a/AIStudio.Wpf.DiagramDesigner/Controls/PointDragThumb.cs b/AIStudio.Wpf.DiagramDesigner/Controls/PointDragThumb.cs index c169d14..17c0ccf 100644 --- a/AIStudio.Wpf.DiagramDesigner/Controls/PointDragThumb.cs +++ b/AIStudio.Wpf.DiagramDesigner/Controls/PointDragThumb.cs @@ -33,19 +33,8 @@ namespace AIStudio.Wpf.DiagramDesigner.Controls { if (this.DataContext is ConnectorPoint point) { - double minLeft = double.MaxValue; - double minTop = double.MaxValue; - - double left = point.X; - double top = point.Y; - minLeft = double.IsNaN(left) ? 0 : Math.Min(left, minLeft); - minTop = double.IsNaN(top) ? 0 : Math.Min(top, minTop); - - double deltaHorizontal = Math.Max(-minLeft, e.HorizontalChange); - double deltaVertical = Math.Max(-minTop, e.VerticalChange); - - point.X += deltaHorizontal; - point.Y += deltaVertical; + point.X += e.HorizontalChange; + point.Y += e.VerticalChange; } } diff --git a/AIStudio.Wpf.DiagramDesigner/PathGenerators/PathGenerators.Smooth.cs b/AIStudio.Wpf.DiagramDesigner/PathGenerators/PathGenerators.Smooth.cs index 815ca47..e44b1cb 100644 --- a/AIStudio.Wpf.DiagramDesigner/PathGenerators/PathGenerators.Smooth.cs +++ b/AIStudio.Wpf.DiagramDesigner/PathGenerators/PathGenerators.Smooth.cs @@ -32,7 +32,12 @@ namespace AIStudio.Wpf.DiagramDesigner double targetAngle = TargetMarkerAdjustement(route, link.ColorViewModel.RightArrowSize); BezierSpline.GetCurveControlPoints(route, out var firstControlPoints, out var secondControlPoints); - var paths = new string[firstControlPoints.Length]; + + DoShift(route, link); + DoShift(firstControlPoints, link); + DoShift(secondControlPoints, link); + + var paths = new string[firstControlPoints.Length]; for (var i = 0; i < firstControlPoints.Length; i++) { diff --git a/AIStudio.Wpf.DiagramDesigner/Routers/Routers.Normal.cs b/AIStudio.Wpf.DiagramDesigner/Routers/Routers.Normal.cs index d6aa2ed..1523303 100644 --- a/AIStudio.Wpf.DiagramDesigner/Routers/Routers.Normal.cs +++ b/AIStudio.Wpf.DiagramDesigner/Routers/Routers.Normal.cs @@ -7,7 +7,7 @@ namespace AIStudio.Wpf.DiagramDesigner { public static PointBase[] Normal(IDiagramViewModel _, ConnectorViewModel link) { - return link.Vertices.Select(v => (PointBase)v).ToArray(); + return link.Vertices.Select(v => v.MiddlePosition).ToArray(); } } } diff --git a/AIStudio.Wpf.DiagramDesigner/UserControls/DiagramControl.xaml b/AIStudio.Wpf.DiagramDesigner/UserControls/DiagramControl.xaml index 4c87c8a..36597e1 100644 --- a/AIStudio.Wpf.DiagramDesigner/UserControls/DiagramControl.xaml +++ b/AIStudio.Wpf.DiagramDesigner/UserControls/DiagramControl.xaml @@ -542,7 +542,7 @@ @@ -600,7 +600,7 @@ - + @@ -616,6 +616,7 @@ + diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/ConnectorPoint.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/ConnectorPoint.cs index 2623f95..1b79d77 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/ConnectorPoint.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/ConnectorPoint.cs @@ -91,14 +91,14 @@ namespace AIStudio.Wpf.DiagramDesigner } } - public PointBase Position + public virtual PointBase Position { get { return new PointBase(Left, Top); } } - public PointBase MiddlePosition => new PointBase(Left + ConnectorWidth / 2, Top + ConnectorHeight / 2); + public virtual PointBase MiddlePosition => new PointBase(Left + ConnectorWidth / 2, Top + ConnectorHeight / 2); private double connectorWidth = 8; public double ConnectorWidth diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/ConnectorViewModel.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/ConnectorViewModel.cs index e0b3f16..95704f5 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/ConnectorViewModel.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/ConnectorViewModel.cs @@ -46,7 +46,8 @@ namespace AIStudio.Wpf.DiagramDesigner { this.ColorViewModel.RightArrowPathStyle = ArrowPathStyle.None; } - ColorViewModel.PropertyChanged += ColorViewModel_PropertyChanged; + ColorViewModel.PropertyChanged += ConnectorViewModel_PropertyChanged; + this.PropertyChanged += ConnectorViewModel_PropertyChanged; var routetype = GlobalType.AllTypes.Where(p => typeof(IRouter).IsAssignableFrom(p)).FirstOrDefault(p => p.Name == RouterMode); Router = routetype != null ? (System.Activator.CreateInstance(routetype) as IRouter) : new RouterNormal(); @@ -57,18 +58,12 @@ namespace AIStudio.Wpf.DiagramDesigner this.SourceConnectorInfo = sourceConnectorInfo; this.SinkConnectorInfo = sinkConnectorInfo; DeleteConnectionCommand = new SimpleCommand(DeleteConnection); - AddVertexCommand = new SimpleCommand(AddVertex); + AddVertexCommand = new SimpleCommand(AddVertex); } - private void ColorViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) + private void ConnectorViewModel_PropertyChanged1(object sender, PropertyChangedEventArgs e) { - if (e.PropertyName == nameof(ColorViewModel.LeftArrowPathStyle) || - e.PropertyName == nameof(ColorViewModel.LeftArrowSizeStyle) || - e.PropertyName == nameof(ColorViewModel.RightArrowPathStyle) || - e.PropertyName == nameof(ColorViewModel.RightArrowSizeStyle)) - { - UpdatePathGeneratorResult(); - } + throw new NotImplementedException(); } public override SelectableDesignerItemBase ToXmlObject() @@ -90,6 +85,7 @@ namespace AIStudio.Wpf.DiagramDesigner return typeof(ConnectionItem); } + #region 属性 private PointBase _sourceA; public PointBase SourceA { @@ -99,10 +95,7 @@ namespace AIStudio.Wpf.DiagramDesigner } set { - if (SetProperty(ref _sourceA, value)) - { - UpdateArea(); - } + SetProperty(ref _sourceA, value); } } @@ -115,37 +108,7 @@ namespace AIStudio.Wpf.DiagramDesigner } set { - if (SetProperty(ref _sourceB, value)) - { - UpdateArea(); - } - } - } - - private ObservableCollection _connectionPoints = new ObservableCollection(); - public ObservableCollection ConnectionPoints - { - get - { - return _connectionPoints; - } - private set - { - if (_connectionPoints != null) - { - foreach (var connectionPoint in _connectionPoints) - { - connectionPoint.PropertyChanged -= new WeakINPCEventHandler(ConnectionPoint_PropertyChanged).Handler; - } - } - SetProperty(ref _connectionPoints, value); - if (_connectionPoints != null) - { - foreach (var connectionPoint in _connectionPoints) - { - connectionPoint.PropertyChanged += new WeakINPCEventHandler(ConnectionPoint_PropertyChanged).Handler; - } - } + SetProperty(ref _sourceB, value); } } @@ -210,12 +173,7 @@ namespace AIStudio.Wpf.DiagramDesigner } private set { - RectangleBase oldarea = _area; - if (SetProperty(ref _area, value)) - { - UpdatePathGeneratorResult(); - OutTextItemLocation(oldarea, value); - } + SetProperty(ref _area, value); } } @@ -266,7 +224,19 @@ namespace AIStudio.Wpf.DiagramDesigner } //待完善这两处 - public List Vertices { get; } = new List(); + private ObservableCollection _vertices = new ObservableCollection(); + public ObservableCollection Vertices + { + get + { + return _vertices; + } + private set + { + SetProperty(ref _vertices, value); + } + } + public List Labels { get; set; } = new List(); public virtual Dictionary PropertiesSetting @@ -289,11 +259,7 @@ namespace AIStudio.Wpf.DiagramDesigner } set { - if (SetProperty(ref _sourceConnectorInfo, value)) - { - SourceA = PointHelper.GetPointForConnector(_sourceConnectorInfo); - (_sourceConnectorInfo.DataItem as INotifyPropertyChanged).PropertyChanged += new WeakINPCEventHandler(ConnectorViewModel_PropertyChanged).Handler; - } + SetProperty(ref _sourceConnectorInfo, value); } } @@ -306,14 +272,8 @@ namespace AIStudio.Wpf.DiagramDesigner } set { - if (SetProperty(ref _sinkConnectorInfo, value)) - { - SourceB = _sinkConnectorInfo.Position; - if (_sinkConnectorInfo is FullyCreatedConnectorInfo) - { - (((FullyCreatedConnectorInfo)_sinkConnectorInfo).DataItem as INotifyPropertyChanged).PropertyChanged += new WeakINPCEventHandler(ConnectorViewModel_PropertyChanged).Handler; - } - } + SetProperty(ref _sinkConnectorInfo, value); + } } @@ -350,6 +310,95 @@ namespace AIStudio.Wpf.DiagramDesigner } public bool IsPortless => SourceConnectorInfo?.DataItem?.Connectors?.Count() == 0; + #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; + + } + } + } private void UpdateArea() { @@ -405,18 +454,19 @@ namespace AIStudio.Wpf.DiagramDesigner case ConnectorOrientation.Left: { EndPoint = new PointBase(PathGeneratorResult.TargetMarkerPosition.X, PathGeneratorResult.TargetMarkerPosition.Y - ColorViewModel.RightArrowSize / 2); - break; } - + break; + } + case ConnectorOrientation.Top: { EndPoint = new PointBase(PathGeneratorResult.TargetMarkerPosition.X - ColorViewModel.RightArrowSize / 2, PathGeneratorResult.TargetMarkerPosition.Y); break; - } + } case ConnectorOrientation.Right: { EndPoint = new PointBase(PathGeneratorResult.TargetMarkerPosition.X - ColorViewModel.RightArrowSize, PathGeneratorResult.TargetMarkerPosition.Y - ColorViewModel.RightArrowSize / 2); break; - } + } case ConnectorOrientation.Bottom: { EndPoint = new PointBase(PathGeneratorResult.TargetMarkerPosition.X - ColorViewModel.RightArrowSize / 2, PathGeneratorResult.TargetMarkerPosition.Y - ColorViewModel.RightArrowSize); @@ -433,48 +483,6 @@ namespace AIStudio.Wpf.DiagramDesigner EndAngle = PathGeneratorResult.TargetMarkerAngle; } - - - private void ConnectorViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) - { - switch (e.PropertyName) - { - case "ItemHeight": - case "ItemWidth": - case "Left": - case "Top": - SourceA = PointHelper.GetPointForConnector(this.SourceConnectorInfo); - if (IsFullConnection) - { - SourceB = PointHelper.GetPointForConnector(this.SinkConnectorInfoFully); - } - break; - - } - } - - private void ConnectionPoint_PropertyChanged(object sender, PropertyChangedEventArgs e) - { - switch (e.PropertyName) - { - case "Left": - case "Top": - RaisePropertyChanged(nameof(ConnectionPoints)); - break; - - } - } - - public SimpleCommand DeleteConnectionCommand - { - get; set; - } - - public SimpleCommand AddVertexCommand - { - get; set; - } - private void DeleteConnection(object args) { if (this.Parent is IDiagramViewModel) @@ -489,7 +497,10 @@ namespace AIStudio.Wpf.DiagramDesigner MouseButtonEventArgs mosueArg = ((EventToCommandArgs)parameter).EventArgs as MouseButtonEventArgs; var position = mosueArg.GetPosition(((EventToCommandArgs)parameter).Sender as IInputElement); - ConnectionPoints.Add(new ConnectorPoint(position.X, position.Y)); + var vertice = new LinkVertexModel(this, new PointBase(position.X, position.Y)); + vertice.PropertyChanged += new WeakINPCEventHandler(ConnectorViewModel_PropertyChanged).Handler; + Vertices.Add(vertice); + UpdatePathGeneratorResult(); if (!((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)) { @@ -497,58 +508,6 @@ namespace AIStudio.Wpf.DiagramDesigner } } - 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); - - this.OutTextItem = textitem; - } - } - - 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; - } - } - private (PointBase? source, PointBase? target) FindConnectionPoints(PointBase[] route) { if (IsPortless) // Portless @@ -624,7 +583,58 @@ namespace AIStudio.Wpf.DiagramDesigner return minPoint; } + #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); + + this.OutTextItem = textitem; + } + } + + 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 } } diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/LinkVertexModel.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/LinkVertexModel.cs index a81c1e4..7155fe1 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/LinkVertexModel.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BaseViewModel/LinkVertexModel.cs @@ -16,5 +16,15 @@ namespace AIStudio.Wpf.DiagramDesigner get; } + public override PointBase Position + { + get + { + return new PointBase(Parent.Area.Left + Left, Parent.Area.Top + Top); + } + } + + public override PointBase MiddlePosition => new PointBase(Parent.Area.Left + Left + ConnectorWidth / 2, Parent.Area.Top + Top + ConnectorHeight / 2); + } } diff --git a/AIStudio.Wpf.DiagramDesigner/ViewModels/BindableBase.cs b/AIStudio.Wpf.DiagramDesigner/ViewModels/BindableBase.cs index 5588858..c4819c8 100644 --- a/AIStudio.Wpf.DiagramDesigner/ViewModels/BindableBase.cs +++ b/AIStudio.Wpf.DiagramDesigner/ViewModels/BindableBase.cs @@ -17,40 +17,6 @@ namespace AIStudio.Wpf.DiagramDesigner /// public event PropertyChangedEventHandler PropertyChanged; - /// - /// Checks if a property already matches a desired value. Sets the property and - /// notifies listeners only when necessary. - /// - /// Type of the property. - /// Reference to a property with both getter and setter. - /// Desired value for the property. - /// Name of the property used to notify listeners. This - /// value is optional and can be provided automatically when invoked from compilers that - /// support CallerMemberName. - /// True if the value was changed, false if the existing value matched the - /// desired value. - protected virtual bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null) - { - if (EqualityComparer.Default.Equals(storage, value)) - return false; - - if (propertyName == "IsSelected") - { - - } - else if (this.ContainsProperty("IsReadOnly")) - { - if (object.Equals(this.GetPropertyValue("IsReadOnly"), true)) - return false; - } - - var old = storage; - storage = value; - RaisePropertyChanged(propertyName, old, value); - - return true; - } - private Dictionary _oldDic = new Dictionary(); public void SetOldValue(T value, string propertyName, string guid = null) { @@ -78,6 +44,44 @@ namespace AIStudio.Wpf.DiagramDesigner } } + /// + /// Checks if a property already matches a desired value. Sets the property and + /// notifies listeners only when necessary. + /// + /// Type of the property. + /// Reference to a property with both getter and setter. + /// Desired value for the property. + /// Name of the property used to notify listeners. This + /// value is optional and can be provided automatically when invoked from compilers that + /// support CallerMemberName. + /// True if the value was changed, false if the existing value matched the + /// desired value. + protected virtual bool SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null) + { + if (EqualityComparer.Default.Equals(storage, value)) + return false; + + if (propertyName == "IsSelected") + { + + } + else if (this.ContainsProperty("IsReadOnly")) + { + if (object.Equals(this.GetPropertyValue("IsReadOnly"), true)) + return false; + } + + if (propertyName == "Area") + { + + } + var old = storage; + storage = value; + RaisePropertyChanged(propertyName, old, value); + + return true; + } + /// /// Checks if a property already matches a desired value. Sets the property and /// notifies listeners only when necessary. @@ -93,11 +97,22 @@ namespace AIStudio.Wpf.DiagramDesigner /// desired value. protected virtual bool SetProperty(ref T storage, T value, Action onChanged, [CallerMemberName] string propertyName = null) { - if (EqualityComparer.Default.Equals(storage, value)) return false; + if (EqualityComparer.Default.Equals(storage, value)) return false; + if (propertyName == "IsSelected") + { + + } + else if (this.ContainsProperty("IsReadOnly")) + { + if (object.Equals(this.GetPropertyValue("IsReadOnly"), true)) + return false; + } + + var old = storage; storage = value; onChanged?.Invoke(); - RaisePropertyChanged(propertyName); + RaisePropertyChanged(propertyName, old, value); return true; }