Cannot delete shape

Jun 8, 2013 at 2:03 PM
Hi,

I have a problem when deleting shape with GenericModelObject instance from display in Designer.

Steps to reproduce:
  1. Attach a GenericModelObject instance to a shape when the shape inserted in display. Please see following codes.
         private void display1_ShapesInserted(object sender, Dataweb.NShape.Controllers.DiagramPresenterShapesEventArgs e)
        {
            IReadOnlyCollection<Shape> shapes = e.Shapes;
            foreach (Object item in shapes)
            {
                if (item is ILinearShape || item is TextBase || item is PictureBase)
                {
                    continue;
                }
                this.SetModelObject(shapes, item as Shape);
                break;
            }
        }

        private void SetModelObject(IReadOnlyCollection<Shape> shapes, Shape shape)
        {
            if (shape.ModelObject == null)
            {
                GenericModelObject modelObj = (GenericModelObject)this.project1.ModelObjectTypes["GenericModelObject"].CreateInstance();
                shape.ModelObject = modelObj;
                project1.Repository.Insert(modelObj);
                project1.Repository.Update();
            }
        }
  1. Drag 2 Boxes from tool set into Display;
  2. Drage a Polyline from tool set into Display and connect the 2 boxes with it.
  3. Delete one box;
  4. Undo;
  5. Delete the box again. An error message pop up “Connection already deleted from repository”
Best,

Michael
Coordinator
Jun 12, 2013 at 9:04 AM
Hello Michael,

I'm sorry, I cannot reproduce this issue. I created a small demo program using your code and I tried it also using the NShape Designer.
  • Are you sure that there is no other code in your application that interferes and causes the issue?
  • Is attaching the model objects necessary for reporoducing the issue?
  • If it is:
    • Which model objects do you use?
    • Which terminal mappings did you set?
    • Which Points do you connect with the line shape?
Could you please provide me a small demo program that demonstrates the issue?
Jul 4, 2013 at 7:20 AM
Thanks for your reply.

I did some tests and found that:
The error occured when I was using the DLLs compiled from NShape source code.
The error will disapear if use the DLLS you published.

The source code in package NShape_2.0.3.msi is not the latest version? Where can I get the latest one?

Thanks.
Coordinator
Jul 8, 2013 at 12:51 PM
Edited Jul 8, 2013 at 3:42 PM
NShape 2.0.3 is the lastest release version.
We are curently working on 2.0.4 but there is no release date yet.

Michael2000 wrote:
The error occured when I was using the DLLs compiled from NShape source code.
The error will disapear if use the DLLS you published.
That is because the assemblies installed to "Program Files" are compiled in Release mode. This means that not all consistency checks are performed - and that's what this issue is about:
The repository consistency check throwing the exception you ran into will not be executed in release mode (or if you do not specify the compiler option "REPOSITORY_CHECK").

Having the above in mind I already created my demo using the debug assemblies. I also used the debug version of the NShapeDesigner.
But unfortunately, I cannot reproduce the issue.

So I would like to ask you again whether you can reproduce this issue with (a self-compiled debug version of) the NShapeDesigner?
If not: Could you please provide me a small demo that shows the issue?
Jul 22, 2013 at 4:57 AM
I can reproduce the issue with (a self-compiled debug version of) the NShapeDesigner.

Here is a small demo for you.
using System;
using System.IO;
using System.Windows.Forms;
using Dataweb.NShape;
using Dataweb.NShape.Advanced;
using Dataweb.NShape.GeneralShapes;

