Hi @jeremytammik,
Thanks for having a look at my code!
If I understand what you mean I guess the confusing parts are two:
1) You don't need to create a list of RevitLinkType and therefore you don't need to initially create an ICollection of elements
2) Zipping the name of the link to be reloaded with its file path.
I'll try to explain why I did it:
1) I found it confusing as well but I was taking it from another script and I copied as it was because I thought it was the way of doing it...I know it's a very bad way of doing and learning things. I'll try to avoid it in the future
Although, now I modified the program with your suggestion of using the IsLoaded method (thanks for that one as well!!) and I'm now it's a lot more efficient cause I can select whatever file from the browser but Revit is going to reload only the ones that are Not Loaded skipping the ones that don't need reloading. But to use the IsLoaded I need a RevitLinkType object otherwise it says that the method doesn't exist for other types. Is it correct?
2) I might have overworried for this last part but the reason why I Zipped a tuple and then loaded by items at the same index is because I was worried that my program could have associated the wrong file path to the file to be reloaded, hence finding that, for example, it was reloading the file named Link1 from the path C:\user\Links\Link2.rvt...does it make sense?
Looking at how you did this last part I believe it actually does the same thing without Zipping. I swapped my code with yours and it actually works as expected without mixing up files and file paths. The only problem is that it gives me an exception saying: "Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index" Even though it then works correctly.
Did I understand what you meant?
Here is how I modified the code thanks to the IsLoaded method:
namespace BatchReloadRVTLinks
{
[Transaction(TransactionMode.Manual)]
public class Command : IExternalCommand
{
public Result Execute(
ExternalCommandData commandData,
ref string message,
ElementSet elements)
{
// Get application and document objects
UIApplication uiapp = commandData.Application;
UIDocument uidoc = uiapp.ActiveUIDocument;
Autodesk.Revit.ApplicationServices.Application app = uiapp.Application;
Autodesk.Revit.DB.Document doc = uidoc.Document;
// NO TRANSACTION NEEDS TO BE OPENED
try
{
using (Transaction tx = new Transaction(doc))
{
// Collect files linked in current project
FilteredElementCollector collector = new FilteredElementCollector(doc);
ICollection<Element> linkInstances = collector.OfClass(typeof(RevitLinkType)).ToElements();
// Check which elements are loaded > to be used as filter
List<bool> loaded = new List<bool>();
foreach (RevitLinkType i in linkInstances)
{
loaded.Add(RevitLinkType.IsLoaded(doc, i.Id));
}
// Convert ICollection into a list of RevitLinkTypes
int i1 = 0;
List<RevitLinkType> revLinkType = new List<RevitLinkType>();
foreach (RevitLinkType rli in linkInstances)
{
if (!loaded[i1++])
{
revLinkType.Add(rli);
}
}
// Put names of linked files into a list of strings
int i2 = 0;
List<string> linkNames = new List<string>();
foreach (Element eli in linkInstances)
{
if (!loaded[i2++])
{
linkNames.Add(eli.Name.Split(' ')[0]);
}
}
// Prompt user with files selection dialog
Start:
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.InitialDirectory = (@"P:\");
openFileDialog1.Filter = "RVT|*.rvt";
openFileDialog1.Multiselect = true;
openFileDialog1.RestoreDirectory = true;
// If you select the files and hit OK (in the file browser)
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
// Show which files (path + version) has been selected before linking them
StringBuilder userSelectionWVersion = new StringBuilder();
foreach (string fp in openFileDialog1.FileNames)
{
userSelectionWVersion.AppendLine(
fp.ToString()
+ " which was created with " +
BasicFileInfo.Extract(fp).SavedInVersion.ToString().ToUpper());
}
// Recap the user with his selection + Revit version of the file
DialogResult linkCorrect = MessageBox.Show(
userSelectionWVersion.ToString(),
"You selected the files:",
MessageBoxButtons.OKCancel);
// Put paths of files selected by user into a list
if (linkCorrect == DialogResult.OK)
{
List<string> userSelectionNames = new List<string>();
foreach (string fp in openFileDialog1.FileNames)
{
userSelectionNames.Add(fp.ToString());
}
// Check which of the files that the user selected have the same name of the files linked in the project
IEnumerable<string> elementsToReload = userSelectionNames.Where(a => linkNames.Exists(b => a.Contains(b)));
// Show which files need to be reloaded
StringBuilder intersection = new StringBuilder();
foreach (string fp in elementsToReload)
{
intersection.AppendLine(fp.ToString());
}
DialogResult promptToLoad = MessageBox.Show(intersection.ToString(), "The following files need to be roloaded");
// Initialize + populate list of ModelPaths > path from where to reload
List<ModelPath> modPaths = new List<ModelPath>();
foreach (string fp in elementsToReload)
{
FileInfo filePath = new FileInfo(fp);
ModelPath linkpath = ModelPathUtils.ConvertUserVisiblePathToModelPath(filePath.ToString());
modPaths.Add(linkpath);
}
// Zip together file (as RevitLinkType) and the corresponding path to be reloaded from > Reload
foreach (var ab in revLinkType.Zip(modPaths, Tuple.Create))
{
ab.Item1.LoadFrom(ab.Item2, new WorksetConfiguration());
}
}
return Result.Succeeded;
}
catch (Exception ex)
{
// If something went wrong return Result.Failed
DialogResult genericException = MessageBox.Show(ex.Message, "Oops there was problem!");
return Result.Failed;
}
}
}
}
Does it make sense?
Thanks for the help!
Andrea