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...)
Solved! Go to Solution.
Solved by jeremy_tammik. Go to Solution.
Solved by mhannonQ65N2. Go to Solution.
Solved by mhannonQ65N2. Go to Solution.
Solved by jeremy_tammik. Go to Solution.
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 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
@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.
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
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());
}
}
}
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
I see, gotta do some reading lol thank you
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
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;
}
}
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 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{}
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.
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.
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.
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
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