namespace Demo4NShape
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.CreateNewProject();
        }

        private System.ComponentModel.IContainer components = null;
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code
        #endregion

        private Dataweb.NShape.WinFormsUI.Display display1;
        private Dataweb.NShape.Controllers.ToolSetController toolSetController1;
        private Dataweb.NShape.Project project1;
        private Dataweb.NShape.Controllers.DiagramSetController diagramSetController1;
        private System.Windows.Forms.ListView listView1;
        private Dataweb.NShape.Advanced.CachedRepository cachedRepository1;
        private Dataweb.NShape.XmlStore xmlStore1;
        private Dataweb.NShape.WinFormsUI.ToolSetListViewPresenter toolSetListViewPresenter1;

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
            Dataweb.NShape.RoleBasedSecurityManager roleBasedSecurityManager1 = new Dataweb.NShape.RoleBasedSecurityManager();
            this.display1 = new Dataweb.NShape.WinFormsUI.Display();
            this.diagramSetController1 = new Dataweb.NShape.Controllers.DiagramSetController();
            this.project1 = new Dataweb.NShape.Project(this.components);
            this.cachedRepository1 = new Dataweb.NShape.Advanced.CachedRepository();
            this.xmlStore1 = new Dataweb.NShape.XmlStore();
            this.toolSetController1 = new Dataweb.NShape.Controllers.ToolSetController();
            this.listView1 = new System.Windows.Forms.ListView();
            this.toolSetListViewPresenter1 = new Dataweb.NShape.WinFormsUI.ToolSetListViewPresenter(this.components);
            this.SuspendLayout();
            // 
            // display1
            // 
            this.display1.AllowDrop = true;
            this.display1.BackColorGradient = System.Drawing.SystemColors.Control;
            this.display1.DiagramSetController = this.diagramSetController1;
            this.display1.GridColor = System.Drawing.Color.Gainsboro;
            this.display1.GridSize = 19;
            this.display1.ImeMode = System.Windows.Forms.ImeMode.NoControl;
            this.display1.Location = new System.Drawing.Point(257, 12);
            this.display1.Name = "display1";
            this.display1.PropertyController = null;
            this.display1.SelectionHilightColor = System.Drawing.Color.Firebrick;
            this.display1.SelectionInactiveColor = System.Drawing.Color.Gray;
            this.display1.SelectionInteriorColor = System.Drawing.Color.WhiteSmoke;
            this.display1.SelectionNormalColor = System.Drawing.Color.DarkGreen;
            this.display1.Size = new System.Drawing.Size(537, 419);
            this.display1.TabIndex = 0;
            this.display1.ToolPreviewBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(119)))), ((int)(((byte)(136)))), ((int)(((byte)(153)))));
            this.display1.ToolPreviewColor = System.Drawing.Color.FromArgb(((int)(((byte)(96)))), ((int)(((byte)(70)))), ((int)(((byte)(130)))), ((int)(((byte)(180)))));
            this.display1.ShapesInserted += new System.EventHandler<Dataweb.NShape.Controllers.DiagramPresenterShapesEventArgs>(this.display1_ShapesInserted);
            // 
            // diagramSetController1
            // 
            this.diagramSetController1.ActiveTool = null;
            this.diagramSetController1.Project = this.project1;
            // 
            // project1
            // 
            this.project1.AutoGenerateTemplates = false;
            this.project1.AutoLoadLibraries = true;
            this.project1.LibrarySearchPaths = ((System.Collections.Generic.IList<string>)(resources.GetObject("project1.LibrarySearchPaths")));
            this.project1.Name = null;
            this.project1.Repository = this.cachedRepository1;
            roleBasedSecurityManager1.CurrentRole = Dataweb.NShape.StandardRole.Administrator;
            roleBasedSecurityManager1.CurrentRoleName = "Administrator";
            this.project1.SecurityManager = roleBasedSecurityManager1;
            // 
            // cachedRepository1
            // 
            this.cachedRepository1.ProjectName = null;
            this.cachedRepository1.Store = this.xmlStore1;
            this.cachedRepository1.Version = 0;
            // 
            // xmlStore1
            // 
            this.xmlStore1.DesignFileName = "";
            this.xmlStore1.DirectoryName = "";
            this.xmlStore1.FileExtension = ".xml";
            this.xmlStore1.ProjectName = "";
            // 
            // toolSetController1
            // 
            this.toolSetController1.DiagramSetController = this.diagramSetController1;
            // 
            // listView1
            // 
            this.listView1.FullRowSelect = true;
            this.listView1.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None;
            this.listView1.HideSelection = false;
            this.listView1.Location = new System.Drawing.Point(12, 12);
            this.listView1.MultiSelect = false;
            this.listView1.Name = "listView1";
            this.listView1.ShowItemToolTips = true;
            this.listView1.Size = new System.Drawing.Size(239, 306);
            this.listView1.TabIndex = 1;
            this.listView1.UseCompatibleStateImageBehavior = false;
            // 
            // toolSetListViewPresenter1
            // 
            this.toolSetListViewPresenter1.HideDeniedMenuItems = false;
            this.toolSetListViewPresenter1.ListView = this.listView1;
            this.toolSetListViewPresenter1.ShowDefaultContextMenu = true;
            this.toolSetListViewPresenter1.ToolSetController = this.toolSetController1;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(812, 447);
            this.Controls.Add(this.listView1);
            this.Controls.Add(this.display1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);

        }

        private void CreateNewProject()
        {
            this.xmlStore1.DirectoryName = string.Empty;
            this.project1.Name = "new project";
            this.project1.Create();

            Diagram diagram = new Diagram("Diagram 1");
            this.cachedRepository1.InsertAll(diagram);
            this.display1.Diagram = diagram;

            project1.AddLibraryByFilePath(Path.Combine(Application.StartupPath, "Dataweb.NShape.GeneralShapes.dll"), false);
            project1.AddLibrary(GetType().Assembly, false);

            project1.LibrarySearchPaths.Add(Application.StartupPath);
            RoundedBox RoundedBox = (RoundedBox)project1.ShapeTypes["RoundedBox"].CreateInstance();
            RoundedBox.SecurityDomainName = 'B';
            project1.Repository.Insert(new Template("RoundedBox", RoundedBox));

            Polyline Polyline = (Polyline)project1.ShapeTypes["Polyline"].CreateInstance();
            Polyline.SecurityDomainName = 'B';
            project1.Repository.Insert(new Template("Polyline", Polyline));
        }

        private void display1_ShapesInserted(object sender, Dataweb.NShape.Controllers.DiagramPresenterShapesEventArgs e)
        {
            IReadOnlyCollection<Shape> shapes = e.Shapes;
            foreach (Object item in shapes)
            {
                if (item is RoundedBox)
                {
                    GenericModelObject modelObj = (GenericModelObject)this.project1.ModelObjectTypes["GenericModelObject"].CreateInstance();
                    modelObj.AttachShape(item as Shape);
                    project1.Repository.Insert(modelObj);
                    project1.Repository.Update();
                }
            }
        }
    }
    public static class NShapeLibraryInitializer
    {
        public static void Initialize(IRegistrar registrar)
        {
            registrar.RegisterLibrary("GeneralShapes", 1);
        }
    }
}
Coordinator
Jul 22, 2013 at 10:18 AM
Edited Jul 22, 2013 at 10:20 AM
Thanks for the demo, I can reproduce the issue now.

