using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; using System.Security.Policy; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Input; 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); } public override DiagramNode ToDiagram() { var mindNodeModel = new MindNodeModel(); mindNodeModel.Spacing = Spacing; mindNodeModel.Offset = Offset; mindNodeModel.IsExpanded = IsExpanded; mindNodeModel.LinkInfoModel = new LinkInfoModel(LinkInfo); mindNodeModel.ImageInfoModel = new ImageInfoModel(ImageInfo); mindNodeModel.Remark = Remark; mindNodeModel.Priority = Priority; mindNodeModel.Rate = Rate; if (Tags != null) { mindNodeModel.Tags = new List(Tags); } return mindNodeModel; } protected override void Init(IDiagramViewModel root, bool initNew) { base.Init(root, initNew); EnabledForConnection = false; AlwayForResized = true; CustomText = true; AddChildCommand = (Root as IMindDiagramViewModel)?.AddChildCommand; AddParentCommand = (Root as IMindDiagramViewModel)?.AddParentCommand; AddPearCommand = (Root as IMindDiagramViewModel)?.AddPearCommand; DeleteCommand = (Root as IMindDiagramViewModel)?.DeleteCommand; MoveForwardCommand = (Root as IMindDiagramViewModel)?.MoveForwardCommand; MoveBackCommand = (Root as IMindDiagramViewModel)?.MoveBackCommand; BuildMenuOptions(); Tags = new ObservableCollection(); LinkInfo = new LinkInfo(); ImageInfo = new ImageInfo(); } protected override void InitNew() { this.ClearConnectors(); } 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, MindThemeModel, initAppearance); this.PropertyChanged -= this.Item_PropertyChanged; this.PropertyChanged += this.Item_PropertyChanged; } public void ThemeChange() { MindThemeHelper.ThemeChange(this, MindThemeModel); } protected override void LoadDesignerItemViewModel(SelectableItemBase designerbase) { base.LoadDesignerItemViewModel(designerbase); if (designerbase is MindNodeDesignerItem designer) { Spacing = designer.Spacing; Offset = designer.Offset; IsExpanded = designer.IsExpanded; LinkInfo = new LinkInfo() { Link = designer.LinkInfoItem?.Link, Text = designer.LinkInfoItem?.Text }; ImageInfo = new ImageInfo() { Url = designer.ImageInfoItem?.Url, Text = designer.ImageInfoItem?.Text }; Remark = designer.Remark; Priority = designer.Priority; Rate = designer.Rate; if (designer.Tags != null) { Tags = new ObservableCollection(designer.Tags); } } } #region 属性 public IMindLayout MindLayout { get; set; } public MindNode ParentNode { get { return Parent as MindNode; } } public MindNode RootNode { get { return GetLevel1Node(); } } 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; } } public MindThemeModel MindThemeModel { get { return (Root as IMindDiagramViewModel)?.MindThemeModel ?? MindThemeHelper.GetTheme("天空蓝"); } } 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 { if (_linkInfo != null) { _linkInfo.PropertyChanged -= Item_PropertyChanged; } SetProperty(ref _linkInfo, value); if (_linkInfo != null) { _linkInfo.PropertyChanged += Item_PropertyChanged; ; } } } private ImageInfo _imageInfo; public ImageInfo ImageInfo { get { return _imageInfo; } set { if (_imageInfo != null) { _imageInfo.PropertyChanged -= Item_PropertyChanged; } SetProperty(ref _imageInfo, value); if (_imageInfo != null) { _imageInfo.PropertyChanged += Item_PropertyChanged; } } } private string _remark; public string Remark { get { return _remark; } set { SetProperty(ref _remark, value); } } private double? _priority; public double? Priority { get { return _priority; } set { SetProperty(ref _priority, value); } } private double? _rate; public double? Rate { get { return _rate; } set { SetProperty(ref _rate, value); } } private ObservableCollection _tags; public ObservableCollection Tags { get { return _tags; } set { if (_tags != null) { _tags.CollectionChanged -= _tags_CollectionChanged; } SetProperty(ref _tags, value); if (_tags != null) { _tags.CollectionChanged += _tags_CollectionChanged; } } } private void _tags_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { foreach (var item in e.NewItems.OfType()) { var width = GetTextDisplayWidthHelper.GetTextDisplayWidth(item, new FontFamily(FontViewModel.FontFamily), FontViewModel.FontStyle, FontViewModel.FontWeight, FontViewModel.FontStretch, 12) + 6; ItemWidth += width; } } if (e.OldItems != null) { foreach (var item in e.OldItems.OfType()) { var width = GetTextDisplayWidthHelper.GetTextDisplayWidth(item, new FontFamily(FontViewModel.FontFamily), FontViewModel.FontStyle, FontViewModel.FontWeight, FontViewModel.FontStretch, 12) + 6; ItemWidth -= width; } } } #endregion #region 命令 public ICommand AddParentCommand { get; private set; } public ICommand AddChildCommand { get; private set; } public ICommand AddPearCommand { get; private set; } public ICommand DeleteCommand { get; private set; } public ICommand MoveForwardCommand { get; private set; } public ICommand 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 = AddPearCommand; 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);//因为节点的层级不同的样式,所以需要Parent确定后才能初始化 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); } else if (ParentNode != null) { connector = MindLayout?.GetOrSetConnectionViewModel(ParentNode, this, null); Root?.DirectAddItemCommand.Execute(new SelectableDesignerItemViewModelBase[] { connector }); connector.ZIndex = -1; connector.IsSelected = false; } } #endregion private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (GetLevel1Node()?.LayoutUpdating == true) return; if (Root == null) return; switch (e.PropertyName) { case nameof(IsExpanded): case nameof(ItemWidth): case nameof(ItemHeight): { LayoutUpdated(); break; } case nameof(NodeLevel): MindLayout?.Appearance(this); 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; } case nameof(Rate): case nameof(Priority): case nameof(Remark): case nameof(LinkInfo.Link): { if (e is ValuePropertyChangedEventArgs valuePropertyChangedEventArgs) { if (string.IsNullOrEmpty(valuePropertyChangedEventArgs.OldValue?.ToString()) && !string.IsNullOrEmpty(valuePropertyChangedEventArgs.NewValue?.ToString())) { ItemWidth += 24; } else if (!string.IsNullOrEmpty(valuePropertyChangedEventArgs.OldValue?.ToString()) && string.IsNullOrEmpty(valuePropertyChangedEventArgs.NewValue?.ToString())) { ItemWidth -= 24; } } break; } case nameof(ImageInfo.Url): { if (e is ValuePropertyChangedEventArgs valuePropertyChangedEventArgs) { if (string.IsNullOrEmpty(valuePropertyChangedEventArgs.OldValue?.ToString()) && !string.IsNullOrEmpty(valuePropertyChangedEventArgs.NewValue?.ToString())) { ItemWidth = Math.Max(ItemWidth, 160); ItemHeight += 135; } else if (!string.IsNullOrEmpty(valuePropertyChangedEventArgs.OldValue?.ToString()) && string.IsNullOrEmpty(valuePropertyChangedEventArgs.NewValue?.ToString())) { ItemHeight -= 135; } } 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 _link; public string Link { get { return _link; } set { SetProperty(ref _link, 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); } } } }