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: 

Bulk reloading families in a template from a slightly different location

17 REPLIES 17
SOLVED
Reply
Message 1 of 18
EATREVITPOOPCAD
721 Views, 17 Replies

Bulk reloading families in a template from a slightly different location

I have to go through a template and reload all families from a slightly different location. At the moment all of the families loaded in the template go something like this

 

server\Revit_Library\2022\family1.rfa

server\Revit_Library\2022\family2.rfa

server\Revit_Library\2022\family3.rfa

 

and I need to change them to

 

server\Revit_Library\2023\family1.rfa

server\Revit_Library\2023\family2.rfa

server\Revit_Library\2023\family3.rfa

 

Is there a better way to do this instead of UI? It would be too difficult in a short amount of time (I think) for me to code something that would do this, but I wonder If there is a way I could see that data outside of Revit. (Like if open a Revit file with 7 zip you can change some stuff that way...)

The definition of insanity is doing the same thing over and over again and expecting different results
17 REPLIES 17
Message 2 of 18

Dear EatRevitPoopCad,

 

I searched the Internet for 'revit api reload family path' and see some promising results there:

 

https://duckduckgo.com/?q=revit+api+reload+family+path

 

Examining them more closely, it seems that only Sean's answer here in the forum is relevant and helpful:

  

https://forums.autodesk.com/t5/revit-api-forum/family-reload-from/td-p/9521770

 

> Try using the overloaded. Ersionnof the LoadFamily() method that allows you to specify options like overwrite Parameters.

 

I think he means overloaded version of LoadFamily:

 

https://www.revitapidocs.com/2023/cb950c8e-f440-c6db-8563-d1dd16ef3fee.htm

 

Add an IFamilyLoadOptions instance to your call. Note the remarks:

 

> If the family is not loaded, or if the family is loaded but unchanged, the situation will never trigger and OnFamilyFound(Boolean, Boolean ) and OnSharedFamilyFound(Family, Boolean, FamilySource , Boolean ) will not be called. Only if the family is loaded and changed should the interface methods be called.

  

I think if you have more than a couple of dozen families to update, you will be faster doing it with the API than by hand.

  

Maybe you can whip up a quick macro instead of a full-fledged add-in.

    

Cheers,

  

Jeremy

  

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open
Message 3 of 18

@jeremy_tammik you're doing God's work helping people such as my self 😁 Looked up Revit macros, never knew they were c# code... Will do a leap of faith and tackle this via API

The definition of insanity is doing the same thing over and over again and expecting different results
Message 4 of 18

@jeremy_tammik This is why I was going to do this manually...

 

Something strange but the macro IDE says I did not implement IFamilyLoadOptions correctly, but this is what I saw in an example and API docs seem to say the same thing.

 

'MW_Module.FamilyOption' does not implement interface member 'Autodesk.Revit.DB.IFamilyLoadOptions.OnSharedFamilyFound(Autodesk.Revit.DB.Family, bool, out Autodesk.Revit.DB.FamilySource, out bool)' (CS0535) it\Macros\2023\Revit\AppHookup\MW_Module\Source\MW_Module\ThisApplication.cs:25,8

 

'MW_Module.FamilyOption' does not implement interface member 'Autodesk.Revit.DB.IFamilyLoadOptions.OnFamilyFound(bool, out bool)' (CS0535) - 

 

using System;
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Reflection;
using System.ComponentModel;
using System.Collections;
using System.Windows;

using System.IO;

namespace MW_Module
{
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    [Autodesk.Revit.DB.Macros.AddInId("B3515362-619A-49B7-AC79-3428485161C5")]

	
    public partial class ThisApplication
	{
    	
		private void Module_Startup(object sender, EventArgs e)
		{

		}

		private void Module_Shutdown(object sender, EventArgs e)
		{

		}

		#region Revit Macros generated code
		private void InternalStartup()
		{
			this.Startup += new System.EventHandler(Module_Startup);
			this.Shutdown += new System.EventHandler(Module_Shutdown);
		}
		#endregion
		
		public void Hello_World()
		{
			TaskDialog.Show("Hello","World");
		}
		
