Instance Properties and BOMRows

Instance Properties and BOMRows

jdasilvaS39UQ
Advocate Advocate
1,945 Views
13 Replies
Message 1 of 14

Instance Properties and BOMRows

jdasilvaS39UQ
Advocate
Advocate

Hi,

 

We have a vba program that iterates through bom rows of the active assembly, specifically the parts only bom, extracts iproperties from the bomrow, does some manipulation and then outputs to an excel sheet. I wanted to get instance properties working so we can change our output to be different based on the assembly you are in.

 

The first issue I ran into was accessing instance properties of the bomrow, which I was able to get working if the part in question is directly in the assembly. The code I used was: 

On Error Resume Next
    
            Err.clear
            Set InstanceProperties = oBOMRow.OccurrencePropertySets(1)
            
            If Err.Number = 0 Then
                hasInstanceProperties = True
                Err.clear
            End If

' Then to access the property (this is just one example line)
newItem.Raw_Description = UCase(InstanceProperties.item("Raw_Description").value)

 This worked great on this individual assembly, but when I make the assembly in question a part of a larger assembly (the part with instance properties is more than one sub-assembly level down), I'm not able to access the instance properties anymore from the bom row. It is confusing too because the inventor bom shows the instance properties like I am expecting, it just doesn't pick it up from the bomrow.

 

Does anyone have experience with instance properties and bomrows? Can I approach this from another angle?

 

Here is the BOM where 5AJ.49.011-D05 is directly in. Instance properties are accessible here

jdasilvaS39UQ_0-1646932046867.png

 

Here is the BOM at the top level. Instance properties show up on the bomrow but then don't exist when accessing it through vba.

jdasilvaS39UQ_1-1646932148964.png

 

0 Likes
Accepted solutions (1)
1,946 Views
13 Replies
Replies (13)
Message 2 of 14

Ralf_Krieg
Advisor
Advisor
Accepted solution

Hello

 

I think this is because of the parts in subassemblies are represented as ComponentOccurrenceProxies in the context of the main assembly. You need to grab the native object and get it from there.

 

oBOMRow.ComponentOccurrences(1).NativeObject.OccurrencePropertySets(1)

 


R. Krieg
RKW Solutions
www.rkw-solutions.com
0 Likes
Message 3 of 14

jdasilvaS39UQ
Advocate
Advocate

Thank you! This was exactly why. I was also struggling with oBOMRow.ComponentOccurrences(1).OccurrencePropertySetsEnabled erroring out and from your suggestion changed it to oBOMRow.ComponentOccurrences(1).NativeObject.OccurrencePropertySetsEnabled

0 Likes
Message 4 of 14

jdasilvaS39UQ
Advocate
Advocate

Final working code for reference

' Check for instance properties

Dim hasInstanceProperties As Boolean

    If oBOMRow.ComponentOccurrences(1).Type = kComponentOccurrenceObject Then
    
        If oBOMRow.ComponentOccurrences(1).OccurrencePropertySetsEnabled Then
            Set InstanceProperties = oBOMRow.ComponentOccurrences(1).OccurrencePropertySets(1)
            hasInstanceProperties = True
        End If

    ElseIf oBOMRow.ComponentOccurrences(1).Type = kComponentOccurrenceProxyObject Then
        
        If oBOMRow.ComponentOccurrences(1).NativeObject.OccurrencePropertySetsEnabled Then
            Set InstanceProperties = oBOMRow.ComponentOccurrences(1).NativeObject.OccurrencePropertySets(1)
            hasInstanceProperties = True
        End If
        
    Else
        Debug.Print ("component is not an occurrence or occurrence proxy")
    End If

 

0 Likes
Message 5 of 14

mslosar
Advisor
Advisor

Is NativeObject not accessible via c#?

 

I rewrote the above and NativeObject does not seem to be tehre....also, it errors everytime checking to see if OccurrenecPropertySetsEnabled is true. (Line 20)

 

 

