Rename Material

Rename Material

Anonymous
Not applicable
1,919 Views
13 Replies
Message 1 of 14

Rename Material

Anonymous
Not applicable

I am trying to rename materials, removing unwanted characters. Here is what I have...

 

            FilteredElementCollector materials = new FilteredElementCollector(doc).OfClass(typeof(Material));

            Transaction trans = new Transaction(doc);
            trans.Start();
          
            foreach (Material mat in materials)
            {                
                if (mat.Name.Contains(".") || mat.Name.Contains("_") || mat.Name.Contains("/") || mat.Name.Contains("\\") || mat.Name.Contains(","))
                {
                    StringBuilder sb = new StringBuilder(mat.Name);

                    sb.Replace("_", "-");
                    sb.Replace(".", "-");
                    sb.Replace(",", "-");
                    sb.Replace("/", "-");
                    sb.Replace("\\", "-");                    
                    
                    mat.Name = sb.ToString();         
                                      
                }
            }            

            trans.Commit();

 this works when testing by sending the original name then the revised name to a text box. But does not work for actually renaming the materials. What am I missing? Thanks. 

0 Likes
1,920 Views
13 Replies
Replies (13)
Message 2 of 14

Anonymous
Not applicable

As usual, posting a dumb question prompts me to rethink and come up with a better solution. The following code works, except there are duplicate named materials already in the model. The creates an exception and rolls back my transaction, I think. It says it renamed so many materials, but they aren't renamed. 

 

UIApplication uiApp;
        UIDocument uiDoc;
        Application app;
        Document doc;

        List<Material> materialList = new List<Material>();
        
        public Form1(ExternalCommandData commandData)
        {
            InitializeComponent();

            uiApp = commandData.Application;
            uiDoc = uiApp.ActiveUIDocument;
            doc = uiDoc.Document;
            app = uiApp.Application;
        }

        public void GetMaterials()
        {
            FilteredElementCollector materials = new FilteredElementCollector(doc).OfClass(typeof(Material));

            foreach (Material material in materials)
            {
                if (material.Name.Contains(".") || material.Name.Contains("_") || material.Name.Contains("/") || material.Name.Contains("\\") || material.Name.Contains(","))
                {
                    materialList.Add(material);
                }
            }

            label1.Text = string.Format("There are {0} total materials", materials.Count().ToString());
            label2.Text = string.Format("{0} of them need to be re named", materialList.Count.ToString());
        }

        public void RenameMaterials()
        {
            Transaction trans = new Transaction(doc, "Rename Materials");
            int count = 0;

            try
            {                
                foreach (Material mat in materialList)
                {
                    ElementId eid = mat.Id as ElementId;
                    Element type = doc.get_Element(eid);

                    if (null != type)
                    {
                        type.Name = CleanName(mat.Name);
                        count++;
                    }
                }

                trans.Commit();
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
                trans.RollBack();
                //throw;
            }

            MessageBox.Show(string.Format("{0} materials sucessfully renamed", count.ToString()));
        }

        public string CleanName(string oldName)
        {
            StringBuilder newName = new StringBuilder(oldName);

            newName.Replace("_", "-");
            newName.Replace(".", "-");
            newName.Replace(",", "-");
            newName.Replace("/", "-");
            newName.Replace("\\", "-");

            return newName.ToString();
        }

 

0 Likes
Message 3 of 14

Anonymous
Not applicable

No. I'm at a loss here. How can I rename a material? Thanks. 

0 Likes
Message 4 of 14

Anonymous
Not applicable

May I suggest:

 

          
                foreach (Material mat in materialList)
                {
                    ElementId eid = mat.Id as ElementId;
                    Element type = doc.get_Element(eid);

                    if (null != type)
                    {
                      mat.Name = CleanName(mat.Name);
                        count++;
                    }
                }

 

0 Likes
Message 5 of 14

Anonymous
Not applicable

You may! However it gives me the same result. In your suggestion, the updated line 'mat.Name = CleanName(mat.Name);' seems to bypass the ElementID and Element calls? I was working from an example from the link to the BuildingCoder below. 

 

In either case, this is what happens...

 

dialog load, collects materials, checks to see which ones have unwanted characters, and makes a list of them.

2 labels display a count from the total collection, and a count of the materials in the unwanted list.

I run that block of code on the list of materials I want to rename, it appears successful.

if I close and re-run the addin, it indicates that no materials have any of those characters in it's name.

but If I check the manage>materials dialog, the unwanted names still exist.

if I sync and re-run the addin, it finds the same number of unwanted names.

 

so I'm missing a step applying the names or committing the transaction or something. And the fact the my code and your suggested code produce the same results indicated that is where the problem is. Thoughts? Thank you for your time.

 

 

 

 

 

 

 

 

BuildingCoder

0 Likes
Message 6 of 14

Anonymous
Not applicable