		public void Repath_All_Fam()
		{
			
			Document doc = this.ActiveUIDocument.Document;   
    		UIApplication uiapp = new UIApplication(doc.Application);
	
	        FilteredElementCollector collector = new FilteredElementCollector(doc);
	        ICollection<Element> elements = collector.OfClass(typeof(Family)).ToElements();
	
	        StringBuilder sb = new StringBuilder();
	
	        foreach (Element el in elements)
	        {
	        	Family f = el as Family;
	        	Document famDoc = doc.EditFamily(f);
	        	sb.AppendLine(f.Name.ToString() + "\n" + famDoc.PathName);
	        	famDoc.Close();
	        }
	
	        TaskDialog.Show("part 1", sb.ToString());
	        
	       
	 
		}
	}
}

 

Another issue I am having is getting Family File Path... kind of been just shooting darts at the board. Can't find any family information about the file path with the Lookup Revit plugin.

 

 

The definition of insanity is doing the same thing over and over again and expecting different results
Message 5 of 18

Thank you for your kind appreciation. I do my best and pray we all do.

 

IFamilyLoadOptions defines an interface, indicated by the capital I prefix:

 

https://duckduckgo.com/?q=.net+interface

 

You need to implement a class that derives from it and implements the required members. 

 

https://www.google.com/search?q=IFamilyLoadOptions&as_sitesearch=thebuildingcoder.typepad.com

   

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open
Message 6 of 18

https://thebuildingcoder.typepad.com/blog/2011/06/reloading-a-family.html

 

This is the article I post I was reading. Apologize for looking like a lost cause. It was late at night and I pasted the wrong code in my previous post... But my question still remains the same. 

 

 

It seems I implemented a family option class way I am supposed to but the error says I did not - and gives me the same required implementation variables

 

'MW_Module.FamilyOption' does not implement interface member 'Autodesk.Revit.DB.IFamilyLoadOptions.OnSharedFamilyFound(Autodesk.Revit.DB.Family, bool, out Autodesk.Revit.DB.FamilySource, out bool)' (CS0535) it\Macros\2023\Revit\AppHookup\MW_Module\Source\MW_Module\ThisApplication.cs:25,8

 

'MW_Module.FamilyOption' does not implement interface member 'Autodesk.Revit.DB.IFamilyLoadOptions.OnFamilyFound(bool, out bool)' (CS0535) - 

 

using System;
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Reflection;
using System.ComponentModel;
using System.Collections;
using System.Windows;

using System.IO;

namespace MW_Module
{
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    [Autodesk.Revit.DB.Macros.AddInId("B3515362-619A-49B7-AC79-3428485161C5")]

	
    class FamilyOption : IFamilyLoadOptions
	{
	  public bool OnFamilyFound(
	    bool familyInUse,
	    ref bool overwriteParameterValues )
	  {
	    overwriteParameterValues = true;
	    return true;
	  }
	 
	  public bool OnSharedFamilyFound(
	    Family sharedFamily,
	    bool familyInUse,
	    ref FamilySource source,
	    ref bool overwriteParameterValues )
	  {
	    return true;
	  }
	}
    
    public partial class ThisApplication
	{
    	
		private void Module_Startup(object sender, EventArgs e)
		{

		}

		private void Module_Shutdown(object sender, EventArgs e)
		{

		}

		#region Revit Macros generated code
		private void InternalStartup()
		{
			this.Startup += new System.EventHandler(Module_Startup);
			this.Shutdown += new System.EventHandler(Module_Shutdown);
		}
		#endregion
		
		public void Hello_World()
		{
			TaskDialog.Show("Hello","World");
		}
		
		public void Repath_All_Fam()
		{
			
			Document doc = this.ActiveUIDocument.Document;   
    		UIApplication uiapp = new UIApplication(doc.Application);
	
	        FilteredElementCollector collector = new FilteredElementCollector(doc);
	        ICollection<Element> elements = collector.OfClass(typeof(Family)).ToElements();
	
	        StringBuilder sb = new StringBuilder();
	
	        foreach (Element el in elements)
	        {
	        	Family f = el as Family;
	        	sb.AppendLine(f.Name.ToString());
	        	
	        	//Document famDoc = doc.EditFamily(f);
	        	//sb.AppendLine(f.Name.ToString() + "\n" + famDoc.PathName);
	        	//famDoc.Close();
	        }
	
	        TaskDialog.Show("Family Names", sb.ToString());
	        
	       
	 
		}
	}
}

 

