Sooner or later every CAD application developer has to solve the problem of saving auxiliary non-graphical data in .dwg drawings. It can be attributes for a single graphical entity or layout or settings for a whole drawing. Unlike block attributes, this data is hidden from a user and used for programmatic processing of drawings.
There are a number of ways to solve the problem, for example, adding XData objects to drawing’s entities, using XRecords objects, or creating custom non-graphical objects.
In comparison with the traditional approaches, the mechanism provided by MultiCAD.NET API is much more compact and easy-to-use. It is also unified and can be applied in the same way for different object types in a drawing: entities, layouts or a whole drawing. Using this mechanism you are able to create and save auxiliary information of any data type.
Adding custom properties to graphical objects
You can work with object custom data, using the CustomProperties
property. The property is available for all child classes of McPropertySource
, particularly, for all graphical entities in a database (which are instances of the McDbEntity
class). CustomProperties
allows writing and reading data as key-value pairs:
1 2 3 |
entity.DbEntity.CustomProperties["Property"] = Value; |
Data of a simple type or array of simple types can be used as a property value. However, in practice, you can add values of any type by serializing data to an array of bytes. The code below shows the example of writing a dictionary object as a value of the MyCustomProperty
property:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
MemoryStream ms = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); Dictionary<String, Object> MyOptions = new Dictionary<String, Object>(); 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 of “complex” data is also divided in two steps: reading a byte array and its subsequent deserialization. Note, that CustomProperties
gets array as a List object:
1 2 3 4 5 6 7 8 9 10 11 |
List<Byte> loadedBinary = entity.DbEntity.CustomProperties["MyCustomProperty "] as List<Byte>; if (loadedBinary != null) { ms = new MemoryStream(loadedBinary.ToArray()); formatter = new BinaryFormatter(); MyOptions = formatter.Deserialize(ms) as Dictionary<String, Object>; int val = (int)MyOptions["param1"]; String str = MyOptions["param2"] as String; } |
Let’s look at how the method works for a real case. Assume that there is a .dwg-file with a power supply scheme where hot and cold water pipes are represented by polylines. We are going to add a description for lines selected by a user. The description contains the information about pipe type and its diameter:
[Pipe type] = Cold Water
[Pipe diameter] = 20
Create a command that allows the user to select a polyline and then adds the key-value pairs to the selected one:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[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"] = 20.0; } else { MessageBox.Show("No polyline entity selected"); } } |
For all McPropertySource
descendant classes you can also get properties of specific type (Custom, Object, User etc.) by using the GetProperties()
method. The following command gets a complete list of custom properties for user selected entity and prints their names:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
[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<McProperty> customPropertyNames = new List<McProperty>(); 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"); } |
Adding non-graphical data to a document
The MultiCAD.NET API allows saving non-graphical data not only for entities, but also for the entire document, layouts and blocks (subdocuments). The McDocument
class inherits the functionality of the base McPropertySource
class and therefore you can use the same CustomProperties
property to add your custom data to documents of different levels:
1 2 3 4 5 6 7 8 9 10 11 |
[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 look at how it works. In one of the previous posts we talked about creating custom entities in MultiCAD.NET and dealt with the TextInBox
entity that is a framed text string:
Applying custom properties to a document object, you can define its additional parameters and settings. For example, you can add properties that set colors of a frame and text string for all TextInBox
entities present in the current drawing:
1 2 3 4 5 |
McDocument currentDocument = McDocumentsManager.GetActiveDoc(); currentDocument.CustomProperties["BoxColor"] = "Blue"; currentDocument.CustomProperties["TextColor"] = "Green"; |
Overwrite the OnDraw()
method from the example that is responsible for drawing an entity so that the element’s color is derived from the document custom properties:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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); } |
Now all added TextInBox
entities are drawn in compliance with the document’s properties.