using System; using System.Linq; using System.Windows; using System.Windows.Data; using System.Windows.Media; using System.Windows.Input; using System.ComponentModel; using System.Windows.Shapes; using SHJX.Service.WorkFlowEdit; using System.Windows.Controls; using SHJX.Service.Common.UserColor; using System.Collections.Generic; namespace Flowchart { class Controller : IDiagramController { private class UpdateScope : IDisposable { private readonly Controller _parent; public bool IsInprogress { get; set; } public UpdateScope(Controller parent) { _parent = parent; } public void Dispose() { IsInprogress = false; _parent.UpdateView(); } } private readonly DiagramView _view; private readonly FlowchartModel _model; private readonly UpdateScope _updateScope; public Controller(DiagramView view, FlowchartModel model) { _view = view; _model = model; _model.Nodes.CollectionChanged += NodesCollectionChanged; _model.Links.CollectionChanged += LinksCollectionChanged; _updateScope = new UpdateScope(this); foreach (FlowNode t in _model.Nodes) { t.PropertyChanged += NodePropertyChanged; } UpdateView(); } void NodesCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { foreach (INotifyPropertyChanged t in e.NewItems.OfType()) { t.PropertyChanged += NodePropertyChanged; } } if (e.OldItems != null) { foreach (INotifyPropertyChanged t in e.OldItems.OfType()) { t.PropertyChanged -= NodePropertyChanged; } } UpdateView(); } void LinksCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) { UpdateView(); } void NodePropertyChanged(object sender, PropertyChangedEventArgs e) { FlowNode fn = sender as FlowNode; Node n = _view.Children.OfType().FirstOrDefault(p => p.ModelElement.Equals(fn)); if (fn == null || n == null) { return; } UpdateNode(fn, n); } private void UpdateView() { if (!_updateScope.IsInprogress) { _view.Children.Clear(); foreach (FlowNode node in _model.Nodes) { _view.Children.Add(UpdateNode(node, null)); } foreach (Link link in _model.Links) { _view.Children.Add(CreateLink(link)); } } } private Node UpdateNode(FlowNode node, Node item) { if (item == null) { item = new Node { ModelElement = node }; CreatePorts(node, item); item.Content = CreateContent(node); } item.Width = 100; item.Height = 60; item.CanResize = false; item.SetValue(Canvas.LeftProperty, node.Column * _view.GridCellSize.Width + 10); item.SetValue(Canvas.TopProperty, node.Row * _view.GridCellSize.Height + 25); return item; } public static FrameworkElement CreateContent(FlowNode node) { TextBlock textBlock = new() { Foreground = Brushes.White, VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center }; Binding bd = new("Text") { Source = node }; textBlock.SetBinding(TextBlock.TextProperty, bd); FrameworkElement ui = node.Kind switch { NodeKinds.Start or NodeKinds.End => GetStartAndEndUI(textBlock), NodeKinds.MotorX or NodeKinds.MotorY or NodeKinds.MotorZ or NodeKinds.ManiGrab or NodeKinds.ManiLoosen or NodeKinds.MotorZGoBack => GetStepUI(textBlock), NodeKinds.Condition => GetConditionUI(textBlock), _ => throw new ArgumentException("未找到对应的节点"), }; return ui; } private static FrameworkElement GetConditionUI(TextBlock textBlock) { Path ui = new() { Stroke = Brushes.Black, StrokeThickness = 1, Fill = UColor.MainColor }; GeometryConverter converter = new(); ui.Data = (Geometry)converter.ConvertFrom("M 0,0.25 L 0.5 0 L 1,0.25 L 0.5,0.5 Z"); ui.Stretch = Stretch.Uniform; Grid grid = new(); grid.Children.Add(ui); grid.Children.Add(textBlock); return grid; } private static FrameworkElement GetStepUI(TextBlock textBlock) { return new Border { BorderBrush = Brushes.Black, BorderThickness = new Thickness(1), Background = UColor.MainColor, Child = textBlock }; } private static FrameworkElement GetStartAndEndUI(TextBlock textBlock) { return new Border { CornerRadius = new CornerRadius(15), BorderBrush = Brushes.Black, BorderThickness = new Thickness(1), Background = UColor.MainColor, Child = textBlock }; } private void CreatePorts(FlowNode node, Node item) { foreach (PortKinds kind in node.GetPorts()) { EllipsePort port = new() { Width = 10, Height = 10, Margin = new Thickness(-5), Visibility = Visibility.Visible, VerticalAlignment = ToVerticalAligment(kind), HorizontalAlignment = ToHorizontalAligment(kind), CanAcceptIncomingLinks = kind == PortKinds.Top }; port.CanAcceptOutgoingLinks = !port.CanAcceptIncomingLinks; port.Tag = kind; port.Cursor = Cursors.Cross; port.CanCreateLink = true; item.Ports.Add(port); } } private Control CreateLink(Link link) { SegmentLink item = new() { EndCap = true, ModelElement = link, Source = FindPort(link.Source, link.SourcePort), Target = FindPort(link.Target, link.TargetPort) }; Binding bd = new("Text") { Source = link }; item.SetBinding(LinkBase.LabelProperty, bd); return item; } private IPort FindPort(FlowNode node, PortKinds portKind) { if (_view.Items.FirstOrDefault(p => p.ModelElement.Equals(node)) is not INode inode) { return null; } FrameworkElement port = inode.Ports.OfType().FirstOrDefault(p => p.VerticalAlignment.Equals(ToVerticalAligment(portKind)) && p.HorizontalAlignment.Equals(ToHorizontalAligment(portKind))); return (IPort)port; } private VerticalAlignment ToVerticalAligment(PortKinds kind) => kind switch { PortKinds.Top => VerticalAlignment.Top, PortKinds.Bottom => VerticalAlignment.Bottom, _ => VerticalAlignment.Center, }; private HorizontalAlignment ToHorizontalAligment(PortKinds kind) => kind switch { PortKinds.Left => HorizontalAlignment.Left, PortKinds.Right => HorizontalAlignment.Right, _ => HorizontalAlignment.Center }; private void DeleteSelection() { using (BeginUpdate()) { IEnumerable nodes = _view.Selection.Select(p => p.ModelElement as FlowNode).Where(p => p != null); IEnumerable links = _view.Selection.Select(p => p.ModelElement as Link).Where(p => p != null); _model.Nodes.RemoveRange(p => nodes.Contains(p)); _model.Links.RemoveRange(p => links.Contains(p)); _model.Links.RemoveRange(p => nodes.Contains(p.Source) || nodes.Contains(p.Target)); } } private IDisposable BeginUpdate() { _updateScope.IsInprogress = true; return _updateScope; } #region IDiagramController Members public void UpdateItemsBounds(DiagramItem[] items, Rect[] bounds) { for (int i = 0; i < items.Length; i++) { if (items[i].ModelElement is FlowNode node) { node.Column = (int)(bounds[i].X / _view.GridCellSize.Width); node.Row = (int)(bounds[i].Y / _view.GridCellSize.Height); } } } public void UpdateLink(LinkInfo initialState, ILink link) { using (BeginUpdate()) { PortBase sourcePort = link.Source as PortBase; Node source = VisualHelper.FindParent(sourcePort); PortBase targetPort = link.Target as PortBase; Node target = VisualHelper.FindParent(targetPort); _model.Links.Remove((link as LinkBase).ModelElement as Link); _model.Links.Add(new Link((FlowNode)source.ModelElement, (PortKinds)sourcePort.Tag, (FlowNode)target.ModelElement, (PortKinds)targetPort.Tag)); } } public void ExecuteCommand(ICommand command, object parameter) { if (command == ApplicationCommands.Delete) DeleteSelection(); } public bool CanExecuteCommand(ICommand command, object parameter) { return command == ApplicationCommands.Delete; } #endregion } }