Monthly Archives: December 2014

Smart grips of custom object in MultiCAD.NET

smartGrips_withframe

When it comes to creating or modifying objects in a drawing, usability becomes a key characteristic for CAD software.  One of the essential features for efficient manipulating and modifying graphic objects is grips. Grips are markers in the key object points that allow the user to modify an object by mouse and without using the menu or command line.

While embedded CAD primitives are already supplied with grips, custom objects must implement this feature by themselves. The post is intended to describe how to create grips for custom objects using MultiCAD.NET.

The grip management mechanism in MultiCAD.NET permits the user to work with grips of simple and smart types. We have already talked about simple grips in the previous post and in this post will discuss how to work with smart grips. Unlike simple grips, smart grips can have different shapes (circle, triangle, rhombus etc), change specific object parameters, display pop-up menu or perform a set of actions defined in the handler. The smart grips API uses a new unified approach that makes it possible to create simple grips as well.

McSmartGrip class

For representing smart grips in the MultiCAD.NET API the McSmartGrip<T> class is used. This class contains different constructor signatures and handlers of events that are generated by user actions:

  • MoveGrip — used when a grip has been moved,
  • MouseMove — used when a grip of interactive type has been moved,
  • GetContextMenu — used when a grip of the GripType.PopupMenu type has been clicked,
  • OnCommand — used when a grip of the GripType.Button type has been clicked or when a context menu item has been chosen.

For example, to create a simple grip that moves the object, the following constructor can be used:

This constructor takes a MoveGrip delegate as a parameter. In this example we have used a lambda expression (http://msdn.microsoft.com/en-us/library/bb397687.aspx) to implement MoveGrip that just moves the object position by the offset value.

Adding grips

For adding grips to a custom object, the AppendGrip()of the GripPointsInfo class is used:

The GripPointsInfo class is used as the parameter of the GetGripPoints() method which is used for getting grips whenever the object is drawn:

The following code creates and adds the same simple grip:

As you can see the procedure is very compact and requires only one code line. This was the simplest case of grips – simple grips. Let’s see what grip types are supported in the MultiCAD.NET API and discuss specifics of each of them.

Grip types

With MulitiCAD.NET you are able to create grips of the following types:

  • Simple — simple grip. Grip movements are handled in MoveGrip.
  • PopupMenu — grip that displays a popup menu. Handling is performed by OnCommand.
  • Button — grip that represents a button. Actions that can be called by pressing the button are defined in OnCommand.
  • Interactive — the grip type that is similar to Simple, but can be used with object snaps. Unlike Simple grip, Interactive grip uses MouseMove for handling grip movements.

Grip Appearance

One custom object can have grips of different types, in order to distinguish all of them, you can set the specific appearance to each one defining grip shape and color.
Variety of grip shapes are defined by the McBaseGrip.GripAppearance enumeration. Here are some  available shapes:

gripTypes

You can also set the desired grip color from the color set defined in the GripColors class or using standard  System.Drawing.Color.
Let’s look how it works on practice and create several  smart grips of different types for the TextInBox primitive that was described in the previous post.

Simple grip

We have already mentioned this grip type above, and as follows from the name it is used for simple action such as moving points of the object. The code below adds a simple grip for moving a corner point of the entity:

The following picture demonstrates how this grip works:

01

Button grip

Now we will add a button grip that will control displaying the frame around the text. To create the button, we use the constructor signature that takes the grip type (McBaseGrip.GripType.Button) and the color (GripColors.Base ) of the grip as parameters.  If the button is pressed, the handler OnCommand will change the state of the _show_frame indicator to opposite and the button appearance will be changed from the “On” state to “Off” or vice versa. We will set the “On” appearance by default.

Also a registered command can be called as a reaction to button pressing:

02

Menu grip

One more grip type that can be created in MultiCAD.NET is a grip for displaying a context menu. This type is used for example, when the user needs to choose a desired value from the list of object parameters. A menu grip can be created by the grip constructor with passing the McBaseGrip.GripType.PopupMenu grip type and the McBaseGrip.GripAppearance.PopupMenu grip appearance as parameters. To enable the menu, the following delegates should be implemented:

  • GetContextMenu — displays the menu if the grip is clicked,
  • OnCommand — handles selecting menu items.

The following code creates a menu grip that enables a context menu with one item:

03

Interactive grip

And the last grip type in our overview is an interactive grip. The main specific of this grip is that it can use the information about the objects which the grip is snapped to and change its behavior depending on this information.

For example, we will add one interactive grip that can replace the text of our TextInBox primitive with the name of a selected object. Depending on the object type, the grip will change its color: default color will be GripColors.Base, if the grip snaps to the parent object the color will be changed to Color.Red, and if it snaps to any other object that supports snap the color will become Color.Green.

An interactive grip can be created using a grip constructor with passing the McBaseGrip.GripType.Interactive grip type. Handling of grip movement is implemented in the  MouseMove delegate (unlike simple grip, that uses MoveGrip).

Below is the illustration of how this grip works, the «Text field» string is replaced with «MultiCAD.Samples.TextInBox»:

04

So, we have overviewed the basic types of smart grips, that allow development of an efficient and usable interface for working with custom objects created in MultiCAD.NET. The API documentation set is delivered with the nanoCAD SDK which is available in the nanoCAD Developer’s Club (registration is required).

Managing serialization of custom objects in MultiCAD.NET

serialization

In the previous post we discussed the approach used for serializing custom objects in the MultiCAD.NET API. We talked about concepts of using that approach for providing object version compatibility and considered the simplest case when a new version of an object is derived from a previous one by adding additional fields of data. Today we are going to discuss the case of deep changes of the object’s structure such as removing, renaming  or retyping fields.

Assume that a new version of the object has renamed some fields and changed their types. Let’s take, for example, the CrossMark object that is already familiar to us from the previous post:

CrossMark entity

CrossMark entity

The object of the previous version had the following structure:

Let’s assume that  in the new version the corner points are required to be defined by vectors (instead of 3d-points), and the radius field remains unchanged:

Obviously objects of the new version will not be compatible with older objects after such changes.

In order to allow the new version to be able to recognize the older ones, it is required to implement the mechanism for reading desired fields and converting the old data format to the new one. To solve this problem with MultiCAD.NET, the standard ISerializable interface can be used .

The interface allows an object to control its own serialization and deserialization and requires an implementation of two methods:

  • public void GetObjectData(SerializationInfo info, StreamingContext context) — used to serialize the target object,
  • public CrossMark(SerializationInfo info, StreamingContext ctx) — the special constructor used to deserialize values.

In our case the methods should be as follows:

 

So, now the object has two constructors: one (the default) is used for creating and inserting the object into the drawing, and another – when the object is being read (deserialized) from the drawing.
The deserialization process has been divided into two parts: data from the object of the current version is read regularly, while the data of the previous version is read and converted to the current format inside the catch block for handling SerializationException. This exception will be thrown when the application tries to deserialize the nonexistent fields from the previous version.
If you plan to continue developing new versions of the object, then you can also serialize the version number to a new special field and customize the deserialization process depending on this value:

So we’ve discussed the basic cases of custom object serialization with different types of changes in their structure while moving from one version to another. Using the described approaches, you are able to provide your application with a flexible mechanism for managing objects version compatibility.
Serialization questions are very popular, that is why we will soon continue talking about the implementation of this mechanism in MultiCAD.NET and publish some posts regarding the subject. For example, we will discuss the problem of object data exchange between applications by serializing data to external databases and answer other frequently asked questions.