I am trying to create a plugin inside the Revit which supposed to search on some categories(the catgories i get from an Excel sheet according to the elements) and then add new shared paramters to them , but what i found out that when i run the code it only add those new paramters to the last element was in the Excel sheet, not all the elements, and if i remove this elements, the changes will get applied only on the last element not the previou elements in the last, i dunno if its about the transaction or what exactly.
this is where is my transaction happens:
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
// Initialize Revit document
UIDocument uiDoc = commandData.Application.ActiveUIDocument;
Document doc = uiDoc.Document;
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
// Set the EPPlus license context
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
// Open file explorer to select the Excel file
OpenFileDialog openFileDialog = new OpenFileDialog
{
Filter = "Excel Files|*.xlsx",
Title = "Select the Excel File with Parameters"
};
if (openFileDialog.ShowDialog() != true)
{
// If no file is selected, cancel the command
message = "No file was selected.";
return Result.Cancelled;
}
string filePath = openFileDialog.FileName;
FileInfo fileInfo = new FileInfo(filePath);
using (ExcelPackage package = new ExcelPackage(fileInfo))
{
ExcelWorksheet worksheet = package.Workbook.Worksheets[0];
int rowCount = worksheet.Dimension.Rows;
// Start the main transaction once
using (Transaction trans = new Transaction(doc, "Add Parameters from Excel"))
{
if (trans.Start() == TransactionStatus.Started)
{
// Process each row in the Excel sheet
for (int row = 2; row <= rowCount; row++) // Assuming first row is headers
{
string serialNumber = worksheet.Cells[row, 6].Text;
if (!string.IsNullOrEmpty(serialNumber))
{
Element element = FindElementBySerialNumber(doc, serialNumber);
if (element != null && element.Category.AllowsBoundParameters)
{
// Bind and apply parameters for this specific element
for (int col = 1; col <= 5; col++) // Assuming parameters are in columns 1 to 5
{
string paramName = worksheet.Cells[1, col].Text; // Get parameter name from header row
string paramValue = worksheet.Cells[row, col].Text; // Get parameter value for this element
if (!string.IsNullOrEmpty(paramName) && !string.IsNullOrEmpty(paramValue))
{
// Bind the parameter dynamically to the element's category
BindSharedParameterToProject(commandData.Application, doc, paramName, element.Category);
// Assign the parameter value to the element
Parameter param = element.LookupParameter(paramName);
if (param != null && !param.IsReadOnly)
{
param.SetValueString(paramValue.Trim());
}
}
}
}
}
}
// Commit the transaction after all elements are processed
trans.Commit();
}
else
{
trans.RollBack();
}
}
}
// Show a confirmation message to the user
TaskDialog.Show("Success", "Parameters have been successfully created and assigned.");
return Result.Succeeded;
}
and here’s my binding method for adding new shared parameter :
private void BindSharedParameterToProject(UIApplication uiApp, Document doc, string paramName, Category elementCategory)
{
// Check if the category allows bound parameters
if (!elementCategory.AllowsBoundParameters)
{
TaskDialog.Show("Category Error", $"The category '{elementCategory.Name}' does not allow bound parameters.");
return; // Skip binding if the category doesn't support parameters
}
// Ensure a shared parameter file is set
if (string.IsNullOrEmpty(uiApp.Application.SharedParametersFilename))
{
// If no shared parameter file is set, create one
string sharedParamsFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "RevitSharedParams.txt");
uiApp.Application.SharedParametersFilename = sharedParamsFile;
}
DefinitionFile defFile = uiApp.Application.OpenSharedParameterFile();
if (defFile == null)
{
throw new InvalidOperationException("No shared parameter file is set or accessible.");
}
DefinitionGroup defGroup = defFile.Groups.get_Item("Orbits Parameters") ?? defFile.Groups.Create("Orbits Parameters");
// Check if the parameter already exists in the group
Definition paramDef = defGroup.Definitions.get_Item(paramName);
if (paramDef == null)
{
ForgeTypeId dataType = SpecTypeId.String.Text; // Assuming parameter type is string
ExternalDefinitionCreationOptions options = new ExternalDefinitionCreationOptions(paramName, dataType);
paramDef = defGroup.Definitions.Create(options);
}
// Bind the parameter to the element's specific category (dynamically)
CategorySet categorySet = new CategorySet();
categorySet.Insert(elementCategory); // Bind to the specific element's category
// Use instance binding (assuming you want instance parameters)
InstanceBinding instanceBinding = uiApp.Application.Create.NewInstanceBinding(categorySet);
BindingMap bindingMap = doc.ParameterBindings;
ForgeTypeId groupTypeId = GroupTypeId.General;
if (!bindingMap.Contains(paramDef))
{
bindingMap.Insert(paramDef, instanceBinding, groupTypeId); // Insert to bind the parameter
}
else
{
bindingMap.ReInsert(paramDef, instanceBinding, groupTypeId); // ReInsert to ensure the binding is correct
}
}
i tried to make the commit ater each element i add the paramter to it but still it just add the parameters only to the last element in the list
i tried to do sub-transaction also tried to do transaction after each element but didn’t work