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.Media; using AIStudio.Wpf.DiagramDesigner; using AIStudio.Wpf.DiagramDesigner.Algorithms; using AIStudio.Wpf.DiagramDesigner.Geometrys; using AIStudio.Wpf.DiagramDesigner.Models; namespace AIStudio.Wpf.Mind.ViewModels { public class MindNode : DesignerItemViewModelBase { public MindNode(NodeLevel nodeLevel) : this(null, nodeLevel) { } public MindNode(IDiagramViewModel root, NodeLevel nodeLevel) : base(root) { NodeLevel = nodeLevel; LevelInit(true); } public MindNode(IDiagramViewModel root, SelectableItemBase designer) : base(root, designer) { } public MindNode(IDiagramViewModel root, SerializableItem serializableItem, string serializableType) : base(root, serializableItem, serializableType) { } protected override void Init(IDiagramViewModel root) { base.Init(root); EnabledForConnection = false; AddChildCommand = new SimpleCommand(Command_Enable, ExecuteAddChildCommand); AddParentCommand = new SimpleCommand(Level_Enable, ExecuteAddParentCommand); AddPeerCommand = new SimpleCommand(Level_Enable, ExecuteAddPeerCommand); DeleteCommand = new SimpleCommand(Command_Enable, ExecuteDeleteCommand); MoveForwardCommand = new SimpleCommand(Command_Enable, ExecuteMoveForwardCommand); MoveBackCommand = new SimpleCommand(Command_Enable, ExecuteMoveBackCommand); BuildMenuOptions(); this.PropertyChanged += this.Item_PropertyChanged; } private void LevelInit(bool init =false) { switch (NodeLevel) { case NodeLevel.Level1: { ItemWidth = 110; ItemHeight = 40; this.ClearConnectors(); var port = new FullyCreatedConnectorInfo(Root, this, ConnectorOrientation.None, true) { XRatio = 0.5, YRatio = 0.5 }; this.AddConnector(port); IsInnerConnector = true; ColorViewModel.FillColor.Color = Color.FromRgb(0x73, 0xa1, 0xbf); ColorViewModel.LineColor.Color = Color.FromRgb(0x73, 0xa1, 0xbf); FontViewModel.FontColor = Colors.White; FontViewModel.FontSize = 15; Spacing = new SizeBase(50, 15); ShapeViewModel.SinkMarker.PathStyle = ArrowPathStyle.Circle; ShapeViewModel.SinkMarker.SizeStyle = ArrowSizeStyle.VerySmall; break; } case NodeLevel.Level2: { ItemWidth = 80; ItemHeight = 25; this.ClearConnectors(); var port1 = new FullyCreatedConnectorInfo(Root, this, ConnectorOrientation.Left, true) { XRatio = 0, YRatio = 0.5 }; this.AddConnector(port1); var port2 = new FullyCreatedConnectorInfo(Root, this, ConnectorOrientation.Right, true) { XRatio = 1, YRatio = 0.5 }; this.AddConnector(port2); IsInnerConnector = true; ColorViewModel.LineColor.Color = Color.FromRgb(0x73, 0xa1, 0xbf); ShapeViewModel.SinkMarker.PathStyle = ArrowPathStyle.None; ShapeViewModel.SinkMarker.SizeStyle = ArrowSizeStyle.VerySmall; break; } case NodeLevel.Level3: { ItemWidth = 80; ItemHeight = 25; this.ClearConnectors(); var port1 = new FullyCreatedConnectorInfo(Root, this, ConnectorOrientation.Left, true) { XRatio = 0, YRatio = 1 }; this.AddConnector(port1); var port2 = new FullyCreatedConnectorInfo(Root, this, ConnectorOrientation.Right, true) { XRatio = 1, YRatio = 1 }; this.AddConnector(port2); IsInnerConnector = true; ColorViewModel.LineColor.Color = Color.FromRgb(0x73, 0xa1, 0xbf); ShapeViewModel.SinkMarker.PathStyle = ArrowPathStyle.None; ShapeViewModel.SinkMarker.SizeStyle = ArrowSizeStyle.VerySmall; break; } } } private bool Level_Enable(object obj) { if (Command_Enable(obj) == false) return false; return NodeLevel != NodeLevel.Level1; } [Browsable(false)] public NodeLevel NodeLevel { get; set; } private double _cornerRadius = 3; public double CornerRadius { get { return _cornerRadius; } set { SetProperty(ref _cornerRadius, value); } } private bool _isExpanded = true; public bool IsExpanded { get { return _isExpanded; } set { SetProperty(ref _isExpanded, value); } } public bool LayoutUpdating { get; set; } public SizeBase Spacing { get; set; } = new SizeBase(15, 15); public List Children { get; set; } = new List(); 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; } public SimpleCommand AddParentCommand { get; private set; } public SimpleCommand AddChildCommand { get; private set; } public SimpleCommand AddPeerCommand { get; private set; } public SimpleCommand DeleteCommand { get; private set; } public SimpleCommand MoveForwardCommand { get; private set; } public SimpleCommand MoveBackCommand { get; private set; } private void BuildMenuOptions() { menuOptions = new ObservableCollection(); CinchMenuItem menuItem = new CinchMenuItem(); menuItem.Text = "下级"; menuItem.Command = AddChildCommand; menuOptions.Add(menuItem); menuItem = new CinchMenuItem(); menuItem.Text = "同级"; menuItem.Command = AddPeerCommand; menuOptions.Add(menuItem); //menuItem = new CinchMenuItem(); //menuItem.Text = "上级"; //menuItem.Command = AddParentCommand; //menuOptions.Add(menuItem); menuItem = new CinchMenuItem(); menuItem.Text = "前移"; menuItem.Command = MoveForwardCommand; menuOptions.Add(menuItem); menuItem = new CinchMenuItem(); menuItem.Text = "后移"; menuItem.Command = MoveBackCommand; menuOptions.Add(menuItem); menuItem = new CinchMenuItem(); menuItem.Text = "删除"; menuItem.Command = DeleteCommand; menuOptions.Add(menuItem); } public void ExecuteAddChildCommand(object obj) { if (obj is MindNode node) { } else { if (NodeLevel == NodeLevel.Level1) node = new MindNode(Root, NodeLevel.Level2) { Text = "分支主题" }; else node = new MindNode(Root, NodeLevel.Level3) { Text = "分支主题" }; } AddChild(node); LayoutUpdated(); } public void ExecuteAddParentCommand(object obj) { if (Parent is MindNode parent) { if (obj is MindNode node) { } else { if (NodeLevel == NodeLevel.Level1) { return; } else if (NodeLevel == NodeLevel.Level2) node = new MindNode(Root, NodeLevel.Level2) { Text = "分支主题" }; else node = new MindNode(Root, NodeLevel.Level3) { Text = "分支主题" }; } parent.RemoveChild(this); int index = parent.Children.IndexOf(this); parent.AddChild(node, index + 1); node.AddChild(this); LayoutUpdated(); } } public void ExecuteAddPeerCommand(object obj) { if (Parent is MindNode parent) { if (obj is MindNode node) { } else { if (NodeLevel == NodeLevel.Level1) { return; } else if (NodeLevel == NodeLevel.Level2) node = new MindNode(Root, NodeLevel.Level2) { Text = "分支主题" }; else node = new MindNode(Root, NodeLevel.Level3) { Text = "分支主题" }; } int index = parent.Children.IndexOf(this); parent.AddChild(node, index + 1); LayoutUpdated(); } } private void ExecuteMoveBackCommand(object obj) { if (Parent is MindNode parent) { int index = parent.Children.IndexOf(this); if (index < parent.Children.Count - 1) { parent.RemoveChild(this); parent.AddChild(this, index + 1); LayoutUpdated(); } } } private void ExecuteMoveForwardCommand(object obj) { if (Parent is MindNode parent) { int index = parent.Children.IndexOf(this); if (index > 0) { parent.RemoveChild(this); parent.AddChild(this, index - 1); LayoutUpdated(); } } } private void ExecuteDeleteCommand(object obj) { if (Parent is MindNode parent) { parent.RemoveChild(this); LayoutUpdated(); } } public void AddChild(MindNode item, int index = -1) { if (index >= 0) { this.Children.Insert(index, item); } else { this.Children.Add(item); } item.Parent = this; //item.ParentId = this.Id; Root?.DirectAddItemCommand.Execute(item); ConnectionViewModel connector = new ConnectionViewModel(Root, this.Connectors.FirstOrDefault(), item.Connectors.FirstOrDefault()); connector.ColorViewModel.LineColor = this.ColorViewModel.LineColor; connector.SmoothMargin = 20; connector.SmoothAutoSlope = 0.2; connector.ShapeViewModel.SinkMarker.PathStyle = this.ShapeViewModel.SinkMarker.PathStyle; connector.ShapeViewModel.SinkMarker.SizeStyle = this.ShapeViewModel.SinkMarker.SizeStyle; Root?.DirectAddItemCommand.Execute(connector); Root?.BringForwardCommand.Execute(new DesignerItemViewModelBase[] { item }); Root?.ClearSelectedItemsCommand.Execute(new SelectableDesignerItemViewModelBase[] { connector }); } public void RemoveChild(MindNode item) { item.PropertyChanged -= Item_PropertyChanged; this.Children.Remove(item); var connectors = Root?.Items.OfType().Where(p => p.SinkConnectorInfoFully?.DataItem == item).ToList(); Root?.DirectRemoveItemCommand.Execute(item); Root?.DirectRemoveItemCommand.Execute(connectors); if (item.Children?.Count > 0) { foreach (var child in item.Children.ToList()) { item.RemoveChild(child); } } } public MindNode GetLevel1Node() { var node = this; while (node.Parent is MindNode mindNode) { node = mindNode; } return node; } private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { case nameof(IsExpanded): GetLevel1Node()?.LayoutUpdated(); break; case nameof(NodeLevel): LevelInit(); 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; } } } public void UpdateOffsetX(double oldvalue, double newvalue) { if (GetLevel1Node()?.LayoutUpdating == true) return; Offset += new VectorBase(newvalue - oldvalue, 0); } public void UpdateOffsetY(double oldvalue, double newvalue) { if (GetLevel1Node()?.LayoutUpdating == true) return; Offset += new VectorBase(0, newvalue - oldvalue); } public void LayoutUpdated() { GetLevel1Node()?.Level1LayoutUpdated(); } protected void Level1LayoutUpdated() { LayoutUpdating = true; var size = MeasureOverride(); ArrangeOverride(); Root.BringToFrontCommand.Execute(new SelectableDesignerItemViewModelBase[] { this }); Root?.ReconnectLinksToClosestPorts(); LayoutUpdating = false; } protected SizeBase MeasureOverride(bool isExpanded = true) { var sizewithSpacing = SizeWithSpacing; if (Children?.Count > 0) { var childrensizes = Children.Select(p => p.MeasureOverride(IsExpanded && isExpanded)).ToArray(); sizewithSpacing = new SizeBase(sizewithSpacing.Width + childrensizes.Max(p => p.Width), Math.Max(sizewithSpacing.Height, childrensizes.Sum(p => p.Height))); } DesiredSize = isExpanded ? sizewithSpacing : new SizeBase(0, 0); Visible = isExpanded; var connectors = Root?.Items.OfType().Where(p => p.SinkConnectorInfoFully?.DataItem == this).ToList(); connectors?.ForEach(p => p.Visible = Visible); return DesiredSize; } protected void ArrangeOverride() { double left = MiddlePosition.X + ItemWidth / 2 + Spacing.Width; double top = MiddlePosition.Y - DesiredSize.Height / 2; if (Children?.Count > 0) { foreach (var child in Children) { child.Left = left + child.Spacing.Width + child.Offset.X; child.Top = top + child.DesiredSize.Height / 2 - child.ItemHeight / 2 + child.Offset.Y; child.DesiredPosition = child.Position; top += child.DesiredSize.Height; child.ArrangeOverride(); } } } } }