You found a bug that was hereby fixed. The fix will be available with the next release.
In case you need it urgently, please contact us via email (support(at)dataweb(dot)de), I will send you the fixed source code file then (contains other fixes as well).

Unfortunately, the length of discussion postings is limited to 10000 characters and the code of the fixed class has more than 10000 characters so I cannot post the whole class here. So here is a shortened version:
/// <summary>
/// Base class for inserting and removing shapes along with their model objects to a diagram and a cache
/// </summary>
public abstract class InsertOrRemoveShapeCommand : AutoDisconnectShapesCommand {
(...)
    /// <ToBeCompleted></ToBeCompleted>
    protected void DeleteShapesAndModels() {
        if (Shapes.Count == 0) throw new NShapeInternalException("No shapes set. Call SetShapes() before.");
        // Disconnect shapes as long as the model objects still exist.
        Disconnect(Shapes);
        if (Repository != null && ModelObjects != null && ModelObjects.Count > 0) {
            if (modelsAndObjects == null) {
                modelsAndObjects = new Dictionary<IModelObject, AttachedObjects>();
                foreach (IModelObject modelObject in ModelObjects)
                    modelsAndObjects.Add(modelObject, new AttachedObjects(modelObject, Repository));
            }
            foreach (KeyValuePair<IModelObject, AttachedObjects> item in modelsAndObjects)
                DetachAndDeleteObjects(item.Value, Repository);
            Repository.Delete(modelsAndObjects.Keys);
        }
        diagram.Shapes.RemoveRange(Shapes);
        if (Repository != null) Repository.DeleteAll(Shapes);
    }
(...)
    private void DoInsertShapes(bool useOriginalLayers, LayerIds activeLayers) {
        if (Shapes.Count == 0) throw new NShapeInternalException("No shapes set. Call SetShapes() before.");
        for (int i = Shapes.Count - 1; i >= 0; --i) {
            //Shapes[i].ZOrder = Repository.ObtainNewTopZOrder(diagram);
            diagram.Shapes.Add(Shapes[i]);
            diagram.AddShapeToLayers(Shapes[i], useOriginalLayers ? shapeLayers[i] : activeLayers);
        }
        if (Repository != null) {
            // Insert shapes
            Repository.UndeleteAll(GetEntities<Shape>(Shapes, CanUndeleteEntity), diagram);
            Repository.InsertAll(GetEntities<Shape>(Shapes, CanInsertEntity), diagram);
        }
        // connect all selectedShapes that were previously connected to the shape(s)
        Reconnect(Shapes);
    }


