TheSwamp

Code Red => .NET => Topic started by: Helsinki_Dave on April 12, 2011, 08:34:39 AM

Title: Autocad Architecture: Get a SINGLE property set definition value by name?
Post by: Helsinki_Dave on April 12, 2011, 08:34:39 AM
Hi, I was wondering if anyone has cracked this one, as I've been sitting on this since the beginning of the year - but I'm mystified.

How can I find a SINGLE value of a a property set definition - ie I don't want a list as returned by the For Each loop.

Here's the ACA supplied code - which supplies all the PropertyValueUnitPairs cleverly stored in an Arraylist.

Code: [Select]
#Region "Command_GetPropertyDataByName"
    ' <summary>
    ' Command implementation for GetPropertyDataByName.
    ' </summary>

    <Autodesk.AutoCAD.Runtime.CommandMethod("AecPropertySampleMgd", "GetPropertyDataByName", Autodesk.AutoCAD.Runtime.CommandFlags.Modal)> _
    Public Sub Command_GetPropertyDataByName()
        Dim ed As Editor = Application.DocumentManager.MdiActiveDocument.Editor

        ' prompt for the property name
        Dim res1 As PromptResult = ed.GetString(vbCrLf + "Enter Property name: ")

        If res1.Status <> PromptStatus.OK Then
            Return
        End If

        Dim pname As String = res1.StringResult

        Dim db As Database = HostApplicationServices.WorkingDatabase
        Dim tm As AcadDb.TransactionManager = db.TransactionManager

        Dim dbobj As AcadDb.DBObject

        Dim trans As Transaction = tm.StartTransaction()

        Dim bt As BlockTable = tm.GetObject(db.BlockTableId, OpenMode.ForRead, False)
        Dim btr As BlockTableRecord = tm.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForRead, False)

        Dim id As ObjectId
        For Each id In btr

            dbobj = tm.GetObject(id, OpenMode.ForRead, False, False)

            Dim values As System.Collections.ArrayList = GetValuesFromPropertySetByName(pname, dbobj)
            Dim value_unit As AecPropDb.PropertyValueUnitPair
            For Each value_unit In values
                ed.WriteMessage(vbCrLf + "Property with name = " + pname + " Entity ObjectId = " + id.ToString())
                ed.WriteMessage(vbCrLf + "Unit Type = " + value_unit.UnitType.InternalName + ", IsImperial = " + value_unit.UnitType.IsImperial.ToString() + ", Type = " + value_unit.UnitType.Type.ToString())

                Dim val As Object = value_unit.Value
                If Not val Is Nothing Then
                    ed.WriteMessage(vbCrLf + "DataType = " + val.GetType().ToString())
                    ed.WriteMessage(vbCrLf + "Value = " + val.ToString())
                End If

            Next
        Next
        trans.Commit()
        trans.Dispose()
    End Sub
#End Region

And here's the function

Code: [Select]
#Region "GetValuesFromPropertySetByName"
    ' <summary>
    ' Returns the values (PropertyValueUnitPair) of a property by name on a given object.
    ' </summary>
    ' <param name="pname">The property name to find on the object.</param>
    ' <param name="dbobj">The object to find the property on. </param>
    ' <returns> An array of the values </returns>
    Public Function GetValuesFromPropertySetByName(ByVal pname As String, ByVal dbobj As AcadDb.DBObject) As System.Collections.ArrayList

        Dim setIds As ObjectIdCollection = AecPropDb.PropertyDataServices.GetPropertySets(dbobj)

        Dim values As System.Collections.ArrayList = New System.Collections.ArrayList()

        If setIds.Count = 0 Then
            Return values ' just return emtpy collection...
        End If


        Dim db As Database = HostApplicationServices.WorkingDatabase
        Dim tm As AcadDb.TransactionManager = db.TransactionManager
        Dim psId As ObjectId
        For Each psId In setIds
            Dim pset As AecPropDb.PropertySet = tm.GetObject(psId, OpenMode.ForRead, False, False) 'As AecPropDb.PropertySet
            Dim pid As Integer
            Try
                pid = pset.PropertyNameToId(pname)
                values.Add(pset.GetValueAndUnitAt(pid))
            Catch e As Autodesk.AutoCAD.Runtime.Exception
                ' most likely eKeyNotfound.

            End Try

        Next

        Return values
    End Function
