From ca1ac13a1fb731616530b3ae715bf3de29fe291d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=89=BE=E7=AB=B9?= Date: Sun, 19 Feb 2023 21:38:28 +0800 Subject: [PATCH] =?UTF-8?q?mind=20=E6=9C=89=E7=82=B9=E6=A0=B7=E5=AD=90?= =?UTF-8?q?=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...udio.Wpf.DiagramApp_gpbabrim_wpftmp.csproj | 1058 +++++++++++++++++ .../ViewModels/MindViewModel.cs | 36 +- .../Converters/IntToBoolConverter.cs | 15 +- .../Converters/IntToVisibilityConverter.cs | 24 + .../Geometrys/SizeBase.cs | 2 + .../PathGenerators/PathGenerators.Smooth.cs | 32 +- .../UserControls/DiagramControl.xaml | 36 +- .../AdditionViewModel/ShapeViewModel.cs | 1 + .../BaseViewModel/ConnectionViewModel.cs | 10 +- .../DesignerItemViewModelBase.cs | 26 + .../BaseViewModel/DiagramViewModel.cs | 258 +++- .../BaseViewModel/SelectableViewModelBase.cs | 13 + AIStudio.Wpf.Mind/Themes/MindNode.xaml | 151 ++- AIStudio.Wpf.Mind/ViewModels/MindNode.cs | 577 ++++++--- 14 files changed, 1993 insertions(+), 246 deletions(-) create mode 100644 AIStudio.Wpf.DiagramApp/AIStudio.Wpf.DiagramApp_gpbabrim_wpftmp.csproj create mode 100644 AIStudio.Wpf.DiagramDesigner/Converters/IntToVisibilityConverter.cs diff --git a/AIStudio.Wpf.DiagramApp/AIStudio.Wpf.DiagramApp_gpbabrim_wpftmp.csproj b/AIStudio.Wpf.DiagramApp/AIStudio.Wpf.DiagramApp_gpbabrim_wpftmp.csproj new file mode 100644 index 0000000..b68d647 --- /dev/null +++ b/AIStudio.Wpf.DiagramApp/AIStudio.Wpf.DiagramApp_gpbabrim_wpftmp.csproj @@ -0,0 +1,1058 @@ + + + AIStudio.Wpf.DiagramApp + obj\Debug\ + obj\ + F:\aistudio.-wpf.-diagram\AIStudio.Wpf.DiagramApp\obj\ + <_TargetAssemblyProjectName>AIStudio.Wpf.DiagramApp + + + + WinExe + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + 8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AIStudio.Wpf.DiagramApp/ViewModels/MindViewModel.cs b/AIStudio.Wpf.DiagramApp/ViewModels/MindViewModel.cs index 1279cbb..054a19e 100644 --- a/AIStudio.Wpf.DiagramApp/ViewModels/MindViewModel.cs +++ b/AIStudio.Wpf.DiagramApp/ViewModels/MindViewModel.cs @@ -29,7 +29,8 @@ namespace AIStudio.Wpf.Flowchart { base.InitDiagramViewModel(); - DiagramViewModel.GridCellSize = new Size(100, 100); + DiagramViewModel.GridCellSize = new Size(100, 100); + DiagramViewModel.ShowGrid= false; _service.DrawModeViewModel.LineDrawMode = DrawMode.ConnectingLineSmooth; DiagramViewModel.AllowDrop = false; } @@ -38,35 +39,32 @@ namespace AIStudio.Wpf.Flowchart { base.Init(); - MindLevel1Node level1node = new MindLevel1Node() { Text = "思维导图" }; + MindNode level1node = new MindNode(DiagramViewModel, Mind.NodeLevel.Level1) { Text = "思维导图" }; DiagramViewModel.DirectAddItemCommand.Execute(level1node); DiagramViewModel.CenterMoveCommand.Execute(level1node); - MindLevel2Node level2node1_1 = new MindLevel2Node() { Text = "分支主题1" }; - DiagramViewModel.DirectAddItemCommand.Execute(level2node1_1); - level1node.Children.Add(level2node1_1); + MindNode level2node1_1 = new MindNode(DiagramViewModel, Mind.NodeLevel.Level2) { Text = "分支主题1" }; + level1node.AddChild(level2node1_1); - MindLevel3Node level3node1_1_1 = new MindLevel3Node() { Text = "分支主题1_1" }; - DiagramViewModel.DirectAddItemCommand.Execute(level3node1_1_1); - level2node1_1.Children.Add(level3node1_1_1); + MindNode level3node1_1_1 = new MindNode(DiagramViewModel, Mind.NodeLevel.Level3) { Text = "分支主题1_1" }; + level2node1_1.AddChild(level3node1_1_1); - MindLevel3Node level3node1_1_2 = new MindLevel3Node() { Text = "分支主题1_2" }; - DiagramViewModel.DirectAddItemCommand.Execute(level3node1_1_2); - level2node1_1.Children.Add(level3node1_1_2); + MindNode level3node1_1_2 = new MindNode(DiagramViewModel, Mind.NodeLevel.Level3) { Text = "分支主题1_2" }; + level2node1_1.AddChild(level3node1_1_2); - MindLevel3Node level3node1_1_3 = new MindLevel3Node() { Text = "分支主题1_3" }; - DiagramViewModel.DirectAddItemCommand.Execute(level3node1_1_3); - level2node1_1.Children.Add(level3node1_1_3); + MindNode level3node1_1_3 = new MindNode(DiagramViewModel, Mind.NodeLevel.Level3) { Text = "分支主题1_3" }; + level2node1_1.AddChild(level3node1_1_3); - MindLevel2Node level2node1_2 = new MindLevel2Node() { Text = "分支主题2" }; - DiagramViewModel.DirectAddItemCommand.Execute(level2node1_2); - level1node.Children.Add(level2node1_2); + MindNode level2node1_2 = new MindNode(DiagramViewModel, Mind.NodeLevel.Level2) { Text = "分支主题2" }; + level1node.AddChild(level2node1_2); + MindNode level2node1_3 = new MindNode(DiagramViewModel, Mind.NodeLevel.Level2) { Text = "分支主题3" }; + level1node.AddChild(level2node1_3); - DiagramViewModel.ClearSelectedItemsCommand.Execute(null); - + DiagramViewModel.ClearSelectedItemsCommand.Execute(null); level1node.LayoutUpdated(); + } public override void Dispose() diff --git a/AIStudio.Wpf.DiagramDesigner/Converters/IntToBoolConverter.cs b/AIStudio.Wpf.DiagramDesigner/Converters/IntToBoolConverter.cs index 5ff50ac..9c85874 100644 --- a/AIStudio.Wpf.DiagramDesigner/Converters/IntToBoolConverter.cs +++ b/AIStudio.Wpf.DiagramDesigner/Converters/IntToBoolConverter.cs @@ -1,24 +1,25 @@ using System; +using System.Windows; using System.Windows.Data; namespace AIStudio.Wpf.DiagramDesigner.Converters { - public class IntToBoolConverter : IValueConverter + public class IntToVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - if (object.Equals(value, 0d)) - return false; + if (value == null || object.Equals(value, 0)) + return Visibility.Collapsed; else - return true; + return Visibility.Visible; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { - if (object.Equals(value, false)) - return 0d; + if (object.Equals(value, Visibility.Collapsed)) + return 0; else - return 1d; + return 1; } } } diff --git a/AIStudio.Wpf.DiagramDesigner/Converters/IntToVisibilityConverter.cs b/AIStudio.Wpf.DiagramDesigner/Converters/IntToVisibilityConverter.cs new file mode 100644 index 0000000..5ff50ac --- /dev/null +++ b/AIStudio.Wpf.DiagramDesigner/Converters/IntToVisibilityConverter.cs @@ -0,0 +1,24 @@ +using System; +using System.Windows.Data; + +namespace AIStudio.Wpf.DiagramDesigner.Converters +{ + public class IntToBoolConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (object.Equals(value, 0d)) + return false; + else + return true; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (object.Equals(value, false)) + return 0d; + else + return 1d; + } + } +} diff --git a/AIStudio.Wpf.DiagramDesigner/Geometrys/SizeBase.cs b/AIStudio.Wpf.DiagramDesigner/Geometrys/SizeBase.cs index 3b07922..63a2bec 100644 --- a/AIStudio.Wpf.DiagramDesigner/Geometrys/SizeBase.cs +++ b/AIStudio.Wpf.DiagramDesigner/Geometrys/SizeBase.cs @@ -76,6 +76,8 @@ namespace AIStudio.Wpf.DiagramDesigner.Geometrys public SizeBase Add(double value) => new SizeBase(Width + value, Height + value); + public SizeBase Add(double width, double height) => new SizeBase(Width + width, Height + height); + //public bool Equals(Size size) => size != null && Width == size.Width && Height == size.Height; //public override string ToString() => $"Size(width={Width}, height={Height})"; diff --git a/AIStudio.Wpf.DiagramDesigner/PathGenerators/PathGenerators.Smooth.cs b/AIStudio.Wpf.DiagramDesigner/PathGenerators/PathGenerators.Smooth.cs index 6211a04..7dd2c7e 100644 --- a/AIStudio.Wpf.DiagramDesigner/PathGenerators/PathGenerators.Smooth.cs +++ b/AIStudio.Wpf.DiagramDesigner/PathGenerators/PathGenerators.Smooth.cs @@ -6,7 +6,7 @@ namespace AIStudio.Wpf.DiagramDesigner { public static partial class PathGenerators { - private const double _margin = 125; + public static PathGeneratorResult Smooth(IDiagramViewModel _, ConnectionViewModel link, PointBase[] route, PointBase source, PointBase target) { @@ -74,17 +74,39 @@ namespace AIStudio.Wpf.DiagramDesigner if (sourceOrientation == ConnectorOrientation.None)//按照线条的四象限来处理。 { var slope = (route[1].Y - route[0].Y) / (route[1].X - route[0].X); + if (Math.Abs(slope) < link.SmoothAutoSlope) + { + if (route[1].X > route[0].X) + { + sourceOrientation = ConnectorOrientation.Right; + } + else + { + sourceOrientation = ConnectorOrientation.Left; + } + } + else + { + if (route[1].Y > route[0].Y)//Y轴方向是反的 + { + sourceOrientation = ConnectorOrientation.Bottom; + } + else + { + sourceOrientation = ConnectorOrientation.Top; + } + } } - var curvePointA = GetCurvePoint(route[0].X, route[0].Y, cX, cY, sourceOrientation); - var curvePointB = GetCurvePoint(route[1].X, route[1].Y, cX, cY, link.SinkConnectorInfo?.Orientation); + var curvePointA = GetCurvePoint(route[0].X, route[0].Y, cX, cY, link.SmoothMargin, sourceOrientation); + var curvePointB = GetCurvePoint(route[1].X, route[1].Y, cX, cY, link.SmoothMargin, link.SinkConnectorInfo?.Orientation); return new[] { route[0], curvePointA, curvePointB, route[1] }; } } - private static PointBase GetCurvePoint(double pX, double pY, double cX, double cY, ConnectorOrientation? alignment) + private static PointBase GetCurvePoint(double pX, double pY, double cX, double cY, double smoothMargin, ConnectorOrientation? alignment) { - var margin = Math.Min(_margin, Math.Pow(Math.Pow(pX - cX, 2) + Math.Pow(pY - cY, 2), .5)); + var margin = Math.Min(smoothMargin, Math.Pow(Math.Pow(pX - cX, 2) + Math.Pow(pY - cY, 2), .5)); switch (alignment) { case ConnectorOrientation.Top: return new PointBase(pX, Math.Min(pY - margin, cY)); diff --git a/AIStudio.Wpf.DiagramDesigner/UserControls/DiagramControl.xaml b/AIStudio.Wpf.DiagramDesigner/UserControls/DiagramControl.xaml index abc6a2f..0c40cf4 100644 --- a/AIStudio.Wpf.DiagramDesigner/UserControls/DiagramControl.xaml +++ b/AIStudio.Wpf.DiagramDesigner/UserControls/DiagramControl.xaml @@ -301,13 +301,15 @@ + Value="{Binding EnabledForSelection}" /> + Value="{Binding EnabledForConnection}" /> + + Value="{Binding ItemHeight}" /> @@ -351,7 +353,7 @@ VerticalAlignment="Stretch" Content="{TemplateBinding Content}" /> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - + - \ No newline at end of file diff --git a/AIStudio.Wpf.Mind/ViewModels/MindNode.cs b/AIStudio.Wpf.Mind/ViewModels/MindNode.cs index 05f2d20..a2c1847 100644 --- a/AIStudio.Wpf.Mind/ViewModels/MindNode.cs +++ b/AIStudio.Wpf.Mind/ViewModels/MindNode.cs @@ -1,9 +1,13 @@ 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; @@ -13,12 +17,12 @@ namespace AIStudio.Wpf.Mind.ViewModels { 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) @@ -29,6 +33,90 @@ namespace AIStudio.Wpf.Mind.ViewModels { } + 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 { @@ -48,10 +136,28 @@ namespace AIStudio.Wpf.Mind.ViewModels } } - public double Spacing + private bool _isExpanded = true; + public bool IsExpanded + { + get + { + return _isExpanded; + } + set + { + SetProperty(ref _isExpanded, value); + } + } + + public bool LayoutUpdating { get; set; - } = 20; + } + + public SizeBase Spacing + { + get; set; + } = new SizeBase(15, 15); public List Children { @@ -62,170 +168,357 @@ namespace AIStudio.Wpf.Mind.ViewModels { get { - return this.Size.Add(Spacing * 2); + return this.Size.Add(Spacing.Width * 2, Spacing.Height * 2); } } public SizeBase DesiredSize { - get;set; + get; set; } - public SizeBase MeasureOverride() + public PointBase DesiredPosition { - var sizewithSpacing = SizeWithSpacing; - if (Children?.Count > 0) + 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) { - var childrensizes = Children.Select(p => p.MeasureOverride()).ToArray(); - sizewithSpacing = new SizeBase(sizewithSpacing.Width + childrensizes.Max(p => p.Width), Math.Max(sizewithSpacing.Height, childrensizes.Sum(p => p.Height))); } - DesiredSize = sizewithSpacing; - return DesiredSize; + 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() { - var size = MeasureOverride(); - ArrangeOverride(size); + GetLevel1Node()?.Level1LayoutUpdated(); } - public void ArrangeOverride(SizeBase sizeBase) + protected void Level1LayoutUpdated() { - double left = MiddlePosition.X + ItemWidth / 2 + Spacing; - double top = MiddlePosition.Y - sizeBase.Height / 2; + 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 + Spacing; - child.Top = top + child.DesiredSize.Height / 2 - child.ItemHeight; + + 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(child.DesiredSize); + child.ArrangeOverride(); } } } } - - public class MindLevel1Node : MindNode - { - public MindLevel1Node() : this(null) - { - - } - - public MindLevel1Node(IDiagramViewModel root) : base(root, NodeLevel.Level1) - { - - } - - public MindLevel1Node(IDiagramViewModel root, SelectableItemBase designer) : base(root, designer) - { - - } - - public MindLevel1Node(IDiagramViewModel root, SerializableItem serializableItem, string serializableType) : base(root, serializableItem, serializableType) - { - - } - protected override void Init(IDiagramViewModel root) - { - base.Init(root); - - ItemWidth = 80; - ItemHeight = 40; - this.ClearConnectors(); - var port = new FullyCreatedConnectorInfo(root, this, ConnectorOrientation.None, true) { XRatio = 0.5, YRatio = 0.5 }; - this.AddConnector(port); - IsInnerConnector = true; - } - - public RectangleBase Rectangle - { - get; set; - } - - } - - public class MindLevel2Node : MindNode - { - public MindLevel2Node() : this(null) - { - - } - - public MindLevel2Node(IDiagramViewModel root) : base(root, NodeLevel.Level2) - { - - } - - public MindLevel2Node(IDiagramViewModel root, SelectableItemBase designer) : base(root, designer) - { - - } - - public MindLevel2Node(IDiagramViewModel root, SerializableItem serializableItem, string serializableType) : base(root, serializableItem, serializableType) - { - - } - - protected override void Init(IDiagramViewModel root) - { - base.Init(root); - - ItemWidth = 80; - ItemHeight = 40; - this.ClearConnectors(); - var port1 = new FullyCreatedConnectorInfo(root, this, ConnectorOrientation.None, true) { XRatio = 0, YRatio = 0.5 }; - this.AddConnector(port1); - var port2 = new FullyCreatedConnectorInfo(root, this, ConnectorOrientation.None, true) { XRatio = 1, YRatio = 0.5 }; - this.AddConnector(port2); - IsInnerConnector = true; - } - - public PointBase Offset - { - get; set; - } - } - - public class MindLevel3Node : MindNode - { - public MindLevel3Node() : this(null) - { - - } - - public MindLevel3Node(IDiagramViewModel root) : base(root, NodeLevel.Level2) - { - - } - - public MindLevel3Node(IDiagramViewModel root, SelectableItemBase designer) : base(root, designer) - { - - } - - public MindLevel3Node(IDiagramViewModel root, SerializableItem serializableItem, string serializableType) : base(root, serializableItem, serializableType) - { - - } - protected override void Init(IDiagramViewModel root) - { - base.Init(root); - - ItemWidth = 80; - ItemHeight = 40; - this.ClearConnectors(); - var port1 = new FullyCreatedConnectorInfo(root, this, ConnectorOrientation.None, true) { XRatio = 0, YRatio = 1 }; - this.AddConnector(port1); - var port2 = new FullyCreatedConnectorInfo(root, this, ConnectorOrientation.None, true) { XRatio = 1, YRatio = 1 }; - this.AddConnector(port2); - IsInnerConnector = true; - } - public PointBase Offset - { - get; set; - } - } }