private bool PasteShapes() { // Get prototypes from clipboard, if they exist IDataObject dataObject = Clipboard.GetDataObject(); ElementGroupPrototype elementGroupPrototype = ElementOperations.GetElementGroupPrototype(ServiceProvider, dataObject); // if we're not pasting on the diagram (i.e., the ModelRoot), we're pasting the prototype if (TargetElement is ModelRoot && CurrentModelingDocView is EFModelDocView efModelDocView && efModelDocView.Diagram is EFModelDiagram currentDiagram && currentDiagram?.Store != null && elementGroupPrototype != null) { Store store = efModelDocView.Diagram.Store; // get matching elements from the store List <ModelElement> modelElements = elementGroupPrototype.ProtoElements .Select(p => store.ElementDirectory.FindElement(p.ElementId)) .Where(e => e != null) .ToList(); if (modelElements.Any()) { List <ModelClass> modelClasses = modelElements.OfType <ModelClass>().ToList(); List <Comment> comments = modelElements.OfType <Comment>().ToList(); List <ModelElement> everythingElse = modelElements.Except(modelClasses).Except(comments).ToList(); List <ShapeElement> newShapes = new List <ShapeElement>(); using (Transaction t = store.TransactionManager.BeginTransaction()) { // paste classes and comments first to ensure that any possible connector end is present before the connectors arrive newShapes.AddRange(modelClasses.Select(e => EFModelDiagram.AddExistingModelElement(currentDiagram, e))); newShapes.AddRange(comments.Select(e => EFModelDiagram.AddExistingModelElement(currentDiagram, e))); newShapes.AddRange(everythingElse.Select(e => EFModelDiagram.AddExistingModelElement(currentDiagram, e))); newShapes = newShapes.Where(s => s != null).ToList(); // if nothing got pasted (because it's already there), we succeeded in our paste but didn't really change // the display, so nothing further needs done if (!newShapes.Any()) { return(true); } t.Commit(); } using (Transaction t = store.TransactionManager.BeginTransaction()) { Commands.LayoutDiagram(currentDiagram, newShapes); t.Commit(); } currentDiagram.Invalidate(); return(true); } } return(false); }
public static ShapeElement AddExistingModelElement(EFModelDiagram diagram, ModelElement element) { if (diagram?.NestedChildShapes?.Any(s => s.ModelElement == element) != false) { return(null); } using (Transaction t = element.Store.TransactionManager.BeginTransaction("add existing model elements")) { diagram.ForceAddShape = true; FixUpAllDiagrams.FixUp(diagram, element); diagram.ForceAddShape = false; // find all element links that are attached to our element where the ends are in the diagram but the link isn't already in the diagram List <ElementLink> elementLinks = element.Store.GetAll <ElementLink>() .Where(link => link.LinkedElements.Contains(element) && link.LinkedElements.All(linkedElement => diagram.DisplayedElements.Contains(linkedElement)) && !diagram.DisplayedElements.Contains(link)) .ToList(); foreach (ElementLink elementLink in elementLinks) { FixUpAllDiagrams.FixUp(diagram, elementLink); } t.Commit(); return(diagram.NestedChildShapes.FirstOrDefault(s => s.ModelElement == element)); } }
/// <inheritdoc /> public override void ElementDeleting(ElementDeletingEventArgs e) { base.ElementDeleting(e); ModelDiagramData element = (ModelDiagramData)e.ModelElement; Store store = element.Store; Transaction current = store.TransactionManager.CurrentTransaction; if (current.IsSerializing || ModelRoot.BatchUpdating) { return; } if (BooleanQuestionDisplay.Show($"About to permanently delete diagram named {element.Name} - are you sure?") != true) { current.Rollback(); return; } EFModelDiagram diagram = element.GetDiagram(); ModelDiagramData.CloseDiagram?.Invoke(diagram); diagram.Delete(); }
internal static void LayoutDiagram(EFModelDiagram diagram, IEnumerable <ShapeElement> shapeElements) { using (Transaction tx = diagram.Store.TransactionManager.BeginTransaction("ModelAutoLayout")) { List <ShapeElement> shapeList = shapeElements.ToList(); List <DotNode> vertices = shapeList .OfType <NodeShape>() .Select(node => new DotNode { Shape = node }) .ToList(); List <DotEdge> edges = shapeList .OfType <BinaryLinkShape>() .Select(link => new DotEdge(vertices.Single(vertex => vertex.Shape.Id == link.FromShape.Id), vertices.Single(vertex => vertex.Shape.Id == link.ToShape.Id)) { Shape = link }) .ToList(); // use graphviz as the default if available if (File.Exists(EFModelPackage.Options.DotExePath)) { DoGraphvizLayout(vertices, edges, diagram); } else { DoStandardLayout(edges.Select(edge => edge.Shape).ToList(), diagram); } tx.Commit(); } }
internal static void LayoutDiagram(EFModelDiagram diagram) { try { Cursor.Current = Cursors.WaitCursor; ModelRoot modelRoot = diagram.Store.ModelRoot(); using (Transaction tx = diagram.Store.TransactionManager.BeginTransaction("ModelAutoLayout")) { List <NodeShape> nodeShapes = diagram.NestedChildShapes.Where(s => s.IsVisible).OfType <NodeShape>().ToList(); List <BinaryLinkShape> linkShapes = diagram.NestedChildShapes.Where(s => s.IsVisible).OfType <BinaryLinkShape>().ToList(); // The standard DSL layout method was selected. Just do the deed and be done with it. // otherwise, we need to run an MSAGL layout if (modelRoot.LayoutAlgorithm == LayoutAlgorithm.Default || modelRoot.LayoutAlgorithmSettings == null) { DoStandardLayout(linkShapes, diagram); } else { DoCustomLayout(nodeShapes, linkShapes, modelRoot); } tx.Commit(); } } finally { Cursor.Current = Cursors.Default; } }
public override void ElementPropertyChanged(ElementPropertyChangedEventArgs e) { base.ElementPropertyChanged(e); ModelDiagramData element = (ModelDiagramData)e.ModelElement; if (element.IsDeleted) { return; } Store store = element.Store; Transaction currentTransaction = store.TransactionManager.CurrentTransaction; if (currentTransaction.IsSerializing) { return; } if (Equals(e.NewValue, e.OldValue)) { return; } List <string> errorMessages = new List <string>(); switch (e.DomainProperty.Name) { case "Name": if (string.IsNullOrWhiteSpace(e.NewValue?.ToString())) { errorMessages.Add("Diagram must have a name"); break; } if (store.ElementDirectory.AllElements.OfType <ModelDiagramData>().Where(d => d != element).Any(d => d.Name == e.NewValue.ToString())) { errorMessages.Add("Diagram must have a unique name"); break; } EFModelDiagram diagram = element.GetDiagram(); if (diagram != null) { ModelDiagramData.CloseDiagram?.Invoke(diagram); diagram.Name = element.Name = e.NewValue.ToString(); } break; } errorMessages = errorMessages.Where(m => m != null).ToList(); if (errorMessages.Any()) { currentTransaction.Rollback(); ErrorDisplay.Show(string.Join("\n", errorMessages)); } }
public static void LayoutDiagram(EFModelDiagram diagram) { using (WaitCursor _ = new WaitCursor()) { IEnumerable <ShapeElement> shapeElements = diagram.NestedChildShapes.Where(s => s.IsVisible); LayoutDiagram(diagram, shapeElements); } }
private void OnMenuLayoutDiagram(object sender, EventArgs e) { EFModelDiagram diagram = CurrentSelection.Cast <EFModelDiagram>().SingleOrDefault(); if (diagram == null) { return; } Commands.LayoutDiagram(diagram); }
private bool PasteShapes(List <ModelElement> modelElements) { if (TargetElement is ModelRoot && CurrentModelingDocView is EFModelDocView efModelDocView && efModelDocView.Diagram is EFModelDiagram currentDiagram && currentDiagram.Store != null && modelElements.Any()) { Store store = efModelDocView.Diagram.Store; List <ModelClass> modelClasses = modelElements.OfType <ModelClass>().ToList(); List <Comment> comments = modelElements.OfType <Comment>().ToList(); List <ModelElement> everythingElse = modelElements.Except(modelClasses).Except(comments).ToList(); List <ShapeElement> newShapes = new List <ShapeElement>(); using (Transaction t = store.TransactionManager.BeginTransaction()) { // paste classes and comments first to ensure that any possible connector end is present before the connectors arrive newShapes.AddRange(modelClasses.Select(e => EFModelDiagram.AddExistingModelElement(currentDiagram, e))); newShapes.AddRange(comments.Select(e => EFModelDiagram.AddExistingModelElement(currentDiagram, e))); newShapes = newShapes.Where(s => s != null).ToList(); // select and show the new or existing shape. Search, in order, classes, comments, then everything else ModelElement firstElement = modelClasses.FirstOrDefault() ?? comments.FirstOrDefault() ?? everythingElse.FirstOrDefault(); if (firstElement != null) { currentDiagram.ActiveDiagramView.SelectModelElement(firstElement, true); } // if nothing got pasted (because it's already there), we succeeded in our paste but didn't really change // the display, so nothing further needs done if (!newShapes.Any()) { return(false); } t.Commit(); } //using (Transaction t = store.TransactionManager.BeginTransaction()) //{ // Commands.LayoutDiagram(currentDiagram, newShapes); // t.Commit(); //} currentDiagram.Invalidate(); return(true); } return(false); }
private void OnPostLoadModelAndDiagram( SerializationResult serializationResult, Partition modelPartition, string modelFileName, Partition diagramPartition, string diagramFileName, ModelRoot modelRoot, EFModelDiagram diagram) { Debug.Assert(modelPartition != null); Debug.Assert(modelPartition.Store != null); // Tracking properties need to be set up according to whether the serialization matches the calculated values. ResetTrackingProperties(modelPartition.Store); }
private void OnMenuLayoutDiagram(object sender, EventArgs e) { EFModelDiagram diagram = CurrentSelection.Cast <EFModelDiagram>().FirstOrDefault(); if (diagram != null) { using (Transaction tx = diagram.Store.TransactionManager.BeginTransaction("ModelAutoLayout")) { diagram.AutoLayoutShapeElements(diagram.NestedChildShapes, Microsoft.VisualStudio.Modeling.Diagrams.GraphObject.VGRoutingStyle.VGRouteStraight, Microsoft.VisualStudio.Modeling.Diagrams.GraphObject.PlacementValueStyle.VGPlaceSN, false); tx.Commit(); } } }
/// <summary> /// public virtual method for the client to have his own user-defined delete rule class /// </summary> /// <param name="e"></param> public override void ElementDeleted(ElementDeletedEventArgs e) { base.ElementDeleted(e); ModelDiagramData element = (ModelDiagramData)e.ModelElement; Store store = element.Store; Transaction current = store.TransactionManager.CurrentTransaction; if (current.IsSerializing || ModelRoot.BatchUpdating) { return; } EFModelDiagram diagram = element.GetDiagram(); ModelDiagramData.CloseDiagram?.Invoke(diagram); diagram.Delete(); }
private void ObjectModelBrowser_OnNodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e) { if (e.Node is ExplorerTreeNode elementNode && elementNode.RepresentedElement != null) { ModelElement element = elementNode.RepresentedElement; if (ModelingDocData is EFModelDocData docData) { Diagram diagram = docData.CurrentDocView?.CurrentDiagram; if (diagram != null && diagram is EFModelDiagram efModelDiagram && !element.LocateInActiveDiagram(true)) { EFModelDiagram.AddExistingModelElement(efModelDiagram, element); } } } }
private static void DoStandardLayout(List <BinaryLinkShape> linkShapes, EFModelDiagram diagram) { // first we need to mark all the connectors as dirty so they'll route. Easiest way is to flip their 'ManuallyRouted' flag foreach (BinaryLinkShape linkShape in linkShapes) { linkShape.ManuallyRouted = !linkShape.ManuallyRouted; } // now let the layout mechanism route the connectors by setting 'ManuallyRouted' to false, regardless of what it was before foreach (BinaryLinkShape linkShape in linkShapes) { linkShape.ManuallyRouted = false; } diagram.AutoLayoutShapeElements(diagram.NestedChildShapes.Where(s => s.IsVisible).ToList(), VGRoutingStyle.VGRouteStraight, PlacementValueStyle.VGPlaceSN, true); }
public void SetDiagram(EFModelDiagram d) { diagram = d; }
public virtual ModelRoot LoadModelAndDiagrams(DslModeling::SerializationResult serializationResult, DslModeling::Partition modelPartition, string modelFileName, DslModeling::Partition diagramsPartition, string diagramsFileName, DslModeling::ISchemaResolver schemaResolver, DslValidation::ValidationController validationController, DslModeling::ISerializerLocator serializerLocator) { #region Check Parameters if (serializationResult == null) { throw new global::System.ArgumentNullException("serializationResult"); } if (modelPartition == null) { throw new global::System.ArgumentNullException("modelPartition"); } if (diagramsPartition == null) { throw new global::System.ArgumentNullException("diagramsPartition"); } if (string.IsNullOrEmpty(diagramsFileName)) { throw new global::System.ArgumentNullException("diagramsFileName"); } #endregion ModelRoot modelRoot; // Ensure there is an outer transaction spanning both model and diagram load, so moniker resolution works properly. if (!diagramsPartition.Store.TransactionActive) { throw new global::System.InvalidOperationException(EFModelDomainModel.SingletonResourceManager.GetString("MissingTransaction")); } modelRoot = this.LoadModel(serializationResult, modelPartition, modelFileName, schemaResolver, validationController, serializerLocator); ModelDiagramData data = null; if (serializationResult.Failed) { // don't try to deserialize diagram data if model load failed. return(modelRoot); } if (IsValid(diagramsFileName)) { using (var pkgOutputDoc = global::System.IO.Packaging.Package.Open(diagramsFileName, global::System.IO.FileMode.Open, global::System.IO.FileAccess.Read)) { foreach (var packagePart in pkgOutputDoc.GetParts()) { EFModelDiagram diagram = this.LoadDiagram(serializationResult , modelPartition , modelFileName , diagramsFileName , modelRoot , diagramsPartition , packagePart.GetStream(global::System.IO.FileMode.Open, global::System.IO.FileAccess.Read) , schemaResolver , validationController , serializerLocator) as EFModelDiagram; if (diagram != null) { data = FixupDiagramData(diagram); } if (diagram.Name == Path.GetFileNameWithoutExtension(diagramsFileName).Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries).First()) { data.SetLocks(Locks.All); } } } } else { // missing diagram file indicates we should create a new diagram. EFModelDiagram diagram = this.LoadDiagram(serializationResult , modelPartition , modelFileName , diagramsFileName , modelRoot , diagramsPartition , global::System.IO.Stream.Null , schemaResolver , validationController , serializerLocator) as EFModelDiagram; if (diagram != null) { data = FixupDiagramData(diagram); } if (diagram.Name == Path.GetFileNameWithoutExtension(diagramsFileName)) { data.SetLocks(Locks.All); } } modelRoot.Store .GetAll <ModelDiagramData>() .Where(d => d.GetDiagram() == null) .ToList() .ForEach(d => modelRoot.Diagrams.Remove(d)); return(modelRoot); ModelDiagramData FixupDiagramData(EFModelDiagram diagram) { ModelDiagramData diagramData = modelRoot.Store .GetAll <ModelDiagramData>() .FirstOrDefault(d => d.Name == diagram.Name); if (diagramData == null) { modelRoot.Diagrams.Add(diagramData = new ModelDiagramData(modelRoot.Store.DefaultPartition , new DslModeling.PropertyAssignment(ModelDiagramData.NameDomainPropertyId, diagram.Name))); } diagramData.SetDiagram(diagram); return(diagramData); } }
// ReSharper disable once UnusedParameter.Local private static void DoGraphvizLayout(List <DotNode> vertices, List <DotEdge> edges, EFModelDiagram diagram) { // set up to be a bidirectional graph with the edges we found BidirectionalGraph <DotNode, DotEdge> graph = edges.ToBidirectionalGraph <DotNode, DotEdge>(); // add all the vertices that aren't connected by edges graph.AddVertexRange(vertices.Except(edges.Select(e => e.Source).Union(edges.Select(e => e.Target)))); // we'll process as Graphviz GraphvizAlgorithm <DotNode, DotEdge> graphviz = new GraphvizAlgorithm <DotNode, DotEdge>(graph); graphviz.GraphFormat.NodeSeparation = 1.0; graphviz.GraphFormat.Splines = GraphvizSplineType.Ortho; graphviz.CommonVertexFormat.Shape = GraphvizVertexShape.Record; // labels will be the Id of the underlying Shape graphviz.FormatVertex += (sender, args) => { args.VertexFormat.Label = args.Vertex.Shape.ModelElement is ModelClass modelClass ? modelClass.Name : args.Vertex.Shape.ModelElement is ModelEnum modelEnum ? modelEnum.Name : args.Vertex.Shape.ModelElement.Id.ToString(); args.VertexFormat.FixedSize = true; args.VertexFormat.Size = new GraphvizSizeF((float)args.Vertex.Shape.Size.Width, (float)args.Vertex.Shape.Size.Height); args.VertexFormat.Label = args.Vertex.Shape.Id.ToString(); }; graphviz.FormatEdge += (sender, args) => { args.EdgeFormat.Label.Value = args.Edge.Shape.Id.ToString(); }; // generate the commands string dotCommands = graphviz.Generate(new DotEngine(), Path.Combine(Path.GetTempPath(), Path.GetTempFileName())); Debug.WriteLine(dotCommands); ProcessStartInfo dotStartInfo = new ProcessStartInfo(EFModelPackage.Options.DotExePath, "-T plain") { RedirectStandardInput = true, RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true }; string graphOutput; using (Process dotProcess = Process.Start(dotStartInfo)) { // stdin is redirected to our stream, so pump the commands in through that dotProcess.StandardInput.WriteLine(dotCommands); // closing the stream starts the process dotProcess.StandardInput.Close(); // stdout is redirected too, so capture the output graphOutput = dotProcess.StandardOutput.ReadToEnd(); dotProcess.WaitForExit(); } Debug.WriteLine(graphOutput); // break it up into lines of text for processing string[] outputLines = graphOutput.Split(new [] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); SizeD graphSize = SizeD.Empty; // ReSharper disable once LoopCanBePartlyConvertedToQuery foreach (string outputLine in outputLines) { // spaces aren't valid in any of the data, so we can treat them as delimiters string[] parts = outputLine.Split(' '); string id; double x, y; // graphviz coordinates have 0,0 at the bottom left, positive means moving up // our coordinates have 0,0 at the top left, positive means moving down // so we need to transform them switch (parts[0]) { case "graph": // 0 1 2 3 // graph 1 109.38 92.681 graphSize = new SizeD(double.Parse(parts[2]), double.Parse(parts[3])); break; case "node": // 0 1 2 3 4 5 6 7 // node 71 78.514 93.639 1.5 3.3056 "0f651fe7-da0f-453f-a08a-ec1d31ec0e71" solid record black lightgrey id = parts[6].Trim('"'); DotNode dotNode = vertices.Single(v => v.Shape.Id.ToString() == id); // label x = double.Parse(parts[2]); y = graphSize.Height - double.Parse(parts[3]); dotNode.Shape.Bounds = new RectangleD(x, y, dotNode.Shape.Size.Width, dotNode.Shape.Size.Height); break; case "edge": // 0 1 2 3 4 5 6 7 8 9 10 11 12 // edge 6 18 4 34.926 77.518 34.926 77.518 34.926 75.88 34.926 75.88 "567b5db7-7591-4aa7-845c-76635bf56f28" 36.083 77.16 solid black id = parts[4 + int.Parse(parts[3]) * 2].Trim('"'); DotEdge edge = edges.Single(e => e.Shape.Id.ToString() == id); // need to mark the connector as dirty. this is the easiest way to do this BinaryLinkShape linkShape = edge.Shape; linkShape.ManuallyRouted = !linkShape.ManuallyRouted; linkShape.FixedFrom = VGFixedCode.NotFixed; linkShape.FixedTo = VGFixedCode.NotFixed; // make the labels follow the lines foreach (LineLabelShape lineLabelShape in linkShape.RelativeChildShapes.OfType <LineLabelShape>()) { lineLabelShape.ManuallySized = false; lineLabelShape.ManuallyPlaced = false; } linkShape.EdgePoints.Clear(); int pointCount = int.Parse(parts[3]); for (int index = 4; index < 4 + pointCount * 2; index += 2) { x = double.Parse(parts[index]); y = graphSize.Height - double.Parse(parts[index + 1]); linkShape.EdgePoints.Add(new EdgePoint(x, y, VGPointType.Normal)); } // since we're not changing the nodes this edge connects, this really doesn't do much. // what it DOES do, however, is call ConnectEdgeToNodes, which is an internal method we'd otherwise // be unable to access linkShape.Connect(linkShape.FromShape, linkShape.ToShape); linkShape.ManuallyRouted = false; break; } } }