The definition of insanity is doing the same thing over and over again and expecting different results
Message 7 of 18

It looks like you're using ref instead of out.

Message 8 of 18

That was my thought too. However when I change out to ref I get another error for the same lines of code:

 

 

The out parameter 'overwriteParameterValues' must be assigned to before control leaves the current method (CS0177) - C:\ProgramData\Autodesk\Revit\Macros\2023\Revit\AppHookup\MW_Module\Source\MW_Module\ThisApplication.cs:37,6

The out parameter 'overwriteParameterValues' must be assigned to before control leaves the current method (CS0177) - C:\ProgramData\Autodesk\Revit\Macros\2023\Revit\AppHookup\MW_Module\Source\MW_Module\ThisApplication.cs:37,6

 

 

 

My understanding is that when you create an interface you use out for variables that would be declared later but when you implement them you use ref 

 

I am going to try this in Visual Studio instead of Revit's macro IDE, maybe I will see something I don't here. If not, I will save this battle for next year when upgrading the library and do this old fashion way for now...

 

EDIT: >.< man now is not the time for thebuildingcoder website to be down

The definition of insanity is doing the same thing over and over again and expecting different results
Message 9 of 18

out and ref are not related to interfaces and implementations. An out parameter is effectively a second return value. Therefore, you need to assign it a value before you return.

Message 10 of 18

I see, gotta do some reading lol thank you

The definition of insanity is doing the same thing over and over again and expecting different results
Message 11 of 18

So I almost got this working using out (it threw me off because all examples I saw used ref...). This code fully compiles and reloads a good chunk of families.

 

However, now I got another issue. Revit says Some Families may be no longer valid and command can not be executed... How can I skip those culprits?  I tried doing in the for loop if (el != null)  but got the same error

 

EATREVITPOOPCAD_1-1671606743075.png

 

 

 

 

 

using Autodesk.Revit.Attributes;
using System;
using Autodesk.Revit.UI;
using Autodesk.Revit.DB;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;


namespace _Reload_Upgraded_Family
{
    class FamilyOption : IFamilyLoadOptions
    {
        public bool OnFamilyFound(
          bool familyInUse,
          out bool overwriteParameterValues)
        {
            overwriteParameterValues = true;
            return true;
        }

        public bool OnSharedFamilyFound(
          Family sharedFamily,
          bool familyInUse,
          out FamilySource source,
          out bool overwriteParameterValues)
        {
            source = FamilySource.Family;
            overwriteParameterValues = true;
            return true;
        }
    }

    [Transaction(TransactionMode.Manual)]
    [Regeneration(RegenerationOption.Manual)]