    private IEnumerable<TEntity> GetEntities<TEntity>(IEnumerable<TEntity> entities, Predicate<TEntity> predicate) where TEntity : IEntity {
        foreach (TEntity entity in entities)
            if (predicate(entity)) yield return entity;
    }


    private void DoInsertShapesAndModels(bool useOriginalLayers, LayerIds activeLayers) {
        // Insert model objects first
        if (Repository != null && ModelObjects != null && ModelObjects.Count > 0) {
            if (modelsAndObjects == null) {
                modelsAndObjects = new Dictionary<IModelObject, AttachedObjects>();
                foreach (IModelObject modelObject in ModelObjects)
                    modelsAndObjects.Add(modelObject, new AttachedObjects(modelObject, Repository));
            }
            if (CanUndeleteEntities(modelsAndObjects.Keys))
                Repository.Undelete(modelsAndObjects.Keys);
            else Repository.Insert(modelsAndObjects.Keys);
        }
        // Insert shapes afterwards
        if (useOriginalLayers) 
            DoInsertShapes(true, LayerIds.None);
        else 
            DoInsertShapes(false, activeLayers);
        // Attach model obejcts to shapes finally
        if (Repository != null && modelsAndObjects != null) {
            foreach (KeyValuePair<IModelObject, AttachedObjects> item in modelsAndObjects)
                InsertAndAttachObjects(item.Key, item.Value, Repository);
        }
    }
(...)
    ///// <ToBeCompleted></ToBeCompleted>
    //protected void InsertShapes(LayerIds activeLayers) {
    //    DoInsertShapes(false, activeLayers);
    //}


    ///// <ToBeCompleted></ToBeCompleted>
    //protected void InsertShapes() {
    //    DoInsertShapes(true, LayerIds.None);
    //}
(...)
}
Jul 22, 2013 at 11:15 AM
Thanks for your greate help!

Another issue I found in the demo that:
step 1. drag a RoundedBox to diagram;
step 2. right click on the shape -> click context menu Create Template.
An exception ocurred: "GenericModelObject 'Dataweb.NShape.Advanced.GenericModelObject' already exists in the repository."
Coordinator
Jul 22, 2013 at 12:03 PM
Thanks for the hint - you found another bug.
Here is the fixed code: (from Command.cs):
/// <ToBeCompleted></ToBeCompleted>
public class CreateTemplateCommand : TemplateCommand {