#End Region

so to acceess just a single property set def...do I have to do something wierd like...

Code: [Select]
           For Each objId As ObjectId In mdlSpace

                If objId.ObjectClass.Name = "AecDbSpace" Then

                    Dim spce As ArchDbSrvcs.Space = trx.GetObject(objId, OpenMode.ForRead)

                    Dim spceStyle As ArchDbSrvcs.SpaceStyle = trx.GetObject(spce.StyleId, OpenMode.ForRead)

                    Dim Values_Result As ArrayList = GetValuesFromPropertySetByName("Category", spce)

                    Dim ArrayListIndexValue As Integer = Values_Result.IndexOf("Category")


                    Dim value_unit As AecPropDb.PropertyValueUnitPair = Values_Result.Item(ArrayListIndexValue)

                    Dim val As Object = value_unit.Value
                    'ed.WriteMessage(vbCrLf + "DataType = " + val.GetType().ToString())
                    Dim CaseSelValue As String = val.ToString()

                    Select Case CaseSelValue

                        Case "Block"

thanks in advance for your help.
Title: Re: Autocad Architecture: Get a SINGLE property set definition value by name?
Post by: Helsinki_Dave on April 26, 2011, 07:06:45 AM
ok, well I did it. It seems that, in lay-mans terms, there's been a bit of a storm in the timber yard - and, well, the whole way of thinking about property sets and property set definitions has been re-thought on the journey from VBA to .NET . That said, the future is bright - albeit full of transactions and blocktables, just takes a long time for guys like me to get their head around it.

So here's some pretty butch code that finds a single definition, on a single property set, on a AecDbSpace. If you have the same named definition on another property set, it wont touch it, or if you have the same named definition on another object - like a AECPolygon, it won't touch it.

Pat on the back.
 
Code: [Select]
#Region "Namespaces"

Imports System

Imports Autodesk.AutoCAD.ApplicationServices
Imports Autodesk.AutoCAD.DatabaseServices
Imports Autodesk.AutoCAD.EditorInput
Imports Autodesk.AutoCAD.Runtime

Imports Autodesk.Aec.DatabaseServices
Imports Autodesk.Aec.PropertyData.DatabaseServices

Imports DBTransactionManager = Autodesk.AutoCAD.DatabaseServices.TransactionManager
Imports AcadDb = Autodesk.AutoCAD.DatabaseServices
Imports AecDb = Autodesk.Aec.DatabaseServices
Imports AecPropDb = Autodesk.Aec.PropertyData.DatabaseServices

Imports ObjectId = Autodesk.AutoCAD.DatabaseServices.ObjectId
Imports ObjectIdCollection = Autodesk.AutoCAD.DatabaseServices.ObjectIdCollection

Imports AecDbSrvcs = Autodesk.Aec.DatabaseServices
Imports ArchDbSrvcs = Autodesk.Aec.Arch.DatabaseServices

#End Region

Public Class Renumber

