MultiCAD.NET API: Saving non-graphic information in .dwg drawings

    image

    Every CAD application developer sooner or later faces the problem of storing auxiliary non-graphic information in a drawing. It can be attributes of individual graphic elements, attributes of individual sheets, or the settings of the entire drawing. Unlike block attributes, this information is not visible to the user and is used for software processing of drawings.

    Today, there are a number of traditional ways to solve the problem: adding XData to drawing elements, using XRecord, and creating your own non-graphic objects.

    Compared to traditional ones, the mechanism for creating and storing non-graphic information in the MultiCAD.NET API is much more compact and convenient to use. In addition, it is universal and can be equally applied to various types of data in a drawing: graphic elements, sheets, or the drawing itself. Various types of data may be used as additional information.

    How it works and how it is applied in practice, see under the cut.


    Adding custom properties to graphic objects


    To work with the object’s own data, a property is used CustomPropertiesthat is accessible to all descendants of the class McPropertySource, which, in particular, are all graphic primitives in the database (class instances McDbEntity). The property allows you to add and read data in the form of key-value pairs:

    entity.DbEntity.CustomProperties["Property"] = Value;
    

    Simple types can be used as property values, as well as arrays of simple types. In practice, you can add properties of any type using serialization of objects to write data to a byte array. The following example demonstrates an example of saving a data dictionary as a value of its own property MyCustomProperty:

    MemoryStream ms = new MemoryStream();
    BinaryFormatter formatter = new BinaryFormatter();
    Dictionary MyOptions = new Dictionary();
    MyOptions.Add("param1", 10);
    MyOptions.Add("param2", "Value");
    try
    {
      formatter.Serialize(ms, MyOptions);
    }
    catch (SerializationException e)
    {
      Console.WriteLine("Failed to serialize. Reason: " + e.Message);
    }
    Byte[] binary = ms.GetBuffer();
    entity.DbEntity.CustomProperties["MyCustomProperty"] = binary;
    

    Reading "complex" data also occurs in two stages: obtaining a byte array and further deserialization. CustomPropertiesalways returns the contents of arrays in the form of a List:

    List loadedBinary = entity.DbEntity.CustomProperties["MyCustomProperty "] as List;
    if (loadedBinary != null)
    {
      ms = new MemoryStream(loadedBinary.ToArray());
      formatter = new BinaryFormatter();
      MyOptions = formatter.Deserialize(ms) as Dictionary;
      int val = (int)MyOptions["param1"];
      String str = MyOptions["param2"] as String;
    } 
    

    Consider the application of this method with a specific example. Let the .dwg file contain a water supply scheme where hot and cold water pipelines are represented by polylines. Add to the individual polylines a description that will contain information about the purpose of the pipeline and the pipe diameter:

    [Pipe type] = Cold Water
    [Pipe diameter] = 20

    Register a command that will user-select a polyline object and add a key / value pair to it:

    [CommandMethod("WriteCustomProperties", CommandFlags.NoCheck | CommandFlags.NoPrefix)]
    static public void writeCold50()
    {
      McObjectId objId = McObjectManager.SelectObject("Select a polyline entity to write additional data to: ");
      if (objId.GetObject().IsKindOf(DbPolyline.TypeID))
      {
        McEntity ent = objId.GetObject();
        ent.DbEntity.CustomProperties["Pipe type"] = "Cold water";
        ent.DbEntity.CustomProperties["Pipe diameter"] = 50.0;        
      }
      else
      {
        MessageBox.Show("No polyline entity selected");
      }
    }
    

    Also, for all descendants of the class, McPropertySourceyou can get all available properties of a certain type (Custom, Object, User, etc.) using the method GetProperties(). The following command receives a list of all custom properties for the primitive selected in the drawing and displays their names on the screen.

    [CommandMethod("GetCustomProperties", CommandFlags.NoCheck | CommandFlags.NoPrefix)]
    static public void getCustomProperties()
      {
        McObjectId objId = McObjectManager.SelectObject("Select an entity to get its custom property list: ");
        if (objId.IsNull)
        {
          MessageBox.Show("No entity selected");
          return;
        }
        String propertyString = null;
        List customPropertyNames = new List();
        McEntity ent = objId.GetObject();
        customPropertyNames = ent.DbEntity.GetProperties(McProperties.PropertyType.Custom).GetProps();
        foreach (McProperty property in customPropertyNames)
        {
          propertyString = propertyString + property.Name + ";\n";
        }
       MessageBox.Show(propertyString, "Custom property list");
     }
    

    Saving non-graphic information for a document


    MultiCAD.NET allows you to store non-graphic information not only for primitives, but also for the entire document and individual sheets and blocks (subdocuments). The class McDocumentalso inherits functionality McPropertySource, and therefore, you can use the same property CustomPropertiesto set your own data for documents of various levels:

    [CommandMethod("WriteCustomPropertiesToDoc", CommandFlags.NoCheck | CommandFlags.NoPrefix)]
    static public void writeCustomPropertiesToDoc()
    {
      McDocument currentLayout = McDocumentsManager.GetActiveSheet();
      currentLayout.CustomProperties["Layout Property 1"] = "Value";
      McDocument currentDocument = McDocumentsManager.GetActiveDoc();
      currentDocument.CustomProperties["Document Property 1"] = "Value";
    }
    

    Let's see how this works on a concrete example. In one of our previous articles, we talked about creating custom primitives using MultiCAD.NET and dealt with the TextInBox primitive, which is a text in a rectangular frame:

    image

    Using the document’s own properties, you can set parameters and settings for this document. For example, in this case, the color settings for the frame and text string for all TextInBox primitives present in the current document:

    McDocument currentDocument = McDocumentsManager.GetActiveDoc();
    currentDocument.CustomProperties["BoxColor"] = "Blue";
    currentDocument.CustomProperties["TextColor"] = "Green";
    

    We rewrite the function OnDraw()from the example that is responsible for drawing the user primitive so that the color of the elements is read from the set properties of the document:

    public override void OnDraw(GeometryBuilder dc)
    {
      dc.Clear();
      dc.Color = Color.FromName(currentDocument.CustomProperties["BoxColor"] as String);
      dc.DrawPolyline(new Point3d[] { _pnt1, new Point3d(_pnt.X, _pnt2.Y, 0), 
                                      _pnt2, new Point3d(_pnt2.X, _pnt.Y, 0), 
                                      _pnt1});
      dc.TextHeight = 2.5 * DbEntity.Scale;	
      dc.Color = Color.FromName(currentDocument.CustomProperties["TextColor"] as String);
      dc.DrawMText(new Point3d((_pnt2.X + _pnt.X) / 2.0, (_pnt2.Y + _pnt.Y) / 2.0, 0), 
                   Vector3d.XAxis, Text, HorizTextAlign.Center, VertTextAlign.Center);
    }
    

    Thus, all added TextInBox primitives are displayed in accordance with the specified properties of the current document.

    Discussion of the article is also available on our forum: forum.nanocad.ru/index.php?showtopic=6517 .
    Translation of the article into English: MultiCAD.NET API: Saving non-graphical data in .dwg drawings .

    Also popular now: