mirror of
https://gitee.com/akwkevin/aistudio.-wpf.-diagram
synced 2026-03-14 21:46:37 +08:00
连接点可以添加文字
This commit is contained in:
@@ -125,7 +125,20 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
SetProperty(ref _colorViewModel, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IFontViewModel _fontViewModel;
|
||||
public IFontViewModel FontViewModel
|
||||
{
|
||||
get
|
||||
{
|
||||
return _fontViewModel;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetProperty(ref _fontViewModel, value);
|
||||
}
|
||||
}
|
||||
|
||||
public static ConnectorPoint operator -(ConnectorPoint a, ConnectorPoint b)
|
||||
{
|
||||
|
||||
@@ -5,10 +5,10 @@ using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
//using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometrys;
|
||||
using AIStudio.Wpf.DiagramDesigner.Helpers;
|
||||
using SvgPathProperties;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
@@ -39,7 +39,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
protected virtual void Init(FullyCreatedConnectorInfo sourceConnectorInfo, ConnectorInfoBase sinkConnectorInfo)
|
||||
{
|
||||
this.Parent = sourceConnectorInfo.DataItem.Parent;
|
||||
|
||||
|
||||
if (Parent != null && Parent.ColorViewModel != null)
|
||||
{
|
||||
this.ColorViewModel = CopyHelper.Mapper(Parent.ColorViewModel);
|
||||
@@ -60,7 +60,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
this.SourceConnectorInfo = sourceConnectorInfo;
|
||||
this.SinkConnectorInfo = sinkConnectorInfo;
|
||||
DeleteConnectionCommand = new SimpleCommand(DeleteConnection);
|
||||
AddVertexCommand = new SimpleCommand(AddVertex);
|
||||
AddVertexCommand = new SimpleCommand(AddVertex);
|
||||
}
|
||||
|
||||
protected void LoadDesignerItemViewModel(SelectableDesignerItemBase designerbase)
|
||||
@@ -112,7 +112,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
set
|
||||
{
|
||||
SetProperty(ref _sourceB, value);
|
||||
SetProperty(ref _sourceB, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
private set
|
||||
{
|
||||
SetProperty(ref _area, value);
|
||||
SetProperty(ref _area, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +241,18 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
public List<LinkLabelModel> Labels { get; set; } = new List<LinkLabelModel>();
|
||||
private ObservableCollection<LinkLabelModel> _labels = new ObservableCollection<LinkLabelModel>();
|
||||
public ObservableCollection<LinkLabelModel> Labels
|
||||
{
|
||||
get
|
||||
{
|
||||
return _labels;
|
||||
}
|
||||
private set
|
||||
{
|
||||
SetProperty(ref _labels, value);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Dictionary<string, string> PropertiesSetting
|
||||
{
|
||||
@@ -277,7 +288,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
set
|
||||
{
|
||||
SetProperty(ref _sinkConnectorInfo, value);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,6 +362,12 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
vertice.PropertyChanged += new WeakINPCEventHandler(ConnectorViewModel_PropertyChanged).Handler;
|
||||
}
|
||||
break;
|
||||
case nameof(Labels):
|
||||
foreach (var label in Labels)
|
||||
{
|
||||
label.PropertyChanged += new WeakINPCEventHandler(ConnectorViewModel_PropertyChanged).Handler;
|
||||
}
|
||||
break;
|
||||
case nameof(SourceConnectorInfo):
|
||||
SourceA = PointHelper.GetPointForConnector(SourceConnectorInfo);
|
||||
(SourceConnectorInfo.DataItem as INotifyPropertyChanged).PropertyChanged += new WeakINPCEventHandler(ConnectorViewModel_PropertyChanged).Handler;
|
||||
@@ -362,6 +379,12 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
(((FullyCreatedConnectorInfo)SinkConnectorInfo).DataItem as INotifyPropertyChanged).PropertyChanged += new WeakINPCEventHandler(ConnectorViewModel_PropertyChanged).Handler;
|
||||
}
|
||||
break;
|
||||
case nameof(IsSelected):
|
||||
if (IsSelected == false)
|
||||
{
|
||||
Labels.FirstOrDefault()?.AddToSelection(false);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -395,13 +418,35 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(ConnectorPoint.Left):
|
||||
case nameof(ConnectorPoint.Top):
|
||||
case nameof(ConnectorPoint.X):
|
||||
case nameof(ConnectorPoint.Y):
|
||||
UpdatePathGeneratorResult();
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
else if (sender is LinkLabelModel linkLabelModel)
|
||||
{
|
||||
switch (e.PropertyName)
|
||||
{
|
||||
case nameof(ConnectorPoint.X):
|
||||
{
|
||||
if (e is ValuePropertyChangedEventArgs valuePropertyChangedEventArgs)
|
||||
{
|
||||
linkLabelModel.UpdateOffsetX((double)valuePropertyChangedEventArgs.OldValue, (double)valuePropertyChangedEventArgs.NewValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case nameof(ConnectorPoint.Y):
|
||||
{
|
||||
if (e is ValuePropertyChangedEventArgs valuePropertyChangedEventArgs)
|
||||
{
|
||||
linkLabelModel.UpdateOffsetY((double)valuePropertyChangedEventArgs.OldValue, (double)valuePropertyChangedEventArgs.NewValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateArea()
|
||||
@@ -485,6 +530,12 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
|
||||
StartAngle = PathGeneratorResult.SourceMarkerAngle;
|
||||
EndAngle = PathGeneratorResult.TargetMarkerAngle;
|
||||
|
||||
var paths = Labels.Count > 0 ? PathGeneratorResult.Paths.Select(p => new SvgPath(p)).ToArray() : Array.Empty<SvgPath>();
|
||||
foreach (var label in Labels)
|
||||
{
|
||||
label.UpdatePosition(paths);
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteConnection(object args)
|
||||
@@ -496,22 +547,6 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
private void AddVertex(object parameter)
|
||||
{
|
||||
MouseButtonEventArgs mosueArg = ((EventToCommandArgs)parameter).EventArgs as MouseButtonEventArgs;
|
||||
var position = mosueArg.GetPosition(((EventToCommandArgs)parameter).Sender as IInputElement);
|
||||
|
||||
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))
|
||||
{
|
||||
ShouldInsertAnchor = false;
|
||||
}
|
||||
}
|
||||
|
||||
private (PointBase? source, PointBase? target) FindConnectionPoints(PointBase[] route)
|
||||
{
|
||||
if (IsPortless) // Portless
|
||||
@@ -544,7 +579,7 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
if (port == null)
|
||||
return null;
|
||||
|
||||
if (marker == null)
|
||||
if (marker == 0)
|
||||
return port.MiddlePosition;
|
||||
|
||||
var pt = port.Position;
|
||||
@@ -588,6 +623,22 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
|
||||
#region 双击添加
|
||||
private void AddVertex(object parameter)
|
||||
{
|
||||
MouseButtonEventArgs mosueArg = ((EventToCommandArgs)parameter).EventArgs as MouseButtonEventArgs;
|
||||
var position = mosueArg.GetPosition(((EventToCommandArgs)parameter).Sender as IInputElement);
|
||||
|
||||
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))
|
||||
{
|
||||
ShouldInsertAnchor = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ExecuteEditCommand(object param)
|
||||
{
|
||||
if (this.OutTextItem != null) return;
|
||||
@@ -596,48 +647,25 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
|
||||
public void AddText(string text)
|
||||
{
|
||||
if (this.Parent is IDiagramViewModel)
|
||||
{
|
||||
var diagramVM = this.Parent as IDiagramViewModel;
|
||||
var label = new LinkLabelModel(this, "");
|
||||
label.PropertyChanged += new WeakINPCEventHandler(ConnectorViewModel_PropertyChanged).Handler;
|
||||
label.IsSelected = true;
|
||||
Labels.Add(label);
|
||||
|
||||
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;
|
||||
}
|
||||
var paths = Labels.Count > 0 ? PathGeneratorResult.Paths.Select(p => new SvgPath(p)).ToArray() : Array.Empty<SvgPath>();
|
||||
label.UpdatePosition(paths);
|
||||
}
|
||||
|
||||
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);
|
||||
//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;
|
||||
}
|
||||
// text.Left = text.Left + newpoint.X - oldpoint.X;
|
||||
// text.Top = text.Top + newpoint.Y - oldpoint.Y;
|
||||
//}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -188,7 +188,20 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowRotate { get; set; } = true;
|
||||
private bool _showRotate = false;
|
||||
[Browsable(true)]
|
||||
public bool ShowRotate
|
||||
{
|
||||
get
|
||||
{
|
||||
return _showRotate;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetProperty(ref _showRotate, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowArrow { get; set; } = true;
|
||||
|
||||
private double _left;
|
||||
|
||||
@@ -1,32 +1,171 @@
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
using System.Linq;
|
||||
using AIStudio.Wpf.DiagramDesigner.Geometrys;
|
||||
using SvgPathProperties;
|
||||
|
||||
namespace AIStudio.Wpf.DiagramDesigner
|
||||
{
|
||||
public class LinkLabelModel : ConnectorPoint
|
||||
public class LinkLabelModel : ConnectorPoint, ISelectable
|
||||
{
|
||||
public LinkLabelModel(ConnectorViewModel parent, string id, string content, double? distance = null, ConnectorPoint offset = null)
|
||||
public LinkLabelModel(ConnectorViewModel parent, string content, double? distance = null, PointBase? offset = null)
|
||||
{
|
||||
Parent = parent;
|
||||
Content = content;
|
||||
Text = content;
|
||||
Distance = distance;
|
||||
Offset = offset;
|
||||
Offset = offset ?? new PointBase();
|
||||
FontViewModel = Parent.FontViewModel;
|
||||
ColorViewModel = Parent.ColorViewModel;
|
||||
}
|
||||
|
||||
public LinkLabelModel(ConnectorViewModel parent, string content, double? distance = null, ConnectorPoint offset = null)
|
||||
public ConnectorViewModel Parent
|
||||
{
|
||||
Parent = parent;
|
||||
Content = content;
|
||||
Distance = distance;
|
||||
Offset = offset;
|
||||
get;
|
||||
}
|
||||
|
||||
public bool IsHitTestVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
return Parent.IsHitTestVisible;
|
||||
}
|
||||
}
|
||||
|
||||
private string _text;
|
||||
public string Text
|
||||
{
|
||||
get
|
||||
{
|
||||
return _text;
|
||||
}
|
||||
set
|
||||
{
|
||||
SetProperty(ref _text, value);
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectorViewModel Parent { get; }
|
||||
public string Content { get; set; }
|
||||
/// <summary>
|
||||
/// 3 types of values are possible:
|
||||
/// <para>- A number between 0 and 1: Position relative to the link's length</para>
|
||||
/// <para>- A positive number, greater than 1: Position away from the start</para>
|
||||
/// <para>- A negative number, less than 0: Position away from the end</para>
|
||||
/// </summary>
|
||||
public double? Distance { get; set; }
|
||||
public ConnectorPoint Offset { get; set; }
|
||||
public double? Distance
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public PointBase Offset
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
private bool _isSelected;
|
||||
public bool IsSelected
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isSelected;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isSelected, value))
|
||||
{
|
||||
//如果没有文字,失去焦点自动清除
|
||||
if (_isSelected == false && string.IsNullOrEmpty(Text))
|
||||
{
|
||||
Parent.Labels.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
private bool updating = false;
|
||||
|
||||
public void UpdatePosition(SvgPath[] paths)
|
||||
{
|
||||
var position = FindPosition(paths);
|
||||
if (position == null)
|
||||
return;
|
||||
|
||||
updating = true;
|
||||
X = position.Value.X;
|
||||
Y = position.Value.Y;
|
||||
updating = false;
|
||||
}
|
||||
|
||||
public PointBase? FindPosition(SvgPath[] paths)
|
||||
{
|
||||
var totalLength = paths.Sum(p => p.Length);
|
||||
|
||||
double length;
|
||||
|
||||
|
||||
if (Distance >= 0 && Distance <= 1)
|
||||
{
|
||||
length = Distance.Value * totalLength;
|
||||
}
|
||||
else if (Distance > 1)
|
||||
{
|
||||
length = Distance.Value;
|
||||
}
|
||||
else if (Distance < 0)
|
||||
{
|
||||
length = totalLength + Distance.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
length = totalLength * (Parent.Labels.IndexOf(this) + 1) / (Parent.Labels.Count + 1);
|
||||
}
|
||||
|
||||
|
||||
foreach (var path in paths)
|
||||
{
|
||||
var pathLength = path.Length;
|
||||
if (length < pathLength)
|
||||
{
|
||||
var pt = path.GetPointAtLength(length);
|
||||
return new PointBase(pt.X + Offset.X, pt.Y + Offset.Y);
|
||||
}
|
||||
|
||||
length -= pathLength;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void UpdateOffsetX(double oldvalue, double newvalue)
|
||||
{
|
||||
if (updating == true) return;
|
||||
|
||||
Offset += new VectorBase(newvalue - oldvalue, 0);
|
||||
}
|
||||
|
||||
public void UpdateOffsetY(double oldvalue, double newvalue)
|
||||
{
|
||||
if (updating == true) return;
|
||||
|
||||
Offset += new VectorBase(0, newvalue - oldvalue);
|
||||
}
|
||||
|
||||
public void AddToSelection(bool selected)
|
||||
{
|
||||
foreach (var item in Parent.Labels)
|
||||
item.IsSelected = false;
|
||||
|
||||
if (selected == true)
|
||||
{
|
||||
IsSelected = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,6 +364,18 @@ namespace AIStudio.Wpf.DiagramDesigner
|
||||
IsSelected = select;
|
||||
}
|
||||
|
||||
public void AddToSelection(bool selected)
|
||||
{
|
||||
foreach (SelectableDesignerItemViewModelBase item in Parent.SelectedItems)
|
||||
item.IsSelected = false;
|
||||
|
||||
Parent.SelectedItems.Clear();
|
||||
if (selected == true)
|
||||
{
|
||||
Parent.SelectionService.AddToSelection(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void FontViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == "FontCase")
|
||||
|
||||
Reference in New Issue
Block a user