foreach (BOMRow aBomRow in BomRows)
            {
                ComponentDefinition oCompDef = aBomRow.ComponentDefinitions[1];

                //PartDocument oPartDoc = (PartDocument)oCompDef.Document;
                Document oPartDoc = (Document)oCompDef.Document;
                //now that we a have a part document, get it's properties.

                UnitsOfMeasure UOM = oPartDoc.UnitsOfMeasure;

                //playing with SpareMin
                //if (oCompDef.Occurrences[1].OccurrencePropertySetsEnabled)                
                //{
                //    string sm = aBomRow.OccurrencePropertySets[1]["SpareMin"].ToString();
                //    MessageBox.Show(sm);
                //}
                
                PropertySet ipropset;

                if (oCompDef.Occurrences[1].OccurrencePropertySetsEnabled)
                {
                    MessageBox.Show("Enabled");
                }
                else
                {
                    MessageBox.Show("Not Enabled");
                }

 

0 Likes
Message 6 of 14

jdasilvaS39UQ
Advocate
Advocate

You have to differentiate between componentoccurrences (part directly in your assembly) and componentoccurrenceproxies (part in a subassembly). You won't have a native object for a componentoccurrence object.

 

Also, do you have any virtual or suppressed parts? I have since added a line to check if it is a virtual part and if so, it can't have instance properties

0 Likes
Message 7 of 14

mslosar
Advisor
Advisor

Yeah, that's on me. That's an experimental file and i clipped it before the portion i rewrote.

 

The file i'm looking at is a bunch of nuts/bolts from content center. The powers that be want to be able to set some values on a per-project basis. In 2023, we found we can do this by adding instance properties, but i'm having trouble exporting those to our ERP setup, so i'm just trying to access the data. Which i'm not having any success in accessing from the BOM component.  So, this is the original section I rewrote for c# - the NativeObject gets the red lines under it as it doesn't seem to exist there...

 

if (aBomRow.ComponentOccurrences[1].Type == ObjectTypeEnum.kComponentOccurrenceObject)
                {
                    if (aBomRow.ComponentOccurrences[1].OccurrencePropertySetsEnabled)
                    {
                        ipropset = aBomRow.ComponentOccurrences[1].OccurrencePropertySets[1];
                        MessageBox.Show("a");
                    }
                }
                else if (aBomRow.ComponentOccurrences[1].Type == ObjectTypeEnum.kComponentOccurrenceProxyObject)
                {
                    MessageBox.Show("n");
                    ipropset = aBomRow.ComponentOccurrences[1].NativeObject.OccurrencePropertySets[1];
                    foreach (Inventor.Property p in ipropset)
                    {
                        MessageBox.Show(p.Name);
                    }
                }
                else
                {
                    MessageBox.Show("c");
                }

As for the code i'm trying to add it into that is this:

 

foreach (BOMRow aBomRow in BomRows)
            {
                ComponentDefinition oCompDef = aBomRow.ComponentDefinitions[1];

                //PartDocument oPartDoc = (PartDocument)oCompDef.Document;
                Document oPartDoc = (Document)oCompDef.Document;
                //now that we a have a part document, get it's properties.

                UnitsOfMeasure UOM = oPartDoc.UnitsOfMeasure;               
                 
                PropertySet prjPropertySet = (PropertySet)oPartDoc.PropertySets["Design Tracking Properties"];
                string oldPN = Convert.ToChar(34) + prjPropertySet["Part Number"].Value.ToString() + Convert.ToChar(34);
                //oldPN = oldPN.Length < 7 ? oldPN.PadLeft(7, '0') : oldPN;
                string desc = Convert.ToChar(34) + prjPropertySet["Description"].Value.ToString().Replace(@"""", @Anonymous"""") + Convert.ToChar(34);
                string stk = prjPropertySet["Stock Number"].Value.ToString();
               
                
                string itemNum = aBomRow.ItemNumber;
                string qty = aBomRow.TotalQuantity.ToString();

                string len = GetColumnValue(oPartDoc, "length");
                if (!string.IsNullOrEmpty(len))
                {
                    double dlen = UOM.ConvertUnits((double)UOM.GetValueFromExpression(len, UnitsTypeEnum.kInchLengthUnits),
                    UnitsTypeEnum.kDatabaseLengthUnits, UnitsTypeEnum.kInchLengthUnits);
                    dlen = dlen / 2.54f;
                    dlen = Math.Round(dlen, 4, MidpointRounding.AwayFromZero);
                    len = dlen.ToString();
                }
                
                string wid = GetColumnValue(oPartDoc, "width");
                if (!string.IsNullOrEmpty(wid))
                {
                    double dwid = UOM.ConvertUnits((double)UOM.GetValueFromExpression(wid, UnitsTypeEnum.kInchLengthUnits),
                    UnitsTypeEnum.kDatabaseLengthUnits, UnitsTypeEnum.kInchLengthUnits);
                    dwid = dwid / 2.54f;
                    dwid = Math.Round(dwid, 4, MidpointRounding.AwayFromZero);
                    wid = dwid.ToString();
                }

                string pdims =(!string.IsNullOrEmpty(wid) ? wid + " x " + len : len );

                //string shopNotes = Convert.ToChar(34) + GetColumnValue(oPartDoc, "shop1");
                //shopNotes = shopNotes + ((GetColumnValue(oPartDoc, "shop2").Length > 0) ? "\n" + GetColumnValue(oPartDoc, "shop2") : "" + Convert.ToChar(34));
                //shopNotes = shopNotes + ((GetColumnValue(oPartDoc, "shop3").Length > 0) ? "\n" + GetColumnValue(oPartDoc, "shop3") : "" + Convert.ToChar(34));
                string shopNotes = Convert.ToChar(34) + GetColumnValue(oPartDoc, "shop1").Replace(@"""", @Anonymous"""") + " " +
                                   GetColumnValue(oPartDoc, "shop2").Replace(@"""", @Anonymous"""") + " " +
                                   GetColumnValue(oPartDoc, "shop3").Replace(@"""", @Anonymous"""") + Convert.ToChar(34);
                string parentNumber = string.IsNullOrEmpty(parentNum) ? itemNum : parentNum + "." + itemNum;

                if (GetColumnValue(oPartDoc, "item create") == "Yes" || GetColumnValue(oPartDoc, "item create") == "True")
                {
                    sBomTable.Rows.Add(new object[] { itemNum, oldPN, stk, desc, qty, pdims, shopNotes });
                }

                if (aBomRow.ChildRows != null)
                {
                    if (GetColumnValue(oPartDoc, "item create") == "Yes" || GetColumnValue(oPartDoc, "item create") == "True")
                    {
                        ParseBOMLevel(aBomRow.ChildRows, -3, itemNum);
                    }
                    else
                    {
                        //skip it
                    }
                    
                }
            }
0 Likes
Message 8 of 14

jdasilvaS39UQ
Advocate
Advocate

I'm not too familiar with C# but I don't see why the object model would be any different. Does your code work on non-content center parts?

0 Likes
Message 9 of 14

mslosar
Advisor
Advisor

So, I do get different results (though still not access to NativeObject) with non-content center parts.

 

So, what i'm seeing is this:

 

For content center parts, they're returning as proxy objects.  But with no access to NativeObject, it errors because it would seem there is no OccurrencePropertySetsEnabled object on the proxy, just the native object. But I don't have access to it for some reason.

 

The testing i've done with normal parts always lands under kComponentOccurrenceObject, so native never comes into play. Content Center objects come under kComponentOccurrenceProxyObject, which is not giving me access to native object and since there's no OccurrencePropertySetsEnabled in the returned object (it seems), it results in an error.

0 Likes
Message 10 of 14

jdasilvaS39UQ
Advocate
Advocate

Do you have an example assembly with a couple parts? I'm thinking it has something to do with content center but I can't reproduce what you have

0 Likes
Message 11 of 14

mslosar
Advisor
Advisor

This is the file i'm trying this with.

 

it's a bit convoluted, but it's the type of thing i have do deal with and i just need to pull the value per BOM line, not per each individual part.

0 Likes
Message 12 of 14

jdasilvaS39UQ
Advocate
Advocate

I loaded it up and I can see the native object through VBA. Looking like this is a C# or an Inventor version issue, I'm on 2023 for what its worth.

0 Likes
Message 13 of 14

WCrihfield
Mentor
Mentor

Hi guys.  This is a complicated situation to diagnose, but knowing a couple more things might help you figure it out sooner.  The ComponentOccurrence object and the ComponentOccurrenceProxy object both have the same OccurrencePropertySetsEnabledOccurrencePropertySets properties, but only one of them will likely contain the data you are looking for, because those instance properties are stored in 'the active ModelState of the active assembly' at the moment the instance property was created...or if the member edit scope was set to factory scope at the time, it would have been stored in all the ModelStates of the active assembly at the time.  If you were editing a sub assembly when the instance property was created, then it was likely stored at that sub assembly level, and only the component within that sub assembly's context will have it (even if it is a proxy of a lower level component).

 

Another thing to keep in mind is that proxy objects are on a per level/context basis, so if the regular/native component is down in a 4th level sub assembly, there will be a proxy component representing it within every parent assembly up the ladder from it.  And simply using the NativeObject property of the top level component proxy will only get you a reference to the second level proxy component (yes, still a proxy in that situation), so to get the real component, you sometimes have to dig down through several levels of proxy-> NativeObject -> proxy -> NativeObject, until you get to the one that is not a proxy.

 

And when testing if it is a proxy or not, do not test it using "If TypeOf oComponent Is ComponentOccurrence" because both types will be True, because ComponentOccurrenceProxy Type is derived from the ComponentOccurrence Type.

You can use something like this to tell if it is a proxy or not:

Function IsProxy(oObj As Object) As Boolean
	If oObj Is Nothing Then Exit Function
	If TypeName(oObj).ToUpper.EndsWith("PROXY") Then Return True
	Return False
End Function

...you don't actually need the function, just the middle line, where you replace the oObj variable with the component variable.

 

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 14 of 14

mslosar
Advisor
Advisor

I finally got it. So, you have to create a new cast for C#...

 

else if (aBomRow.ComponentOccurrences[1].Type == ObjectTypeEnum.kComponentOccurrenceProxyObject)
                {
                    ComponentOccurrenceProxy componentProxy = (ComponentOccurrenceProxy)aBomRow.ComponentOccurrences[1];

                    if (componentProxy.NativeObject.OccurrencePropertySetsEnabled)
                    //if (aBomRow.ComponentOccurrences[1].OccurrencePropertySetsEnabled)
                    //if (aBomRow.ComponentOccurrences[1].OccurrencePropertySetsEnabled)
                    {
                        MessageBox.Show("b");
                        MessageBox.Show("Spare Min: " + componentProxy.NativeObject.OccurrencePropertySets[1]["SpareMin"].Value.ToString());
                    }
                    else
                    {
                        MessageBox.Show("b2");
                        MessageBox.Show("Spare Min: " + componentProxy.NativeObject.OccurrencePropertySets[1]["SpareMin"].Value.ToString());
                    }                    
                }

 

After that, you finally have access to NativeObject.

0 Likes