Substituting standard PlanarShapeCreationTool and SelectionTool with custom ones

Oct 25, 2012 at 8:12 AM
Edited Oct 25, 2012 at 10:24 AM

Hi,

Here You recommended to use Tool classes to detect "movement end position". So I created 2 classes - MySelectionTool and MyPlanarShapeCreationTool. In these classes I override the EndToolAction() method: read value of "this.ActionDiagramPresenter.SelectedShapes" property and so on.

However, I am not sure that I correctly added my tools to the toolbox:

private void CreateTools()
{
    //foreach(var tool in this.toolSetController1.Tools)
    //{
    //    Console.WriteLine("tool.Title = {0}", tool.Title);
    //}
    this.toolSetController1.Clear();
    this.toolSetController1.AddTool(new MySelectionTool(), true);
    foreach (ShapeType shapeType in this.project1.ShapeTypes)
    {
        if (shapeType.FullName == "Core.ShapeGroup")
            continue; // don't know what is it but I have to skip it :)
        using (Shape shape = shapeType.CreateInstance(this.project1.Repository.GetTemplate(shapeType.Name)))
        {
            if (shape is IPlanarShape)
            {
                this.toolSetController1.AddTool(
                    new MyPlanarShapeCreationTool(
                        shape.Template, shapeType.DefaultCategoryTitle), false);
            }
            else if (shape is ILinearShape)
            {
                this.toolSetController1.AddTool(
                    new LinearShapeCreationTool(
                        shape.Template, shapeType.DefaultCategoryTitle), false);
            }
            else
            {
                ; // seems we never get here
            }
        }
    }
    //Console.WriteLine("================================");
    //foreach (var tool in this.toolSetController1.Tools)
    //{
    //    Console.WriteLine("tool.Title = {0}", tool.Title);
    //}
}

Well, it seems that everything works - the "foreach" loops print the same (if uncommented).

But anyway I would like to know Your opinion.

Thank You.

Coordinator
Oct 26, 2012 at 7:59 AM

Your code is 100% correct.

Two little suggestions:

        if (shapeType.FullName == "Core.ShapeGroup")
            continue; // don't know what is it but I have to skip it :)

You have to skip it because the GroupShape does not support Templates (you cannot create a Template fro a group). Instead of checking the FullName, you can check the "SupportsAutoTemplates" (the name is a bit misleading).

For creating the TemplateTools, you can use the ToolSetController.CreateTemplateTool() methods, then you get rid of the if/else block.

Oct 26, 2012 at 8:11 AM

Thank You! I will try both suggestions today.

One more question: is it OK that I have to create temporary shape for each shape type? and is it always correct to use shapeType.Name as the parameter for the Repository.GetTemplate() method?

Coordinator
Oct 26, 2012 at 8:40 AM
Edited Oct 26, 2012 at 8:41 AM

Yes, that's ok. Creating shapes is not really expensive as they create most of their member objects on demand and not every time the constructor is called.

The alternatives are:

    foreach (Template t in project1.Repository.GetTemplates())
        toolSetController1.AddTemplateTool(t, t.Shape.ShapeType.DefaultCategoryTitle);

or

    toolSetController1.CreateTemplateTool(project1.Repository.GetTemplate("myShape"), categoryTitle);


Using ShapeType.Name as parameter of the Repository.GetTemplate method is ok as long as your user cannot change the template's name. Otherwise, better iterate through the templates.
By default, the template's name equals the shape type's name but you can change this via code and with the template editor dialog (right-click on the tool -> "Edit Template").

Oct 26, 2012 at 9:17 AM
KurtHolzinger wrote:

    foreach (Template t in project1.Repository.GetTemplates()) 

        toolSetController1.AddTemplateTool(t, t.Shape.ShapeType.DefaultCategoryTitle);

With this approach, how will I be able to use MyPlanarShapeCreationTool instead of the standard PlanarShapeCreationTool?

Coordinator
Oct 26, 2012 at 11:08 AM
Edited Oct 26, 2012 at 11:08 AM

Sorry, my fault - I should read more carefully. I overlooked the "My" in "MyPlanarShapeCreationTool".
New try:

    foreach (Template t in this.project1.Repository.GetTemplates())
    {
        // Skip shapes that do not support templates
if (!t.SupportsAutoTemplates)
continue
;
if (t.Shape is IPlanarShape) {
// Create custom Tool for all planar shapes this.toolSetController1.AddTool( new MyPlanarShapeCreationTool( t, t.Shape.ShapeType.DefaultCategoryTitle), false); } else {
// Create default tools for all other shapes this.toolSetController1.CreateTemplateTool(t, t.Shape.ShapeType.DefaultCategoryTitle);
} }
Oct 26, 2012 at 11:15 AM

Now it's clear, thank You!

Corrected code (there were 3 misprints):

foreach (Template t in this.project1.Repository.GetTemplates())
{
    // Skip shapes that do not support templates
    if (!t.Shape.Type.SupportsAutoTemplates)
        continue;

    if (t.Shape is IPlanarShape)
    {
        // Create custom Tool for all planar shapes
        this.toolSetController1.AddTool(
            new MyPlanarShapeCreationTool(
                t, t.Shape.Type.DefaultCategoryTitle), false);
    }
    else
    {
        // Create default tools for all other shapes
        this.toolSetController1.CreateTemplateTool(t, t.Shape.Type.DefaultCategoryTitle);
    }
}

Works like a charm!

Oct 26, 2012 at 2:00 PM

Hmm... one problem.

Code of my custom SelectionTool looks like this:

protected override void EndToolAction()
{
    int action = this.CurrentToolAction.Action;
    base.EndToolAction();
    if (action == 4)
    {
        /*
         * Shape(s) movement has finished; have to use integer (4) instead of SelectionTool.Action.MoveShape
         * because SelectionTool.Action enum is PRIVATE :(( this may be fixed in future releases.
         */
        ShapeModelBinder smb = new ShapeModelBinder(this.ActionDiagramPresenter);
        foreach (Shape shape in this.ActionDiagramPresenter.SelectedShapes)
        {
            smb.UpdateBindings(shape);
        }
    }
}

 - all my code goes after base.EndToolAction() is invoked; everything works fine.

Now code of my custom PlanarShapeCreationTool:

protected override void EndToolAction()
{
    base.EndToolAction();
    ShapeModelBinder smb = new ShapeModelBinder(this.ActionDiagramPresenter);
    smb.UpdateBindings(this.ActionDiagramPresenter.SelectedShapes.First());
}

 - here I placed my code also after base.EndToolAction(), but its first line gave me the following error: "The action's current display was not set yet. Call StartToolAction method to set the action's current display" (in Dataweb.NShape.Tool.get_ActionDiagramPresenter() in C:\NShape\Source\Core\Tool.cs: line 351). The only way to avoid this error is to place my code before base.EndToolAction():

protected override void EndToolAction()
{
    ShapeModelBinder smb = new ShapeModelBinder(this.ActionDiagramPresenter);
    smb.UpdateBindings(this.ActionDiagramPresenter.SelectedShapes.First());
    base.EndToolAction();
}

Why there's such a difference between SelectionTool and PlanarShapeCreationTool behavior?

And is it OK to place my code before base.EndToolAction()? (this code modifies shape's model object and then updates project repository with this model object)

Coordinator
Oct 29, 2012 at 9:15 AM

Move your code before the call to base.EndToolAction().
I will update the documentation in order to make it clearer that code in overridden implemntations should be executed before the base implementation.