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() : this(null) { } public MindNode(IDiagramViewModel root) : base(root) { } 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; AlwayForResized = true; CustomText = true; AddChildCommand = (Root as IMindDiagramViewModel)?.AddChildCommand; AddParentCommand = (Root as IMindDiagramViewModel)?.AddParentCommand; AddPeerCommand = (Root as IMindDiagramViewModel)?.AddPeerCommand; DeleteCommand = (Root as IMindDiagramViewModel)?.DeleteCommand; MoveForwardCommand = (Root as IMindDiagramViewModel)?.MoveForwardCommand; MoveBackCommand = (Root as IMindDiagramViewModel)?.MoveBackCommand; BuildMenuOptions(); } public 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(); IsInnerConnector = true; MindLayout.Appearance(this, initAppearance); this.PropertyChanged -= this.Item_PropertyChanged; this.PropertyChanged += this.Item_PropertyChanged; } protected override void LoadDesignerItemViewModel(SelectableItemBase designerbase) { base.LoadDesignerItemViewModel(designerbase); if (designerbase is MindNodeDesignerItem designer) { Spacing = designer.Spacing; Offset = designer.Offset; IsExpanded = designer.IsExpanded; } } public override DiagramNode ToDiagram() { var mindNodeModel = new MindNodeModel(); mindNodeModel.Spacing = Spacing; mindNodeModel.Offset = Offset; mindNodeModel.IsExpanded = IsExpanded; return mindNodeModel; } #region 属性 public IMindLayout MindLayout { get; set; } public MindNode ParentNode { get { return Parent as MindNode; } } public int NodeLevel { get { if (ParentNode == null) { return 0; } else { return ParentNode.NodeLevel + 1; } } } public MindType MindType { get { return (Root as IMindDiagramViewModel)?.MindType ?? MindType.Mind; } } 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 _children = new ObservableCollection(); public ObservableCollection 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 _tags; public ObservableCollection 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 DeleteCommand { get; private set; } public SimpleCommand MoveForwardCommand { get; private set; } public SimpleCommand MoveBackCommand { get; private set; } #endregion #region 菜单 private void BuildMenuOptions() { menuOptions = new ObservableCollection(); 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 = DeleteCommand; menuItem.CommandParameter = this; menuOptions.Add(menuItem); } #endregion #region 操作 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; item.InitLayout(true); ConnectionViewModel connector = MindLayout?.GetOrSetConnectionViewModel(this, item); Root?.DirectAddItemCommand.Execute(new SelectableDesignerItemViewModelBase[] { item, connector }); connector.ZIndex = -1; connector.IsSelected = false; item.IsSelected = false; this.IsSelected = true; } public void RemoveChild(MindNode item, bool removeall = false) { 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 (removeall) { if (item.Children?.Count > 0) { foreach (var child in item.Children.ToList()) { item.RemoveChild(child); } } } } public void InitConnectLayout() { var connector = Root?.Items.OfType().Where(p => p.IsFullConnection).FirstOrDefault(p => p.SinkConnectorInfoFully.DataItem == this); if (connector != null) { MindLayout?.GetOrSetConnectionViewModel(connector.SourceConnectorInfo.DataItem as MindNode, connector.SinkConnectorInfoFully.DataItem as MindNode, connector); } } #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, false); break; case nameof(Left): { if (e is ValuePropertyChangedEventArgs valuePropertyChangedEventArgs) { if (NodeLevel > 0 && IsSelected) { UpdateOffsetX((double)valuePropertyChangedEventArgs.OldValue, (double)valuePropertyChangedEventArgs.NewValue); } } break; } case nameof(Top): { if (e is ValuePropertyChangedEventArgs valuePropertyChangedEventArgs) { if (NodeLevel > 0 && IsSelected) { UpdateOffsetY((double)valuePropertyChangedEventArgs.OldValue, (double)valuePropertyChangedEventArgs.NewValue); } } 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; } } } public override void AddToSelection(bool selected) { foreach (SelectableDesignerItemViewModelBase item in Root.SelectedItems.ToList()) item.IsSelected = false; Root.SelectedItems.Clear(); IsSelected = selected; } #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 == 1) { 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); } } } }