mirror of
https://gitee.com/akwkevin/aistudio.-wpf.-diagram
synced 2026-03-03 00:00:57 +08:00
722 lines
20 KiB
C#
722 lines
20 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 InitNew()
|
|
{
|
|
AddConnector(new FullyCreatedConnectorInfo(this.Root, this, ConnectorOrientation.Top));
|
|
AddConnector(new FullyCreatedConnectorInfo(this.Root, this, ConnectorOrientation.Bottom));
|
|
AddConnector(new FullyCreatedConnectorInfo(this.Root, this, ConnectorOrientation.Left));
|
|
AddConnector(new FullyCreatedConnectorInfo(this.Root, this, ConnectorOrientation.Right));
|
|
}
|
|
|
|
protected override void LoadDesignerItemViewModel(SelectableItemBase designerbase)
|
|
{
|
|
base.LoadDesignerItemViewModel(designerbase);
|
|
|
|
if (designerbase is DesignerItemBase designer)
|
|
{
|
|
this.PhysicalLeft = designer.PhysicalLeft;
|
|
this.PhysicalTop = designer.PhysicalTop;
|
|
this.Angle = designer.Angle;
|
|
this.ScaleX = designer.ScaleX;
|
|
this.ScaleY = designer.ScaleY;
|
|
this.PhysicalItemWidth = designer.PhysicalItemWidth;
|
|
this.PhysicalItemHeight = designer.PhysicalItemHeight;
|
|
this.Icon = designer.Icon;
|
|
this.CornerRadius = designer.CornerRadius;
|
|
this.BorderThickness = designer.BorderThickness;
|
|
if (designer.Connectors != null)
|
|
{
|
|
foreach (var connector in designer.Connectors)
|
|
{
|
|
FullyCreatedConnectorInfo fullyCreatedConnectorInfo = new FullyCreatedConnectorInfo(this.Root, this, connector);
|
|
AddConnector(fullyCreatedConnectorInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#region 属性
|
|
|
|
public FullyCreatedConnectorInfo FirstConnector
|
|
{
|
|
get
|
|
{
|
|
return connectors?.FirstOrDefault();
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
public virtual PointBase MiddlePosition
|
|
{
|
|
get
|
|
{
|
|
return GetBounds().Center;
|
|
}
|
|
}
|
|
|
|
private string _icon;
|
|
[CanDo]
|
|
public string Icon
|
|
{
|
|
get
|
|
{
|
|
return _icon;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _icon, value);
|
|
}
|
|
}
|
|
|
|
private double _itemWidth = 65;
|
|
[CanDo]
|
|
[Browsable(true)]
|
|
public double ItemWidth
|
|
{
|
|
get
|
|
{
|
|
return _itemWidth;
|
|
}
|
|
set
|
|
{
|
|
if (value <= 0) return;
|
|
if (SetProperty(ref _itemWidth, value))
|
|
{
|
|
RaisePropertyChanged(nameof(PhysicalItemWidth));
|
|
}
|
|
}
|
|
}
|
|
|
|
private double _itemHeight = 65;
|
|
[CanDo]
|
|
[Browsable(true)]
|
|
public double ItemHeight
|
|
{
|
|
get
|
|
{
|
|
return _itemHeight;
|
|
}
|
|
set
|
|
{
|
|
if (value <= 0) return;
|
|
if (SetProperty(ref _itemHeight, value))
|
|
{
|
|
RaisePropertyChanged(nameof(PhysicalItemHeight));
|
|
}
|
|
}
|
|
}
|
|
|
|
[DisplayName("ItemWidth(mm)")]
|
|
[Browsable(true)]
|
|
public double PhysicalItemWidth
|
|
{
|
|
get
|
|
{
|
|
return ScreenHelper.WidthToMm(ItemWidth);
|
|
}
|
|
set
|
|
{
|
|
ItemWidth = ScreenHelper.MmToWidth(value);
|
|
}
|
|
}
|
|
|
|
[DisplayName("ItemHeight(mm)")]
|
|
[Browsable(true)]
|
|
public double PhysicalItemHeight
|
|
{
|
|
get
|
|
{
|
|
return ScreenHelper.WidthToMm(ItemHeight);
|
|
}
|
|
set
|
|
{
|
|
ItemHeight = ScreenHelper.MmToWidth(value);
|
|
}
|
|
}
|
|
|
|
[CanDo]
|
|
public SizeBase Size
|
|
{
|
|
get
|
|
{
|
|
return new SizeBase(ItemWidth, ItemHeight);
|
|
}
|
|
set
|
|
{
|
|
ItemWidth = value.Width;
|
|
ItemHeight = value.Height;
|
|
}
|
|
}
|
|
|
|
private double _connectorMargin = -4;
|
|
public double ConnectorMargin
|
|
{
|
|
get
|
|
{
|
|
return _connectorMargin;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _connectorMargin, value);
|
|
}
|
|
}
|
|
|
|
private bool _showConnectors = 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;
|
|
public bool ShowResize
|
|
{
|
|
get
|
|
{
|
|
return _showResize;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _showResize, value);
|
|
}
|
|
}
|
|
|
|
private bool _showRotate = false;
|
|
public bool ShowRotate
|
|
{
|
|
get
|
|
{
|
|
return _showRotate;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _showRotate, value);
|
|
}
|
|
}
|
|
|
|
public bool ShowArrow { get; set; } = true;
|
|
|
|
public bool ShowResized
|
|
{
|
|
get; set;
|
|
}
|
|
|
|
private bool alwayForResized;
|
|
public bool AlwayForResized
|
|
{
|
|
get
|
|
{
|
|
return alwayForResized;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref alwayForResized, value);
|
|
}
|
|
}
|
|
|
|
private bool enabledForConnection = true;
|
|
public bool EnabledForConnection
|
|
{
|
|
get
|
|
{
|
|
return enabledForConnection;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref enabledForConnection, value);
|
|
}
|
|
}
|
|
|
|
private double _left;
|
|
[CanDo]
|
|
[Browsable(true)]
|
|
public double Left
|
|
{
|
|
get
|
|
{
|
|
return _left;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _left, value);
|
|
}
|
|
}
|
|
|
|
private double _top;
|
|
[CanDo]
|
|
[Browsable(true)]
|
|
public double Top
|
|
{
|
|
get
|
|
{
|
|
return _top;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _top, value);
|
|
}
|
|
}
|
|
|
|
[DisplayName("Left(mm)")]
|
|
[Browsable(true)]
|
|
public double PhysicalLeft
|
|
{
|
|
get
|
|
{
|
|
return ScreenHelper.WidthToMm(Left);
|
|
}
|
|
set
|
|
{
|
|
Left = ScreenHelper.MmToWidth(value);
|
|
}
|
|
}
|
|
|
|
[DisplayName("Top(mm)")]
|
|
[Browsable(true)]
|
|
public double PhysicalTop
|
|
{
|
|
get
|
|
{
|
|
return ScreenHelper.WidthToMm(Top);
|
|
}
|
|
set
|
|
{
|
|
Top = ScreenHelper.MmToWidth(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);
|
|
}
|
|
}
|
|
|
|
private CornerRadius _cornerRadius = new CornerRadius(3);
|
|
public CornerRadius CornerRadius
|
|
{
|
|
get
|
|
{
|
|
return _cornerRadius;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _cornerRadius, value);
|
|
}
|
|
}
|
|
|
|
private Thickness _borderThickness = new Thickness(1);
|
|
public Thickness BorderThickness
|
|
{
|
|
get
|
|
{
|
|
return _borderThickness;
|
|
}
|
|
set
|
|
{
|
|
SetProperty(ref _borderThickness, 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 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);
|
|
ConnectorMargin = 0 - connector.ConnectorWidth / 2;
|
|
}
|
|
}
|
|
|
|
public void RemoveConnector(FullyCreatedConnectorInfo connector)
|
|
{
|
|
if (connectors.Contains(connector))
|
|
{
|
|
connectors.Remove(connector);
|
|
}
|
|
}
|
|
|
|
public virtual 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 FullyCreatedConnectorInfo GetFullConnectorInfo(Guid connectorId, ConnectorOrientation connectorOrientation, double xRatio, double yRatio, bool isInnerPoint, bool isPortless)
|
|
{
|
|
if (isInnerPoint)
|
|
{
|
|
return this.Connectors.Where(p => p.XRatio == xRatio && p.YRatio == yRatio).FirstOrDefault();
|
|
}
|
|
else if (isPortless)
|
|
{
|
|
return this.PortlessConnector;
|
|
}
|
|
else
|
|
{
|
|
switch (connectorOrientation)
|
|
{
|
|
case ConnectorOrientation.Left:
|
|
return this.LeftConnector;
|
|
case ConnectorOrientation.TopLeft:
|
|
return this.TopLeftConnector;
|
|
case ConnectorOrientation.Top:
|
|
return this.TopConnector;
|
|
case ConnectorOrientation.TopRight:
|
|
return this.TopRightConnector;
|
|
case ConnectorOrientation.Right:
|
|
return this.RightConnector;
|
|
case ConnectorOrientation.BottomRight:
|
|
return this.BottomRightConnector;
|
|
case ConnectorOrientation.Bottom:
|
|
return this.BottomConnector;
|
|
case ConnectorOrientation.BottomLeft:
|
|
return this.BottomLeftConnector;
|
|
|
|
default:
|
|
throw new InvalidOperationException(
|
|
string.Format("Found invalid persisted Connector Orientation for Connector Id: {0}", connectorId));
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|