Files
aistudio-wpf-diagram/AIStudio.Wpf.Mind/ViewModels/MindNode.cs
2023-03-05 21:30:53 +08:00

602 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Xml.Linq;
using AIStudio.Wpf.DiagramDesigner;
using AIStudio.Wpf.DiagramDesigner.Algorithms;
using AIStudio.Wpf.DiagramDesigner.Geometrys;
using AIStudio.Wpf.DiagramDesigner.Helpers;
using AIStudio.Wpf.DiagramDesigner.Models;
using AIStudio.Wpf.DiagramModels;
using AIStudio.Wpf.DiagramModels.ViewModels;
using AIStudio.Wpf.Flowchart.Models;
using AIStudio.Wpf.Mind.Helpers;
using AIStudio.Wpf.Mind.Models;
namespace AIStudio.Wpf.Mind.ViewModels
{
public class MindNode : DiagramItemViewModel
{
public MindNode(NodeLevel nodeLevel, MindType mindType = MindType.Mind) : this(null, nodeLevel, mindType)
{
}
public MindNode(IDiagramViewModel root, NodeLevel nodeLevel, MindType mindType = MindType.Mind) : base(root)
{
NodeLevel = nodeLevel;
MindType = mindType;
InitLayout(true);
}
public MindNode(IDiagramViewModel root, SelectableItemBase designer) : base(root, designer)
{
InitLayout(false);
}
public MindNode(IDiagramViewModel root, SerializableItem serializableItem, string serializableType) : base(root, serializableItem, serializableType)
{
InitLayout(false);
}
public override SelectableItemBase GetSerializableObject()
{
return new MindNodeDesignerItem(this);
}
protected override void Init(IDiagramViewModel root)
{
base.Init(root);
EnabledForConnection = false;
AddChildCommand = (Root as IMindDiagramViewModel)?.AddChildCommand;
AddParentCommand = (Root as IMindDiagramViewModel)?.AddParentCommand;
AddPeerCommand = (Root as IMindDiagramViewModel)?.AddPeerCommand;
DeleteSelfCommand = (Root as IMindDiagramViewModel)?.DeleteSelfCommand;
MoveForwardCommand = (Root as IMindDiagramViewModel)?.MoveForwardCommand;
MoveBackCommand = (Root as IMindDiagramViewModel)?.MoveBackCommand;
BuildMenuOptions();
}
protected void InitLayout(bool initAppearance)
{
var layout = GlobalType.AllTypes.Where(p => typeof(IMindLayout).IsAssignableFrom(p)).FirstOrDefault(p => p.Name == MindType.ToString() + "Layout");
MindLayout = layout != null ? (System.Activator.CreateInstance(layout) as IMindLayout) : new MindLayout();
if (initAppearance)
{
MindLayout.Appearance(this);
}
this.PropertyChanged += this.Item_PropertyChanged;
}
protected override void LoadDesignerItemViewModel(SelectableItemBase designerbase)
{
base.LoadDesignerItemViewModel(designerbase);
if (designerbase is MindNodeDesignerItem designer)
{
NodeLevel = designer.NodeLevel;
MindType = designer.MindType;
Spacing = designer.Spacing;
Offset = designer.Offset;
IsExpanded = designer.IsExpanded;
}
}
public override DiagramNode ToDiagram()
{
var mindNodeModel = new MindNodeModel();
mindNodeModel.NodeLevel = NodeLevel;
mindNodeModel.MindType = MindType;
mindNodeModel.Spacing = Spacing;
mindNodeModel.Offset = Offset;
mindNodeModel.IsExpanded = IsExpanded;
return mindNodeModel;
}
private bool Level_Enable(object obj)
{
if (Command_Enable(obj) == false) return false;
return NodeLevel != NodeLevel.Level1;
}
#region
public IMindLayout MindLayout
{
get; set;
}
[Browsable(false)]
private NodeLevel _nodeLevel;
public NodeLevel NodeLevel
{
get
{
return _nodeLevel;
}
set
{
SetProperty(ref _nodeLevel, value);
}
}
//public MindNode ParentNode
//{
// get
// {
// return Parent as MindNode;
// }
//}
//public int NodeLevel
//{
// get
// {
// if (ParentNode == null)
// {
// return 0;
// }
// else
// {
// return ParentNode.NodeLevel + 1;
// }
// }
//}
private MindType _mindType;
public MindType MindType
{
get
{
return _mindType;
}
set
{
SetProperty(ref _mindType, value);
}
}
private bool _isExpanded = true;
public bool IsExpanded
{
get
{
return _isExpanded;
}
set
{
SetProperty(ref _isExpanded, value);
}
}
public SizeBase Spacing
{
get; set;
} = new SizeBase(15, 15);
private ObservableCollection<MindNode> _children = new ObservableCollection<MindNode>();
public ObservableCollection<MindNode> Children
{
get
{
return _children;
}
set
{
SetProperty(ref _children, value);
}
}
public SizeBase SizeWithSpacing
{
get
{
return this.Size.Add(Spacing.Width * 2, Spacing.Height * 2);
}
}
public SizeBase DesiredSize
{
get; set;
}
public PointBase DesiredPosition
{
get; set;
}
public PointBase Offset
{
get; set;
}
private ConnectorOrientation _connectorOrientation = ConnectorOrientation.Left;
public ConnectorOrientation ConnectorOrientation
{
get
{
return _connectorOrientation;
}
set
{
SetProperty(ref _connectorOrientation, value);
}
}
public bool LayoutUpdating
{
get; set;
}
#endregion
#region
private LinkInfo _linkInfo;
public LinkInfo LinkInfo
{
get
{
return _linkInfo;
}
set
{
SetProperty(ref _linkInfo, value);
}
}
private ImageInfo _imageInfo;
public ImageInfo ImageInfo
{
get
{
return _imageInfo;
}
set
{
SetProperty(ref _imageInfo, value);
}
}
private string _remark;
public string Remark
{
get
{
return _remark;
}
set
{
SetProperty(ref _remark, value);
}
}
private string _grade;
public string Grade
{
get
{
return _grade;
}
set
{
SetProperty(ref _grade, value);
}
}
private double _completionRate;
public double CompletionRate
{
get
{
return _completionRate;
}
set
{
SetProperty(ref _completionRate, value);
}
}
private ObservableCollection<string> _tags;
public ObservableCollection<string> Tags
{
get
{
return _tags;
}
set
{
SetProperty(ref _tags, value);
}
}
#endregion
#region
public SimpleCommand AddParentCommand
{
get; private set;
}
public SimpleCommand AddChildCommand
{
get; private set;
}
public SimpleCommand AddPeerCommand
{
get; private set;
}
public SimpleCommand DeleteSelfCommand
{
get; private set;
}
public SimpleCommand MoveForwardCommand
{
get; private set;
}
public SimpleCommand MoveBackCommand
{
get; private set;
}
#endregion
#region
private void BuildMenuOptions()
{
menuOptions = new ObservableCollection<CinchMenuItem>();
CinchMenuItem menuItem = new CinchMenuItem();
menuItem.Text = "下级";
menuItem.Command = AddChildCommand;
menuItem.CommandParameter = this;
menuOptions.Add(menuItem);
menuItem = new CinchMenuItem();
menuItem.Text = "同级";
menuItem.Command = AddPeerCommand;
menuItem.CommandParameter = this;
menuOptions.Add(menuItem);
menuItem = new CinchMenuItem();
menuItem.Text = "上级";
menuItem.Command = AddParentCommand;
menuItem.CommandParameter = this;
menuOptions.Add(menuItem);
menuItem = new CinchMenuItem();
menuItem.Text = "前移";
menuItem.Command = MoveForwardCommand;
menuItem.CommandParameter = this;
menuOptions.Add(menuItem);
menuItem = new CinchMenuItem();
menuItem.Text = "后移";
menuItem.Command = MoveBackCommand;
menuItem.CommandParameter = this;
menuOptions.Add(menuItem);
menuItem = new CinchMenuItem();
menuItem.Text = "删除";
menuItem.Command = DeleteSelfCommand;
menuItem.CommandParameter = this;
menuOptions.Add(menuItem);
}
#endregion
#region
public void AddChild(MindNode item, int index = -1)
{
if (this.NodeLevel == NodeLevel.Level1)
{
item.NodeLevel = NodeLevel.Level2;
}
else
{
item.NodeLevel = NodeLevel.Level3;
}
if (index >= 0)
{
this.Children.Insert(index, item);
}
else
{
this.Children.Add(item);
}
item.Parent = this;
ConnectionViewModel connector = MindLayout?.GetConnectionViewModel(this, item);
Root?.DirectAddItemCommand.Execute(new SelectableDesignerItemViewModelBase[] { item, connector });
connector.ZIndex = -1;
this.IsSelected = true;
}
public void RemoveChild(MindNode item, bool removeall = false)
{
item.PropertyChanged -= Item_PropertyChanged;
this.Children.Remove(item);
var connectors = Root?.Items.OfType<ConnectionViewModel>().Where(p => p.SinkConnectorInfoFully?.DataItem == item).ToList();
Root?.DirectRemoveItemCommand.Execute(item);
Root?.DirectRemoveItemCommand.Execute(connectors);
if (removeall)
{
if (item.Children?.Count > 0)
{
foreach (var child in item.Children.ToList())
{
item.RemoveChild(child);
}
}
}
}
#endregion
private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (GetLevel1Node()?.LayoutUpdating == true) return;
switch (e.PropertyName)
{
case nameof(IsExpanded):
case nameof(ItemWidth):
case nameof(ItemHeight):
{
LayoutUpdated();
break;
}
case nameof(NodeLevel):
MindLayout?.Appearance(this);
break;
case nameof(MindType):
InitLayout(true);
break;
case nameof(Left):
{
if (e is ValuePropertyChangedEventArgs valuePropertyChangedEventArgs)
{
if (NodeLevel == NodeLevel.Level1)
{
LayoutUpdated();
}
else
{
UpdateOffsetX((double)valuePropertyChangedEventArgs.OldValue, (double)valuePropertyChangedEventArgs.NewValue);
LayoutUpdated();
}
}
break;
}
case nameof(Top):
{
if (e is ValuePropertyChangedEventArgs valuePropertyChangedEventArgs)
{
if (NodeLevel == NodeLevel.Level1)
{
LayoutUpdated();
}
else
{
UpdateOffsetY((double)valuePropertyChangedEventArgs.OldValue, (double)valuePropertyChangedEventArgs.NewValue);
LayoutUpdated();
}
}
break;
}
case nameof(Text):
{
ItemWidth = Math.Max(ItemWidth, GetTextDisplayWidthHelper.GetTextDisplayWidth(Text, new FontFamily(FontViewModel.FontFamily), FontViewModel.FontStyle, FontViewModel.FontWeight, FontViewModel.FontStretch, FontViewModel.FontSize) + 30);
break;
}
}
}
#region
public MindNode GetLevel1Node()
{
var node = this;
while (node.Parent is MindNode mindNode)
{
node = mindNode;
}
return node;
}
public MindNode GetLevel2Node()
{
var node = this;
while (node.Parent is MindNode mindNode && mindNode.NodeLevel == NodeLevel.Level2)
{
node = mindNode;
}
return node;
}
protected void UpdateOffsetX(double oldvalue, double newvalue)
{
Offset += new VectorBase(newvalue - oldvalue, 0);
}
protected void UpdateOffsetY(double oldvalue, double newvalue)
{
Offset += new VectorBase(0, newvalue - oldvalue);
}
public void LayoutUpdated()
{
MindLayout?.LayoutUpdated(GetLevel1Node());
}
#endregion
}
public class LinkInfo : BindableBase
{
private string _url;
public string Url
{
get
{
return _url;
}
set
{
SetProperty(ref _url, value);
}
}
private string _text;
public string Text
{
get
{
return _text;
}
set
{
SetProperty(ref _text, value);
}
}
}
public class ImageInfo : BindableBase
{
private string _url;
public string Url
{
get
{
return _url;
}
set
{
SetProperty(ref _url, value);
}
}
private string _text;
public string Text
{
get
{
return _text;
}
set
{
SetProperty(ref _text, value);
}
}
}
}