#Region "Command_Renumber"
    ' <summary>
    ' Command implementation for SetPropertyDataByName.
    ' </summary>
    <Autodesk.AutoCAD.Runtime.CommandMethod("AecPropertySampleMgd", "koe", Autodesk.AutoCAD.Runtime.CommandFlags.Modal)> _
    Public Sub Command_SetPropertyDataByName()
        Dim ed As Editor = Application.DocumentManager.MdiActiveDocument.Editor
        Dim psetname As String = "Block_properties"
        Dim pname As String = "Block_ID"
        Dim NewValue As Integer = 112233

        Dim db As Database = HostApplicationServices.WorkingDatabase
        Dim tm As AcadDb.TransactionManager = db.TransactionManager

        Dim dbobj As AcadDb.DBObject

        Dim trans As Transaction = tm.StartTransaction()

        Dim bt As BlockTable = tm.GetObject(db.BlockTableId, OpenMode.ForRead, False)
        Dim btr As BlockTableRecord = tm.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForRead, False)

        Dim id As ObjectId
        For Each id In btr
            If id.ObjectClass.Name = "AecDbSpace" Then

                dbobj = tm.GetObject(id, OpenMode.ForRead, False, False)

                Dim spobj As ArchDbSrvcs.Space = tm.GetObject(id, OpenMode.ForRead, False, False)
                Dim spceStyle As ArchDbSrvcs.SpaceStyle = tm.GetObject(spobj.StyleId, OpenMode.ForRead, False, False)

                If spceStyle.Name = "Block" Then


                    'Dim values As System.Collections.ArrayList = GetValueFromPropertySetByName(psetname, pname, dbobj)
                    'Dim value_unit As AecPropDb.PropertyValueUnitPair
                    'For Each value_unit In values
                    '    Dim currentValue As Object = value_unit.Value
                    '    If Not currentValue Is Nothing Then
                    '        If (currentValue.GetType() Is GetType(Double)) Then
                    Dim trans2 As Transaction = tm.StartTransaction()

                    dbobj.UpgradeOpen()

                    Dim WasChanged As Boolean = SetValueFromPropertySetByName(psetname, pname, dbobj, NewValue)
                    If WasChanged Then
                        ed.WriteMessage(vbCrLf + "Succesfully changed value for objectId = " + id.ToString())
                        ed.WriteMessage(vbCrLf + " New Value = " + NewValue.ToString())
                    Else
                        ed.WriteMessage(vbCrLf + "Failed to change value for objectId = " + id.ToString())
                    End If
                    trans2.Commit()
                    trans2.Dispose()
                    '    End If
                    'End If
                End If
            End If
        Next
        'Next
        trans.Commit()
        trans.Dispose()
    End Sub
#End Region
#Region "SetValueFromPropertySetByName"
    ' <summary>
    ' Sets the values (PropertyValueUnitPair) of a property by name on a given object.
    ' </summary>
    ' <param name="pname">The property name to find on the object.</param>
    ' <param name="dbobj">The object to set the property on. </param>
    ' <param name="value">The value to set. </param>
    ' <returns> true if succesful, or false otherwise. </returns>
    Public Function SetValueFromPropertySetByName(ByVal psetname As String, ByVal pname As String, ByVal dbobj As AcadDb.DBObject, ByVal NewValue As Object) As Boolean

        Dim findany As Boolean = False
        Dim setIds As ObjectIdCollection = AecPropDb.PropertyDataServices.GetPropertySets(dbobj)

        Dim db As Database = HostApplicationServices.WorkingDatabase
        Dim tm As AcadDb.TransactionManager = db.TransactionManager
        Dim trans As Transaction = tm.StartTransaction()
        Dim psId As ObjectId

        For Each psId In setIds 'setids is all property sets
            Dim pset As AecPropDb.PropertySet = tm.GetObject(psId, OpenMode.ForWrite, False, False) ' As AecPropDb.PropertySet

            If pset.PropertySetDefinitionName = psetname Then

                Dim pid As Integer 'have to create this object to place the PropertyNameToId somewhere

                pid = pset.PropertyNameToId(pname) 'propertynametoid gives the id for the psetdef
                If (pset.IsWriteEnabled) Then
                    pset.SetAt(pid, NewValue)
                End If

                findany = True
                'esential findany changes the value of the function

            End If
        Next

        trans.Commit()
        trans.Dispose()

        Return findany
    End Function

#End Region
End Class