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 System.Windows.Controls; using System.Collections.Generic; using System.Collections.Specialized; using Aga.Diagrams; using Aga.Diagrams.Controls; namespace FlowWorkEditDemo { class ShapesController : IDiagramController { private class UpdateScope : IDisposable { private ShapesController _parent; public bool IsInprogress { get; set; } public UpdateScope(ShapesController parent) { _parent = parent; } public void Dispose() { IsInprogress = false; _parent.UpdateView(); } } private List Shapes { get; set; } private DiagramView View { get; set; } internal FlowchartModel Model { get; set; } private UpdateScope UpdateScope1 { get; set; } public ShapesController(DiagramView view, FlowchartModel model) { View = view; Model = model; Model.Nodes.CollectionChanged += NodesCollectionChanged; Model.Links.CollectionChanged += LinksCollectionChanged; UpdateScope1 = new UpdateScope(this); foreach (var t in Model.Nodes) { t.PropertyChanged += NodePropertyChanged; } UpdateView(); } void LinksCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { UpdateView(); } void NodesCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { foreach (var t in e.NewItems.OfType()) { t.PropertyChanged += NodePropertyChanged; } } if (e.OldItems != null) { foreach (var t in e.OldItems.OfType()) { t.PropertyChanged -= NodePropertyChanged; } } UpdateView(); } void NodePropertyChanged(object sender, PropertyChangedEventArgs e) { var fn = sender as FlowNode; var n = View.Children.OfType().FirstOrDefault(p => p.ModelElement == fn); if (fn != null && n != null) UpdateNode(fn, n); } private Node UpdateNode(FlowNode node, Node item) { if (item == null) { item = new Node { ModelElement = node }; CreatePorts(node, item); item.Content = CreateContent(node); } item.Width = 120; 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; } private void CreatePorts(FlowNode node, Node item) { foreach (var kind in node.GetPorts()) { var port = new EllipsePort { 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 static VerticalAlignment ToVerticalAligment(PortKinds kind) { var alignment = kind switch { PortKinds.Top => VerticalAlignment.Top, PortKinds.Bottom => VerticalAlignment.Bottom, _ => VerticalAlignment.Center, }; return alignment; } private static HorizontalAlignment ToHorizontalAligment(PortKinds kind) { var alignment = kind switch { PortKinds.Left => HorizontalAlignment.Left, PortKinds.Right => HorizontalAlignment.Right, _ => HorizontalAlignment.Center, }; return alignment; } public static FrameworkElement CreateContent(FlowNode node) { var textBlock = new TextBlock() { VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center }; var b = new Binding("Text") { Source = node }; textBlock.SetBinding(TextBlock.TextProperty, b); FrameworkElement ui = node.Kind switch { NodeKinds.Start or NodeKinds.End => GetStartOrEndUI(textBlock), NodeKinds.Action => GetActionUI(textBlock), _ => GetDefaultUI(textBlock), }; return ui; } private static FrameworkElement GetStartOrEndUI(TextBlock textBlock) { return new Border { CornerRadius = new CornerRadius(15), BorderBrush = Brushes.Black, BorderThickness = new Thickness(1), Background = Brushes.Yellow, Child = textBlock }; } private static FrameworkElement GetDefaultUI(TextBlock textBlock) { var ui = new Path { Stroke = Brushes.Black, StrokeThickness = 1, Fill = Brushes.Pink }; var converter = new GeometryConverter(); 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; var grid = new Grid(); grid.Children.Add(ui); grid.Children.Add(textBlock); return grid; } private static FrameworkElement GetActionUI(TextBlock textBlock) { var ui = new Border { BorderBrush = Brushes.Black, BorderThickness = new Thickness(1), Background = Brushes.Lime }; ui.Child = textBlock; return ui; } private List CreateModel() { var list = new List(); var s = new RectangleShape(); s.Location = new Point(10, 10); s.Size = new Size(50, 50); list.Add(s); var s2 = new RectangleShape { Location = new Point(200, 10), Size = new Size(50, 50) }; list.Add(s2); var s3 = new EllipseShape(); s3.Location = new Point(90, 100); s3.Size = new Size(80, 50); list.Add(s3); s.Links.Add(s3); s3.Links.Add(s2); s2.Links.Add(s); return list; } private void UpdateView() { if (!UpdateScope1.IsInprogress) { View.Children.Clear(); foreach (var node in Model.Nodes) View.Children.Add(UpdateNode(node, null)); foreach (var link in Model.Links) View.Children.Add(CreateLink(link)); } } private IPort FindPort(FlowNode node, PortKinds portKind) { var inode = View.Items.FirstOrDefault(p => p.ModelElement == node) as INode; if (inode == null) return null; var port = inode.Ports.OfType().FirstOrDefault( p => p.VerticalAlignment == ToVerticalAligment(portKind) && p.HorizontalAlignment == ToHorizontalAligment(portKind) ); return (IPort)port; } private Control CreateLink(Link link) { var item = new OrthogonalLink { ModelElement = link, EndCap = true, Source = FindPort(link.Source, link.SourcePort), Target = FindPort(link.Target, link.TargetPort) }; var b = new Binding("Text") { Source = link }; item.SetBinding(LinkBase.LabelProperty, b); return item; } private void BindEvents() { foreach (var s in Shapes) { s.PropertyChanged += ShapePropertyChanged; } } void ShapePropertyChanged(object sender, PropertyChangedEventArgs e) { var shape = sender as ShapeBase; UpdateUIElement(shape); } private void UpdateUIElement(ShapeBase shape) { UpdateUIElement(shape, (Node)View.FindItem(shape)); } private void UpdateUIElement(ShapeBase shape, Node item) { if (item == null) { item = new Node(); if (shape is RectangleShape) { item.Ports.Add(new RectPort() { Visibility = Visibility.Hidden }); var ui = new Border(); ui.CornerRadius = new CornerRadius(15); ui.BorderBrush = new SolidColorBrush(Colors.Black); ui.BorderThickness = new Thickness(1); ui.Background = new SolidColorBrush(Colors.Yellow); item.Content = ui; } else { item.Ports.Add(new EllipsePort() { Visibility = Visibility.Hidden }); var ui = new Ellipse { Fill = Brushes.Green, Stroke = Brushes.Black, StrokeThickness = 1 }; item.Content = ui; } item.ModelElement = shape; View.Children.Add(item); } item.Width = shape.Size.Width; item.Height = shape.Size.Height; item.SetValue(Canvas.LeftProperty, shape.Location.X); item.SetValue(Canvas.TopProperty, shape.Location.Y); } private void CreateLinks(ShapeBase shape, Node item) { foreach (var dest in shape.Links) { var destItem = (Node)View.FindItem(dest); if (destItem != null) { var link = new SegmentLink { EndCap = true, Source = item.Ports.First(), Target = destItem.Ports.First() }; View.Children.Add(link); } } } #region IDiagramController Members public void UpdateItemsBounds(DiagramItem[] items, Rect[] bounds) { for (int i = 0; i < items.Length; i++) { var node = items[i].ModelElement as FlowNode; if (node != null) { 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) { } public bool CanExecuteCommand(ICommand command, object parameter) { return (command == ApplicationCommands.Delete && View.Selection.Count > 0); } public void ExecuteCommand(ICommand command, object parameter) { if (command == ApplicationCommands.Delete) { foreach (var e in View.Selection.Select(p => p.ModelElement)) { if (e is ShapeBase) Shapes.Remove(e as ShapeBase); } UpdateView(); } } #endregion } }