Try removing this:

 

      }

        public void RenameMaterials()
        {

It looks like you have broken your code down into two blocks. One finds the bad names and adds them to a list and completes. The other one finds the list and renames the bad ones. However..

 

When Rename starts, it finds an empty list, but it doesn't complain because you declare the materialList on Form Load.

 

Removing the above lines will run the two blocks as one, and may give you the result you are looking for.

 

 

0 Likes
Message 7 of 14

Anonymous
Not applicable

Sorry, it looks like I missed something.

 

When I mock up your code and run it, I get the error:

 

"Attempt to modify the model outside of transaction"

 

When I replace this:

            Transaction trans = new Transaction(doc, "Rename Materials");

            int count = 0;

 

With this:

            Transaction trans = new Transaction(doc, "Rename Materials");

            trans.Start();

            int count = 0;

 

It works as expected. (I ran it in 2013.)

 

Are you by chance in TransactionMode.Automatic? Or do you have some error handler that is hiding the problem?

0 Likes
Message 8 of 14

Anonymous
Not applicable

Yes, I just came back to post that. I wasn't starting the transaction. But I also wasnt getting an error about it? Thank you very much for your time and help!

0 Likes
Message 9 of 14

arnostlobel
Alumni
Alumni

Hello bthatcher,

 

I am a bit puzzled about the code you’ve shown here, particular the transaction business, and I would like if you could shed some light on it.

 

In your last email you stated that you indeed failed to start a transaction, but, surprisingly (to me certainly) you did not get any error about it. You are correct; Revit should give you an error. The fact it did not would indicate one of the following causes:

 

  1. Your code runs in an external command that uses automatic transaction mode.
  2. You had another transaction started someplace else in your code and you forgot about it.
  3. The code inside your transaction does not actually change the model (for whatever reason, possibly some error condition.)
  4. Your code accesses Revit API illegally from another thread of modeless dialog.

Could you please tell me which one of the above applies to your case?  (My gut feeling tells me it’s probably #d)

Even with that answered, however, I will still be puzzled, because beside the lack of error returned from Revit upon model modification you should get an exception when attempting to finish the transaction. There are only three possibilities there :

 

  1. If there is no exception in your material-changing code, you should get an exception when you try to commit the un-started transaction.
  2. If there was an exception, yet another exception should be thrown from your catch block upon the attempt to explicitly roll the transaction back. As with the Commit method, a transaction can only be rolled back if it started.
  3. However, if your code accesses Revit API illegally from another thread of modeless dialog there will be no exception, for Revit does not know about your application and will not throw.

So again, which one is it? (And again my gut feeling tells me it’s probably #c)

I would be very thankful if you provide some explanation for the apparent mystery although I sort of expect you to confirm my hunch which is that you are trying to access the Revit API illegally from some external thread or a modeless dialog (without utilizing neither External Events nor Idling events.)

 

Thank you

 

Arnošt Löbel

Autodesk Revit R&D

Arnošt Löbel
0 Likes
Message 10 of 14

Anonymous
Not applicable

I don't know the answers to your questions. I have attached a VisualStudio 2010 template to this thread. The template is a little bloated. I originally worked backward from an example in the SDK samples. I get plenty of errors while working up the solutions I need. Why this transaction error gets past is beyond me. The only thing the Try catches is duplicately named materials (they will show up in the list, but not the Manage Materials dialog, I would live a solution for that by the way) purging the model before running the command will fix that. Comment out line 91 and it should work (not work) as described, the materials will appear renamed until synchronized. Thank you for your interest. I would appreciate you posting back your findings. Thank you.

0 Likes
Message 11 of 14

arnostlobel
Alumni
Alumni

The code you uploaded gave me the answers to my questions. Indeed, you are calling into the API illegally from a modeless Form. That is why Revit does not complain about your modifying the model without starting a transaction first. It did not complain because you probably happened to make the modification when there was another transaction open (by someone else) in the model, or because Revit ignored it thinking some internal code was behaving incorrectly.

Here’s what is happening in your code:

  1. End-user invokes your external command
  2. Inside the Execute method you create a form and show it
  3. You return from the command and the control goes back to Revit (and to the end-user)
  4. Your form is left behind being displayed on the screen. However, you cannot access the API from it in any way, because it is illegal and it is not supported. The only way calling from a modeless dialog (or another external thread) to Revit API is either through a handler of Idling event, or External Event object. Please look for examples of this in the Revit SDK. (Look for ModelessDialog folder.)

Thank you

Arnošt Löbel

Autodesk Revit R&D

Arnošt Löbel
0 Likes
Message 12 of 14

arnostlobel
Alumni
Alumni

(For some reason, my post did not making through; I am posting again. My apologies if they both show up, eventually.)

 

The code you uploaded gave me the answers to my questions. Indeed, you are calling into the API illegally from a modeless Form. That is why Revit does not complain about your modifying the model without starting a transaction first. It did not complain because you probably happened to make the modification when there was another transaction open (by someone else) in the model, or because Revit ignored it thinking some internal code was behaving incorrectly.

Here’s what is happening in your code:

  1. End-user invokes your external command
  2. Inside the Execute method you create a form and show it
  3. You return from the command and the control goes back to Revit (and to the end-user)
  4. Your form is left behind being displayed on the screen. However, you cannot access the API from it in any way, because it is illegal and it is not supported. The only way calling from a modeless dialog (or another external thread) to Revit API is either through a handler of Idling event, or External Event object. Please look for examples of this in the Revit SDK. (Look for ModelessDialog folder.)

Thank you

Arnošt Löbel

Autodesk Revit R&D

Arnošt Löbel
0 Likes
Message 13 of 14

arnostlobel
Alumni
Alumni

OK, they both showed up. Smiley Happy

Arnošt Löbel
0 Likes
Message 14 of 14

Anonymous
Not applicable

I'm guessing the modeless dialog is unintentional.

 

Try changing:

 

form1.Show();

 

to

 

form1.ShowDialog();

 

in Class1.cs

0 Likes