mirror of
https://gitee.com/akwkevin/aistudio.-wpf.-diagram
synced 2026-03-03 00:00:57 +08:00
540 lines
15 KiB
C#
540 lines
15 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Specialized;
|
|
using System.ComponentModel;
|
|
using System.Windows;
|
|
using System.Linq;
|
|
using System.Reactive.Linq;
|
|
using AIStudio.Wpf.DiagramDesigner.Models;
|
|
using AIStudio.Wpf.DiagramDesigner.Geometrys;
|
|
|
|
namespace AIStudio.Wpf.DiagramDesigner
|
|
{
|
|
public abstract class DesignerItemViewModelBase : SelectableDesignerItemViewModelBase
|
|
{
|
|
public DesignerItemViewModelBase() : this(null)
|
|
{
|
|
|
|
}
|
|
|
|
public DesignerItemViewModelBase(IDiagramViewModel root) : base(root)
|
|
{
|
|
ShapeDefiner = Shapes.Rectangle;
|
|
}
|
|
|
|
public DesignerItemViewModelBase(IDiagramViewModel root, SelectableItemBase designer) : base(root, designer)
|
|
{
|
|
ShapeDefiner = Shapes.Rectangle;
|
|
}
|
|
|
|
public DesignerItemViewModelBase(IDiagramViewModel root, SerializableItem serializableItem, string serializableType) : base(root, serializableItem, serializableType)
|
|
{
|
|
ShapeDefiner = Shapes.Rectangle;
|
|
}
|
|
|
|
public override SelectableItemBase GetSerializableObject()
|
|
{
|
|
return new DesignerItemBase(this);
|
|
}
|
|
|
|
protected override void Init(IDiagramViewModel root)
|
|
{
|
|
base.Init(root);
|
|
|
|
InitConnector();
|
|
}
|
|
|
|
protected override void LoadDesignerItemViewModel(SelectableItemBase designerbase)
|
|
{
|
|
base.LoadDesignerItemViewModel(designerbase);
|
|
|
|
if (designerbase is DesignerItemBase designer)
|
|
{
|
|
this.Left = designer.Left;
|
|
this.Top = designer.Top;
|
|
this.Angle = designer.Angle;
|
|
this.ScaleX = designer.ScaleX;
|
|
this.ScaleY = designer.ScaleY;
|
|
this.ItemWidth = designer.ItemWidth;
|
|
this.ItemHeight = designer.ItemHeight;
|
|
this.Icon = designer.Icon;
|
|
}
|
|
}
|
|
|
|
protected virtual void InitConnector()
|
|
{
|
|
connectors.Add(new FullyCreatedConnectorInfo(this.Root, this, ConnectorOrientation.Top));
|
|
connectors.Add(new FullyCreatedConnectorInfo(this.Root, this, ConnectorOrientation.Bottom));
|
|
connectors.Add(new FullyCreatedConnectorInfo(this.Root, this, ConnectorOrientation.Left));
|
|
connectors.Add(new FullyCreatedConnectorInfo(this.Root, this, ConnectorOrientation.Right));
|
|
}
|
|
|
|
#region 属性
|
|
public FullyCreatedConnectorInfo TopConnector
|
|
{
|
|
get
|
|
{
|
|
return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.Top);
|
|
}
|
|
}
|
|
|
|
public FullyCreatedConnectorInfo BottomConnector
|
|
{
|
|
get
|
|
{
|
|
return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.Bottom);
|
|
}
|
|
}
|
|
|
|
public FullyCreatedConnectorInfo LeftConnector
|
|
{
|
|
get
|
|
{
|
|
return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.Left);
|
|
}
|
|
}
|
|
|
|
public FullyCreatedConnectorInfo RightConnector
|
|
{
|
|
get
|
|
{
|
|
return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.Right);
|
|
}
|
|
}
|
|
|
|
public FullyCreatedConnectorInfo TopLeftConnector
|
|
{
|
|
get
|
|
{
|
|
return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.TopLeft);
|
|
}
|
|
}
|
|
|
|
public FullyCreatedConnectorInfo TopRightConnector
|
|
{
|
|
get
|
|
{
|
|
return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.TopRight);
|
|
}
|
|
}
|
|
|
|
public FullyCreatedConnectorInfo BottomLeftConnector
|
|
{
|
|
get
|
|
{
|
|
return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.BottomLeft);
|
|
}
|
|
}
|
|
|
|
public FullyCreatedConnectorInfo BottomRightConnector
|
|
{
|
|
get
|
|
{
|
|
return connectors?.FirstOrDefault(p => p.Orientation == ConnectorOrientation.BottomRight);
|
|
}
|
|
}
|
|
|
|
private FullyCreatedConnectorInfo _portlessConnector;
|
|
public FullyCreatedConnectorInfo PortlessConnector
|
|
{
|
|
get
|
|
{
|
|
if (_portlessConnector == null)
|
|
_portlessConnector = new FullyCreatedConnectorInfo(this.Root, this, ConnectorOrientation.None) { IsPortless = true };
|
|
|
|
return _portlessConnector;
|
|
}
|
|
}
|
|
|
|
public Style ConnectorStyle
|
|
{
|
|
get; set;
|
|
}
|
|
|
|
public ShapeDefiner ShapeDefiner
|
|
{
|
|
get;
|
|
}
|
|
|
|
private string _icon;
|
|
[CanDo]
|
|
public string Icon
|
|
{
|
|
get
|
|
{
|
|
return _icon;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _icon, value);
|
|
}
|
|
}
|
|
|
|
private double _itemWidth = 65;
|
|
[Browsable(true)]
|
|
[CanDo]
|
|
public double ItemWidth
|
|
{
|
|
get
|
|
{
|
|
return _itemWidth;
|
|
}
|
|
set
|
|
{
|
|
if (value <= 0) return;
|
|
SetProperty(ref _itemWidth, value);
|
|
}
|
|
}
|
|
|
|
private double _itemHeight = 65;
|
|
[Browsable(true)]
|
|
[CanDo]
|
|
public double ItemHeight
|
|
{
|
|
get
|
|
{
|
|
return _itemHeight;
|
|
}
|
|
set
|
|
{
|
|
if (value <= 0) return;
|
|
SetProperty(ref _itemHeight, value);
|
|
}
|
|
}
|
|
|
|
[CanDo]
|
|
public SizeBase Size
|
|
{
|
|
get
|
|
{
|
|
return new SizeBase(ItemWidth, ItemHeight);
|
|
}
|
|
set
|
|
{
|
|
ItemWidth = value.Width;
|
|
ItemHeight = value.Height;
|
|
}
|
|
}
|
|
|
|
private bool _showConnectors = false;
|
|
[Browsable(false)]
|
|
public bool ShowConnectors
|
|
{
|
|
get
|
|
{
|
|
return _showConnectors;
|
|
}
|
|
set
|
|
{
|
|
if (SetProperty(ref _showConnectors, value))
|
|
{
|
|
foreach (var connector in connectors)
|
|
{
|
|
connector.ShowConnectors = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool _showResize = true;
|
|
[Browsable(false)]
|
|
public bool ShowResize
|
|
{
|
|
get
|
|
{
|
|
return _showResize;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _showResize, value);
|
|
}
|
|
}
|
|
|
|
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;
|
|
[Browsable(true)]
|
|
[CanDo]
|
|
public double Left
|
|
{
|
|
get
|
|
{
|
|
return _left;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _left, value);
|
|
}
|
|
}
|
|
|
|
private double _top;
|
|
[Browsable(true)]
|
|
[CanDo]
|
|
public double Top
|
|
{
|
|
get
|
|
{
|
|
return _top;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _top, value);
|
|
}
|
|
}
|
|
|
|
public PointBase Position
|
|
{
|
|
get
|
|
{
|
|
return new PointBase(Left, Top);
|
|
}
|
|
}
|
|
|
|
[CanDo]
|
|
public PointBase TopLeft
|
|
{
|
|
get
|
|
{
|
|
return new PointBase(Left, Top);
|
|
}
|
|
set
|
|
{
|
|
Left = value.X;
|
|
Top = value.Y;
|
|
}
|
|
}
|
|
|
|
private double _angle;
|
|
[CanDo]
|
|
public double Angle
|
|
{
|
|
get
|
|
{
|
|
return _angle;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _angle, value);
|
|
}
|
|
}
|
|
|
|
private double _scaleX = 1;
|
|
[CanDo]
|
|
public double ScaleX
|
|
{
|
|
get
|
|
{
|
|
return _scaleX;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _scaleX, value);
|
|
}
|
|
}
|
|
|
|
private double _scaleY = 1;
|
|
[CanDo]
|
|
public double ScaleY
|
|
{
|
|
get
|
|
{
|
|
return _scaleY;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _scaleY, value);
|
|
}
|
|
}
|
|
|
|
private double _margin;
|
|
|
|
public double Margin
|
|
{
|
|
get
|
|
{
|
|
return _margin;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _margin, value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 连接点是否可以按偏移自定义
|
|
/// </summary>
|
|
public bool IsInnerConnector
|
|
{
|
|
get; set;
|
|
}
|
|
|
|
protected ObservableCollection<FullyCreatedConnectorInfo> connectors = new ObservableCollection<FullyCreatedConnectorInfo>();
|
|
public IEnumerable<FullyCreatedConnectorInfo> Connectors
|
|
{
|
|
get
|
|
{
|
|
return connectors;
|
|
}
|
|
}
|
|
|
|
protected ObservableCollection<CinchMenuItem> menuOptions;
|
|
public IEnumerable<CinchMenuItem> MenuOptions
|
|
{
|
|
get
|
|
{
|
|
return menuOptions;
|
|
}
|
|
}
|
|
|
|
public bool ShowMenuOptions
|
|
{
|
|
get
|
|
{
|
|
if (MenuOptions == null || MenuOptions.Count() == 0)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public bool BeginDo
|
|
{
|
|
get; set;
|
|
}
|
|
|
|
public IObservable<NotifyCollectionChangedEventArgs> WhenConnectorsChanged
|
|
{
|
|
get
|
|
{
|
|
return Observable
|
|
.FromEventPattern<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>(
|
|
h => this.connectors.CollectionChanged += h,
|
|
h => this.connectors.CollectionChanged -= h)
|
|
.Select(x => x.EventArgs);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region 方法
|
|
public void AddConnector(FullyCreatedConnectorInfo connector)
|
|
{
|
|
if (!connectors.Contains(connector))
|
|
{
|
|
connectors.Add(connector);
|
|
}
|
|
}
|
|
|
|
public void RemoveConnector(FullyCreatedConnectorInfo connector)
|
|
{
|
|
if (connectors.Contains(connector))
|
|
{
|
|
connectors.Remove(connector);
|
|
}
|
|
}
|
|
|
|
public void ClearConnectors()
|
|
{
|
|
connectors.Clear();
|
|
}
|
|
|
|
public void SetCellAlignment()
|
|
{
|
|
if (!(this is TextDesignerItemViewModel))
|
|
{
|
|
if (Root.CellHorizontalAlignment == CellHorizontalAlignment.Center)
|
|
{
|
|
if (Root.GridCellSize.Width > this.ItemWidth)
|
|
{
|
|
this.Left = (int)(this.Left / Root.GridCellSize.Width) * Root.GridCellSize.Width + Root.GridMarginSize.Width + (Root.GridCellSize.Width - this.ItemWidth) / 2;
|
|
}
|
|
}
|
|
else if (Root.CellHorizontalAlignment == CellHorizontalAlignment.Left)
|
|
{
|
|
this.Left = (int)(this.Left / Root.GridCellSize.Width) * Root.GridCellSize.Width + Root.GridMarginSize.Width;
|
|
}
|
|
else if (Root.CellHorizontalAlignment == CellHorizontalAlignment.Right)
|
|
{
|
|
if (Root.GridCellSize.Width > this.ItemWidth)
|
|
{
|
|
this.Left = (int)(this.Left / Root.GridCellSize.Width) * Root.GridCellSize.Width + Root.GridMarginSize.Width + (Root.GridCellSize.Width - this.ItemWidth);
|
|
}
|
|
}
|
|
|
|
if (Root.CellVerticalAlignment == CellVerticalAlignment.Center)
|
|
{
|
|
if (Root.GridCellSize.Height > this.ItemHeight)
|
|
{
|
|
this.Top = (int)(this.Top / Root.GridCellSize.Height) * Root.GridCellSize.Height + Root.GridMarginSize.Height + (Root.GridCellSize.Height - this.ItemHeight) / 2;
|
|
}
|
|
}
|
|
else if (Root.CellVerticalAlignment == CellVerticalAlignment.Top)
|
|
{
|
|
this.Top = (int)(this.Top / Root.GridCellSize.Height) * Root.GridCellSize.Height + Root.GridMarginSize.Height;
|
|
}
|
|
else if (Root.CellVerticalAlignment == CellVerticalAlignment.Bottom)
|
|
{
|
|
if (Root.GridCellSize.Height > this.ItemHeight)
|
|
{
|
|
this.Top = (int)(this.Top / Root.GridCellSize.Height) * Root.GridCellSize.Height + Root.GridMarginSize.Height + (Root.GridCellSize.Height - this.ItemHeight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void RaiseTopLeft()
|
|
{
|
|
this.RaisePropertyChanged(nameof(TopLeft), new PointBase(GetOldValue<double>(nameof(Left)), GetOldValue<double>(nameof(Top))), TopLeft);
|
|
}
|
|
|
|
public void RaiseItemWidthHeight()
|
|
{
|
|
this.RaisePropertyChanged(nameof(Size), new SizeBase(GetOldValue<double>(nameof(ItemWidth)), GetOldValue<double>(nameof(ItemHeight))), Size);
|
|
}
|
|
|
|
public void RaiseAngle()
|
|
{
|
|
this.RaisePropertyChanged(nameof(Angle), GetOldValue<double>(nameof(Angle)), Angle);
|
|
}
|
|
|
|
public RectangleBase GetBounds(bool includePorts = false)
|
|
{
|
|
if (!includePorts)
|
|
return new RectangleBase(Position, Size);
|
|
|
|
var leftPort = LeftConnector;
|
|
var topPort = TopConnector;
|
|
var rightPort = RightConnector;
|
|
var bottomPort = BottomConnector;
|
|
|
|
var left = leftPort == null ? Position.X : Math.Min(Position.X, leftPort.Position.X);
|
|
var top = topPort == null ? Position.Y : Math.Min(Position.Y, topPort.Position.Y);
|
|
var right = rightPort == null ? Position.X + ItemWidth :
|
|
Math.Max(rightPort.Position.X + rightPort.ConnectorWidth, Position.X + ItemWidth);
|
|
var bottom = bottomPort == null ? Position.Y + ItemHeight :
|
|
Math.Max(bottomPort.Position.Y + bottomPort.ConnectorHeight, Position.Y + ItemHeight);
|
|
|
|
return new RectangleBase(left, top, right, bottom, true);
|
|
}
|
|
|
|
public IShape GetShape() => ShapeDefiner(this);
|
|
|
|
#endregion
|
|
}
|
|
}
|