Access and modify PropertySet in vb.net

Access and modify PropertySet in vb.net

Anonymous
Not applicable
4,669 Views
12 Replies
Message 1 of 13

Access and modify PropertySet in vb.net

Anonymous
Not applicable

I am trying to access  and to modify the propertysets of an object (same way I am modifying attributes value for a block attribute).

I have something like this in a loop within a selection set, that I hope will give me the propertysets collection of an object and I would like to be able to modify the value for each propertyset after checking its name:

Dim ent As DBObject = tm.GetObject(id, OpenMode.ForWrite, True)
Dim PropSets As ObjectIdCollection
Dim propSet As PropertySet

PropSets = PropertyDataServices.GetPropertySets(ent)

 

I can't find documentation.

(I have checked these samples already:

https://adndevblog.typepad.com/aec/2012/09/attach-a-property-set-to-an-object.html

https://adndevblog.typepad.com/aec/2012/09/defining-a-property-set-definition-in-net-.html)

 

0 Likes
4,670 Views
12 Replies
Replies (12)
Message 2 of 13

gleeuwdrent
Advocate
Advocate

See code snippet below (in C#) for an example.

 

 

 

string propSetName = "Your property set name"
ObjectIdCollection psIds = PropertyDataServices.GetPropertySets(selectedObject); //retrieve all property sets of object
foreach (ObjectId oidPs in psIds) //iterate trough property sets
{
		PropertySet ps = oidPs.GetObject(AcDb.OpenMode.ForRead) as PropertySet; //get property set for read
		if (ps.PropertySetDefinitionName == propSetName) 
		{
				PropertySet psCurrent = ps;
				int psIdCurrent = psCurrent.PropertyNameToId(propName); //id of the corridor name property
				psCurrent.SetAt(psIdCurrent, propValue); //update property value
				continue;
		}
}

 

 

 

0 Likes
Message 3 of 13

Anonymous
Not applicable

Thanks, but the solution is throwing a ekeynotfound error:

   à Autodesk.Aec.PropertyData.DatabaseServices.PropertySet.PropertyNameToId(String name)

   à MyCadCommands.Form1.singlePick() dans ...\source\repos\MyCadCommands\MyCadCommands\Form1.vb:ligne 598

I had the same issue with my own tests.

Furthermore I was unclear in my topic, what I would like is to access a property definition inside a property set and modity the value: (not the property set name itself)

            Dim id As ObjectId
            For Each id In idarray
                Dim ent As DBObject = tm.GetObject(id, OpenMode.ForWrite, True)
                ' Dim Sched As New AecScheduleApplication
                ' Dim idsPropSets As ObjectIdCollection
                'Dim propSet As PropertySet

                Dim propSetName As String = "test"
                Dim propValue As String = "yolo"
                Dim psIds As ObjectIdCollection = PropertyDataServices.GetPropertySets(ent) 'retrieve all property sets of object
                For Each oidPs As ObjectId In psIds 'iterate trough Property sets

                    Dim psas As PropertySet = oidPs.GetObject(AecDb2.OpenMode.ForRead) 'get property set for read
                    If psas.PropertySetDefinitionName = propSetName Then

                        Dim psCurrent As PropertySet = psas
                        'Dim psIdCurrent As Integer = psCurrent.PropertyNameToId(propSetName) 'id of the corridor name property
                        'psCurrent.SetAt(psIdCurrent, propValue) 'update Property value
                        Dim propSetDef As PropertySetDefinition = CType(myT.GetObject(psCurrent.PropertySetDefinition, OpenMode.ForRead), PropertySetDefinition)
                        Dim propDefs As PropertyDefinitionCollection = propSetDef.Definitions
                        Dim propDef As PropertyDefinition
                        For Each propDef In propDefs


                        Next propDef
                        Continue For
                    End If

                Next oidPs

 

0 Likes
Message 4 of 13

norman.yuan
Mentor
Mentor

You did not indicate which line of code caused the error. But I assume/guess it is this line:

 

Dim psIds As ObjectIdCollection = PropertyDataServices.GetPropertySets(ent)

 

and the reason of this error is the entity you passed in does not have data from any PropertySet.

 

Autodesk implements this method in very strange way: naturally, we would expect he method returns an empty ObjectIdCollection, or even a null, if the entity does not have any propertyset data attached. Instead the method raise an exception.

 

The same thing would also happen to this method (if you want to retrieve specific PropertySet data from an entity, but the entity does not have data from that PropertyDataSet):

 

Dim psId = PropertySetDataServices.GetPropertySet([DBobject], [PropertysetDefinitionId])

 

The same thing also happens in AutoCAD Map's API implementation (Object Classification).

 

So, the trick is you need to wrap up the code into a Try...Catch... block, and handle the exception in Catch clause, or not handle it at all with empty Catch... clause.

 

Try

Dim psIds As ObjectIdCollection = PropertyDataServices.GetPropertySets(ent)

...

Catch

End Try

 

HTH

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 5 of 13

Anonymous
Not applicable

The issue was due to this line: PropertyNameToId(propSetName) that I have removed.

I know that I have a property set and property set definitions because I use a vb.net to create them and attach them to an object that I pick.

I think I am almost where I want to get:

Iterate through property set collection, then within the chosen property set I iterate through property set definition collection

And finaly get the propertyset definition properties. I just need now to change the value.

Dim SS As Autodesk.AutoCAD.EditorInput.SelectionSet = res.Value
Dim idarray As ObjectId() = SS.GetObjectIds()
Dim tm As Autodesk.AutoCAD.DatabaseServices.TransactionManager = db.TransactionManager
Dim myT As Transaction = tm.StartTransaction()

Try
Dim id As ObjectId
For Each id In idarray
	Dim ent As DBObject = tm.GetObject(id, OpenMode.ForWrite, True)
	Dim propSetName As String = "PROPERTY_SET_NAME"
	Dim propValue As String = "yolo"
	Dim psIds As ObjectIdCollection = PropertyDataServices.GetPropertySets(ent) 
	For Each oidPs As ObjectId In psIds 
		Dim psas As PropertySet = oidPs.GetObject(AecDb2.OpenMode.ForRead) 
		If psas.PropertySetDefinitionName = propSetName Then
			MsgBox(psas.PropertySetDefinitionName)
			Dim propSetDefObj As DBObject = myT.GetObject(psas.PropertySetDefinition, OpenMode.ForRead)
			Dim propSetDef As PropertySetDefinition = propSetDefObj
			Dim propDefs As PropertyDefinitionCollection = propSetDef.Definitions
			Dim propDef As PropertyDefinition
			Dim iProp As Integer = 0
			For Each propDef In propDefs
				Dim propDefId As ObjectId = propDef.FormatId
				iProp += 1
				If (propDef.Automatic) Then 'AUTOMATIC
				Else

					If propDef.Name = "PROPERTY_SET_DEFINITION_NAME" Then
'HERE I WANT TO SET profDef value
				
					End If
				End If
			Next propDef
		End If
	Next oidPs
Next id

 

I

0 Likes
Message 6 of 13

norman.yuan
Mentor
Mentor

Well, I think the first reply you got from @gleeuwdrent was quite clear: after you identified the PropertySet that is attached to the entity, you simply use each property's name to set its value, by first calling PropertyNameToId(string) method, and then calling SetAt(int, object) to set its value.

 

@gleeuwdrent 's code is to set a particular property's value, if the property's name is known. Following code sample assumes that you have a set of data meant to update a PropertySet attached to an entity, and the data is organized in a Dictionary<string, object> with the keys being property names:

private void SetPropertySetValueToEntity(
            ObjectId entId, string propertySetName, Dictionary<string, object> values)
        {
            using (var tran = entId.Database.TransactionManager.StartTransaction())
            {
                var ent = tran.GetObject(entId, OpenMode.ForRead);
                ObjectIdCollection propSetIds = null;
                try
                {
                    propSetIds = Autodesk.Aec.PropertyData.DatabaseServices.PropertyDataServices.
                        GetPropertySets(ent);  
                }
                catch { }

                if (propSetIds!=null)
                {
                    foreach (ObjectId id in propSetIds)
                    {
                        var propSet = (PropertySet)tran.GetObject(id, OpenMode.ForWrite);
                        if (propSet.Name.ToUpper()==propertySetName.ToUpper())
                        {
                            SetPropertySetValues(propSet, values);
                            break;
                        }
                    }
                }

                tran.Commit();
            }
        }

        private void SetPropertySetValues(PropertySet propSet, Dictionary<string, object> values)
        {
            foreach (var item in values)
            {
                try
                {
                    int propId = propSet.PropertyNameToId(item.Key);
                    propSet.SetAt(propId, item.Value);
                }
                catch { }
            }
        }

 

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 7 of 13

Anonymous
Not applicable

Thanks for your efforts. The code sample you have posted is most probably what I need and I get the principles but the line :

 

propSet.SetAt(propId, item.Value);

 

Is causing a Fatal Error... (Unhandled Access Violation Reading 0x0000 Exception at fbb76559h)

 

    Public Sub SetPropertySetValueToEntity(entId As ObjectId, propertySetName As String, values As Dictionary(Of String, Object))

        Using tran = entId.Database.TransactionManager.StartTransaction()

            Dim ent = tran.GetObject(entId, OpenMode.ForRead)
            Dim propSetIds As ObjectIdCollection = Nothing
            Try

                propSetIds = Autodesk.Aec.PropertyData.DatabaseServices.PropertyDataServices.GetPropertySets(ent)

            Catch
            End Try
            If (propSetIds IsNot Nothing) Then

                For Each id As ObjectId In propSetIds

                    Dim propSet As PropertySet = tran.GetObject(id, OpenMode.ForWrite)
                    If (propSet.Name.ToUpper() = propertySetName.ToUpper()) Then

                        SetPropertySetValues(propSet, values)


                    End If
                Next id
            End If

            tran.Commit()
        End Using
    End Sub

    Public Sub SetPropertySetValues(propSet As PropertySet, values As Dictionary(Of String, Object))
        For Each item In values
            Try
                Dim propId As Integer = propSet.PropertyNameToId(item.Key)
                propSet.SetAt(propId, item.Value)

            Catch ex As Exception
                MsgBox("error: " + ex.ToString + vbCrLf)
            End Try
        Next item
    End Sub

    <CommandMethod("CHECKPSET2")>
    Public Sub picker()
        ' Top most objects.
        Dim doc As Document = Application.DocumentManager.MdiActiveDocument
        Dim db As Database = doc.Database
        Dim ed As Editor = doc.Editor

        Dim Opts As New PromptSelectionOptions()
        Opts.AllowDuplicates = True
        Opts.MessageForAdding = "Select entities"
        Dim res As PromptSelectionResult = ed.GetSelection(Opts)

        If res.Status = PromptStatus.Error Then
            Return
        End If

        Dim SS As Autodesk.AutoCAD.EditorInput.SelectionSet = res.Value
        Dim idarray As ObjectId() = SS.GetObjectIds()

        Dim mydic As Dictionary(Of String, Object) = Nothing
        mydic.Add("LAC_TYPE_SUPPORT", "TEST")
        Try
            Dim id As ObjectId
            For Each id In idarray
                SetPropertySetValueToEntity(id, "DONNEES_ETENDUES_LAC", mydic)
            Next id
        Finally

        End Try
    End Sub 'singlePick

 

0 Likes
Message 8 of 13

norman.yuan
Mentor
Mentor

I am not sure what is wrong without having your actual drawing to test/debug the code. However, the code has a minor bug, which seems not related to the error you get: when comparing PropertySet's name with given PropertySetDefinition's name, we should use "PropertySetDefinitionName" property, instead of "Name". That is:

 

if line:

 

If (propSet.Name.ToUpper() = propertySetName.ToUpper()) Then

 

should be 

If (propSet.PropertySetDefinitionName.ToUpper() = propertySetName.ToUpper() Then

... ... 

 

I put together some code to retrieve/set an entity with PropertySet data in this scenario: a propertyset is defined in the drawing, called "Test Property Set", which has 2 Properties: Name (string) and Count (integer). Code is as following:

 

using System.Collections.Generic;
using CadDb = Autodesk.AutoCAD.DatabaseServices;
using PsDb = Autodesk.Aec.PropertyData.DatabaseServices;
using Autodesk.Aec.PropertyData.DatabaseServices;
using Autodesk.AutoCAD.DatabaseServices;

namespace EntityPropertySetData
{
    public class PropertySetDataUtil
    {
        public static void SetPropertySetDataToEntity(
            CadDb.ObjectId entId, string propertysetName, Dictionary<string, object> values)
        {
            using (var tran = entId.Database.TransactionManager.StartTransaction())
            {
                var ent = tran.GetObject(entId, CadDb.OpenMode.ForRead);

                var pset = GetPropertySetFromEntity(propertysetName, ent, tran, true);

                UpdatePropertySetData(pset, values);

                tran.Commit();
            }
        }

        public static Dictionary<string, object> GetPropertySetDataFromEntity(
            CadDb.ObjectId entId, string propertysetName)
        {
            var dic = new Dictionary<string, object>();

            using (var tran = entId.Database.TransactionManager.StartTransaction())
            {
                var ent = tran.GetObject(entId, OpenMode.ForRead);
                var pset = GetPropertySetFromEntity(propertysetName, ent, tran, false);
                if (pset!=null)
                {
                    foreach (PsDb.PropertySetData data in pset.PropertySetData)
                    {
                        var propId = data.Id;
                        var propName = pset.PropertyIdToName(propId);

                        // Get property value
                        object val = data.GetData();

                        // or 
                        val = pset.GetAt(propId);

                        dic.Add(propName, val);
                    }
                }

                tran.Commit();
            }

            return dic;
        }

        private static PropertySet GetPropertySetFromEntity(
            string propertysetName, DBObject ent, Transaction tran, bool create = true)
        {
            PropertySet ps = GetPropertySetFromExistings(ent, propertysetName, tran);
            
            if (ps==null && create)
            {
                var psDefId = GetPropertySetDefinitionId(ent.Database, propertysetName);
                if (!psDefId.IsNull)
                {
                    if (!ent.IsWriteEnabled) ent.UpgradeOpen();
                    PropertyDataServices.AddPropertySet(ent, psDefId);
                    ps = GetPropertySetFromExistings(ent, propertysetName, tran);
                }
            }

            return ps;
        }

        private static PropertySet GetPropertySetFromExistings(
            DBObject ent, string propertysetName, Transaction tran)
        {
            PropertySet pset = null;
            try
            {
                var psIds = PropertyDataServices.GetPropertySets(ent);
                foreach (CadDb.ObjectId id in psIds)
                {
                    var ps = (PropertySet)tran.GetObject(id, OpenMode.ForRead);
                    if (ps.PropertySetDefinitionName.ToUpper() == propertysetName.ToUpper())
                    {
                        pset = ps;
                        break;
                    }
                }
            }
            catch { }

            return pset;
        }

        private static CadDb.ObjectId GetPropertySetDefinitionId(
            Database db, string propertysetName)
        {
            using (var propDic = new PsDb.DictionaryPropertySetDefinitions(db))
            {
                foreach (string name in propDic.NamesInUse)
                {
                    if (name.ToUpper() == propertysetName.ToUpper()) 
                        return propDic.GetAt(name);
                }
            }

            return ObjectId.Null;
        }

        private static void UpdatePropertySetData(
            PropertySet pset, Dictionary<string, object> values)
        {
            if (!pset.IsWriteEnabled) pset.UpgradeOpen();

            foreach (var item in values)
            {
                var propName = item.Key;
                var val = item.Value;

                UpdateProperty(pset, propName, val);
            }
        }

        private static void UpdateProperty(
            PropertySet pset, string propName, object propValue)
        {
            int propId = -1;
            try
            {
                propId = pset.PropertyNameToId(propName);
            }
            catch { }
            if (propId >= 0)
            {
                pset.SetAt(propId, propValue);
            }
        }
    }
}

 

 

 

using System.Collections.Generic;
using System.Text;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;

[assembly: CommandClass(typeof(EntityPropertySetData.MyCommands))]

namespace EntityPropertySetData
{
    public class MyCommands
    {
        private const string PROPERTY_SET_NAME = "Test Property Set";
        private const string PROPERTY_NAME_1 = "Name";
        private const string PROPERTY_NAME_2 = "Count";

        [CommandMethod("GetPsData")]
        public static void GetPropertySetData()
        {
            var doc = CadApp.DocumentManager.MdiActiveDocument;
            var ed = doc.Editor;

            var entId = SelectEntity(ed);
            if (!entId.IsNull)
            {
                var dic = PropertySetDataUtil.GetPropertySetDataFromEntity(
                    entId, PROPERTY_SET_NAME);
                if (dic.Count>0)
                {
                    var msg = new StringBuilder();
                    foreach (var item in dic)
                    {
                        msg.Append($"\nProperty Name: {item.Key}\t" +
                            $"Property Value: {item.Value.ToString()}");
                    }

                    ed.WriteMessage(msg.ToString());
                }
                else
                {
                    ed.WriteMessage(
                        "\nNo property data found with selected entity!");
                }
            }

            ed.WriteMessage("\n");
        }

        [CommandMethod("SetPsData")]
        public static void SetPropertySetData()
        {
            var doc = CadApp.DocumentManager.MdiActiveDocument;
            var ed = doc.Editor;

            var entId = SelectEntity(ed);
            if (!entId.IsNull)
            {
                var values = new Dictionary<string, object>();
                values.Add(PROPERTY_NAME_1, "ABCDEFG");
                values.Add(PROPERTY_NAME_2, 202);

                PropertySetDataUtil.SetPropertySetDataToEntity(
                    entId, PROPERTY_SET_NAME, values);
            }
        }

        private static ObjectId SelectEntity(Editor ed)
        {
            var res = ed.GetEntity("\nSelect entity:");
            if (res.Status == PromptStatus.OK)
                return res.ObjectId;
            else
                return ObjectId.Null;
        }
    }
}

 

 

See the video clip bellow on how the code works, as expected:

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 9 of 13

Joaquin.FernandezVZWM2
Explorer
Explorer

Can you post the VS project file. my C# skills are not there yet.

Thanks.

0 Likes
Message 10 of 13

norman.yuan
Mentor
Mentor

Sorry, due to my work place change/computer change, I cannot locate the original code project. 

On the other hand, if you do know how to create a .NET framework class library project as AutoCAD plugin, then it would be very simple to copy code from my reply of this post here. Here are the steps:

 

1. Start Visual Studio, choose to create a new project of .NET framework class library;

2. Name the project "EntityPropertySetData" (or whatever name you choose, but you may have to change the namespace when copy the 2 classes from my post accordingly);

3. Once the project is open, add the 3 Acad .NET API assemblies as references (accoremgd/acdbmgd/acmgd.dll), and then add reference to AecPropDataMgd.dll, which usually locates in folder "C:\Program Files\Autodesk\AutoCAD 202x\ACA\". Make sure set "Copy Local" to "False" for all 4 references;

4. Add class "PropertySetDataUtil", and then copy/paste the code from my post to the class;

5. Add class "MyCommand", and then copy/paste the code from my post to the class;

6. Compile the code and, off you go for a test run.

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 11 of 13

Anonymous
Not applicable

Mark

0 Likes
Message 12 of 13

Joaquin.FernandezVZWM2
Explorer
Explorer

Thanks for your help. I got it to work as per your instructions. But I am getting an anomaly. In the property set definition I have the object Handle as part of the extended data, and it is not displaying in the output of the app. Do you have any suggestions?acad_j73ScIdMI3.png

0 Likes
Message 13 of 13

norman.yuan
Mentor
Mentor

Sorry, without seeing your code of how the PropertyDataSet is defined and how it is displayed(showed at command line), it is difficult to comment.

Norman Yuan

Drive CAD With Code

EESignature

0 Likes