Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Set Different Materials to Parts of a Wall

6 REPLIES 6
SOLVED
Reply
Message 1 of 7
jrlatta
685 Views, 6 Replies

Set Different Materials to Parts of a Wall

I am trying to set different materials to different parts of a wall like shown in the picture below.

RevitForumMaterialtoPartsExample.png

 

My current method allows me to set all the parts to have the same material, but doesn't let me assign each part its own material. Here is my current approach:

1) Get all the parts in the file (this is a controlled file where I know exactly what parts are there)

2) Check whether a part is currently set to material "Facade Material" -- this is how I've organized my wall ahead of time and I only want to change the material of parts of the wall that have material "Facade Material"

3) Set each part's built in parameter "Material by original" to false

4) Set each part's built-in material parameter to my desired one

The issue is, once I set the material parameter for a given part, then it changes the material parameters for all the other parts.  I'm not sure if the concept of instance parameter vs type parameter vs family parameter holds for Parts, but from the object browser in Revit, the material parameter seems to be an "instance" parameter.

IEnumerable<Part> facadePanels2 = new FilteredElementCollector(doc).OfClass(typeof(Part)).Cast<Part>();
                foreach (Part p in facadePanels2)
                { 
                    using (var trans = new Transaction(doc, "Modify1"))
                    {
                        trans.Start();                        
                        Parameter pMat = p.get_Parameter(BuiltInParameter.DPART_MATERIAL_ID_PARAM);                        
                        System.Diagnostics.Debug.Print(pMat.AsValueString());
                        if (pMat.AsValueString() == "Facade Material")
                        {
                            System.Diagnostics.Debug.Print("Facade Material Found");
                            Parameter pOrig = p.get_Parameter(BuiltInParameter.DPART_MATERIAL_BY_ORIGINAL);
                            if (pOrig.IsReadOnly == false)
                            {
                                pOrig.Set(0);
                            }
                            if (pMat.IsReadOnly == false)
                            {
                                ElementId matId = materialIds[getMaterialOfPart(p, panels, viewDefault3D)];
                                
                                pMat.Set(matId);
                            }                            
                        }
                        trans.Commit();
                    }

 

Any thoughts?

Labels (2)
6 REPLIES 6
Message 2 of 7
jeremy_tammik
in reply to: jrlatta

My first thoughts have little to do with your question.

 

First: I mostly recommend to avoid using AsValueString. I prefer the methods that return the raw parameter value: AsInteger, AsDouble, AsElementId, AsString.

 

Secondly: why do you start a new transaction for each part in the loop? Can't you just use one single large transaction and run the loop within that?

 

Regardless of that, though, I see nothing that explains the behaviour you describe, causing all parts to be assigned the same material.

  

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open
Message 3 of 7
jrlatta
in reply to: jrlatta

1) Ok noted, I'll use those functions

2) Lazy coding, I was going to change the transaction to wrap the loop eventually but so far performance hadn't been an issue and I wanted to get it working first.

 

And ok, I'll keep exploring different methods. I've seen this thing be done before so I know it's possible.

Message 4 of 7
RPTHOMAS108
in reply to: jrlatta

What does this line return and what is the function doing?

ElementId matId = materialIds[getMaterialOfPart(p, panels, viewDefault3D)];

How do you verify the matId is what you expect it to be and the line that immediately follows this attempts to set the material to what you are aiming for?

 

Does 'Material by original' parameter value change?

 

Message 5 of 7
jrlatta
in reply to: jrlatta

I figured it out. It had to do with my transactions.

 

Once I wrapped the setting of the two parameters (pOrig and pMat) in their own transactions for each part, it now works properly. It looks like all other parameters get set to read only when you change 1 parameter for a part in a single transaction?

 

Here's the code that's working for those who care:

IEnumerable<Part> facadePanels2 = new FilteredElementCollector(doc).OfClass(typeof(Part)).Cast<Part>();
                foreach (Part p in facadePanels2)
                {
                        
                    Parameter pMat = p.get_Parameter(BuiltInParameter.DPART_MATERIAL_ID_PARAM);
                    if (pMat.AsElementId().IntegerValue == 309859) //("Facade Material")
                    {
                        System.Diagnostics.Debug.Print("Facade Material Found");
                        Parameter pOrig = p.get_Parameter(BuiltInParameter.DPART_MATERIAL_BY_ORIGINAL);
                        if (pOrig.IsReadOnly == false)
                        {
                            using (var trans = new Transaction(doc, "Modify1"))
                            {
                                trans.Start();
                                pOrig.Set(0);
                                trans.Commit();
                            }
                        }
                        if (pMat.IsReadOnly == false)
                        {
                            ElementId matId = materialIds[getMaterialOfPart(p, panels, viewDefault3D)];
                            using (var trans = new Transaction(doc, "Modify1"))
                            {
                                trans.Start();
                                pMat.Set(matId);
                                trans.Commit();
                            }
                        } 
                    }
Message 6 of 7
RPTHOMAS108
in reply to: jrlatta

Yes that makes sense since second parameter would also be greyed out in UI if first is not set to false i.e. the ability to set second parameter is dependant on value of first.

 

This also highlights the importance of order when batch processing built-in parameter values. There are a few add-ins out there that populate parameter values but they probably are not considering such relationships between parameters.

Message 7 of 7
jeremy_tammik
in reply to: jrlatta

Congratulations on solving it and thank you Richard for the explanation.

 

In that case, you can very probably increase performance by using only one transaction after all and replacing the multiple mini-transactions by calls to doc.Regenerate.

 

Yet another of the innumerable examples of the need to regen:

 

https://thebuildingcoder.typepad.com/blog/about-the-author.html#5.33

  

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Rail Community