    public class Command : IExternalCommand
    {
    public Result Execute(
        ExternalCommandData commandData,
        ref string message,
        ElementSet elements
    )
    {
        UIApplication app = commandData.Application;
        Document doc = app.ActiveUIDocument.Document;
        UIDocument uidoc = new UIDocument(doc);

        FilteredElementCollector collector = new FilteredElementCollector(doc);
        ICollection<Element> famElements = collector.OfClass(typeof(Family)).ToElements();

        StringBuilder report = new StringBuilder();

        Transaction trans = new Transaction(doc, "Loading Upgraded Families");

        foreach (Element el in famElements)
        {
            Family f = el as Family;
    
            report.AppendLine(f.Name.ToString());

            if(f.IsEditable)
            {

                    Document familyDoc = doc.EditFamily(f);
                    string fPathCurrent = familyDoc.PathName;
                    string fPathNew = "";
                    string curVersion = "2023";

                    if (fPathCurrent.Contains("\\Revit\\2017\\Families_"))
                    {
                        fPathNew = fPathCurrent.Replace("2017", curVersion);
                        report.AppendLine(fPathNew);
                    }

                    else if (fPathCurrent.Contains("\\Revit\\2018\\Families_"))
                    {
                        fPathNew = fPathCurrent.Replace("2018", curVersion);
                        report.AppendLine(fPathNew);
                    }

                    else if (fPathCurrent.Contains("\\Revit\\2019\\Families_"))
                    {
                        fPathNew = fPathCurrent.Replace("2019", curVersion);
                        report.AppendLine(fPathNew);
                    }

                    else if (fPathCurrent.Contains("\\Revit\\2020\\Families_"))
                    {
                        fPathNew = fPathCurrent.Replace("2020", curVersion);
                        report.AppendLine(fPathNew);
                    }

                    else if (fPathCurrent.Contains("\\Revit\\2021\\Families_"))
                    {
                        fPathNew = fPathCurrent.Replace("2021", curVersion);
                        report.AppendLine(fPathNew);
                    }

                    else
                        report.AppendLine(fPathCurrent);

                    if(fPathNew != "")
                    {
                        //TaskDialog.Show("Debug", "Upgrading Family");
                        if (File.Exists(fPathNew))
                        {
                            trans.Start();
                            Family f2; 
                            doc.LoadFamily(fPathNew, new FamilyOption(), out f2);
                            trans.Commit();
                        }
                    }
             }
        }

        string desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
        File.WriteAllText(desktop + @"\FamilyExport.txt", report.ToString() + "\n\nTotal: " + famElements.Count().ToString());

        //TaskDialog.Show("Family Names", sb.ToString() + "\n\nTotal: " + famElements.Count().ToString());

        TaskDialog.Show("Family Names", "\n\nTotal: " + famElements.Count().ToString());

        return Result.Succeeded;
    }
}

 

 

The definition of insanity is doing the same thing over and over again and expecting different results
Message 12 of 18

You say that Revit says, Some Families may be no longer valid and command can not be executed.

  

'Revit says' is not really clearly defined... Do you mean that an exception is thrown?

  

If so, you can add an exception handler to encapsulate that and handle it. In this case, if you just wish to skip the offending family and continue with the others, you can ignore the exception:

  

https://duckduckgo.com/?q=.net+exception+handling

  

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open
Message 13 of 18

@jeremy_tammik that is exactly what I meant! Appreciate your patience, very satisfying to watch 200+ families reload in 5 minutes 

 

In the for loop I added 

try { // Loop contents } catch{} 

 

The definition of insanity is doing the same thing over and over again and expecting different results
Message 14 of 18

Strange exception since I would expect it relates to hosting or dimensioning

 

Now you just need to reload the nested families perhaps, also close the documents or they remain open after editing. You may notice the impact of that when you eventually close Revit.

 

This is a good reason for templates with minimal loaded families but not always easy to achieve in practice I suppose.

Message 15 of 18

Hmm I didn’t close document 😅 will do that. I guess because running on a decent machine it hasn’t affected performance yet. 

I think it has something to do with nested families also. Basically what I will do is run this command in all of the families in our library first, and then in the template. It should take care of most of the nested families, however for the few that aren’t (when hosting family was upgraded first, then nested) those I can just look in the report generated and update manually. 

the 200 families in the template isn’t even crazy this type of template considering only few doors, windows and etc are actually loaded. Most of it are essential building blocks to streamline our typical construction type. Who ever made it before me put a lot of thought into it. At least it seems like that.

The definition of insanity is doing the same thing over and over again and expecting different results
Message 16 of 18

Yeah it is kind of crazy they didn't think about having an option for relative paths for this kind of thing. In reality the path is stored in the family but not overly important in terms of the existence of that family i.e. it is only really important that when the family is edited from the project it doesn't end up being saved back in the older library. For that you would have thought relative paths from the template location would have been more practical.

Message 17 of 18

A relative path would really help LOL. And yes, that's the only reason we update all of the paths, basically to avoid this possible user error

The definition of insanity is doing the same thing over and over again and expecting different results
Message 18 of 18

Something interesting I noticed when I closed each opened family document, the plugin got noticeably slower than leaving all of the families open in purgatory. I guess it takes time to close it. I will obviously still have it close just because it's the right thing to... Later on I will add a timer and see how much slower it actually is, maybe I am just going crazy 

The definition of insanity is doing the same thing over and over again and expecting different results

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