    /// <ToBeCompleted></ToBeCompleted>
    public CreateTemplateCommand(Template template)
        : this(null, template) {
    }


    /// <ToBeCompleted></ToBeCompleted>
    public CreateTemplateCommand(IRepository repository, Template template)
        : base(repository, template) {
        this.description = string.Format("Create new tempate '{0}' based on '{1}'", template.Title, template.Shape.Type.Name);
    }


    /// <ToBeCompleted></ToBeCompleted>
    public CreateTemplateCommand(string templateName, Shape baseShape)
        : this(null, templateName, baseShape) {
    }


    /// <ToBeCompleted></ToBeCompleted>
    public CreateTemplateCommand(IRepository repository, string templateName, Shape baseShape)
        : base(repository) {
        if (string.IsNullOrEmpty(templateName)) throw new ArgumentNullException("templateName");
        if (baseShape == null) throw new ArgumentNullException("baseShape");
        this.description = string.Format("Create new tempate '{0}' based on '{1}'", templateName, baseShape.Type.Name);
        this.templateName = templateName;
        this.baseShape = baseShape;
    }


    /// <override></override>
    public override void Execute() {
        if (template == null) {
            // Create a template shape and copy properties
            Shape templateShape = baseShape.Type.CreateInstance();
            foreach (Shape childShape in baseShape.Children.BottomUp)
                templateShape.Children.Add(childShape.Type.CreateInstance(), childShape.ZOrder);
            templateShape.CopyFrom(baseShape);
            // Clone model object if necessary
            if (baseShape.ModelObject != null) {
                IModelObject templateModelObject = baseShape.ModelObject.Clone();
                templateShape.ModelObject = templateModelObject;
            }
            // Create the new template
            template = new Template(templateName, templateShape);
        }
        if (Repository != null) {
            if (CanUndeleteEntity(template))
                Repository.UndeleteAll(template);
            else Repository.InsertAll(template);
        }
    }


    /// <override></override>
    public override void Revert() {
        if (Repository != null) {
            Repository.Delete(template.GetPropertyMappings());
            Repository.DeleteAll(template);
        }
    }


    #region Fields
    private string templateName;
    private Shape baseShape;
    #endregion
}
Jul 23, 2013 at 3:31 AM
Hi Kurt,

I can create template after applying the new command.cs. Thanks.
But there is a new problem that a shape created from the template cannot be connected by a Polyline.
The issue should be reproduced with the above demo.

Best,
Michael
Coordinator
Jul 23, 2013 at 7:59 AM
In your demo, you assign an IModelObject to every planar shape that is created.
After creating a template from one of these shapes (with model object), you have to assign connection point mappings (map the shape's connection points to the model object's terminals). Connection points without terminal mapping are deactivated (only if the template's shape has a model object).

This is because the framework assumes that connection points of shape have a certain meaning in case there is a model object attached to it.
Example:
When you have a symbol for a diode or a transistor, the connection points will have the meaning 'anode' and 'kathode' (and 'base' for the transformer). For the data model behind the scenes, it will be vital that to know which terminals (connection points of the model object) are connected.

In order to see what I am talking about, save your project and open it with the NShapeDesigner. Right-click the template you created and choose "Edit..." from the context menu.
There will be a tab "Connection Points" that is not shown if the template's shape has no model object.
Here, you can choose which terminal should be mapped to the connection points of the shape. All connection points without mapping are deactivated.
Coordinator
Jul 23, 2013 at 8:33 AM
I forgot to mention - you can create the mappings using the buildt in template editor or with code:
Template template = ...
template.UnmapAllTerminals();
...
template.MapTerminal(terminalId, connectionPointId);
...