Updating Family Type Parameters
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
I am still relatively new to coding and the Revit API, and I am working on a program that uses a windows form to display the user with parameters from a family type, make changes and then save those changes back to the type. I have everything working up until actually applying the changes to the type. From everything I can find, it seems like I am following samples of similar code to a t, but once the transaction commits, the family type is not updating like I would expect (nothing is changing).
The debug statements I have "MessageBox.Show("Changing " + checkBoxEntry.Value.ToString() + " to " + checkboxValue);" shows the correct parameter name, so I am not sure what exactly is going wrong.
Here is the code I am using to get all the parameters
private void comboBoxFamilyType_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBoxFamilyType.SelectedItem is FamilyTypeItem selectedFamilyType)
{
// Use a filtered element collector to find all instances of the selected family type
var collector = new FilteredElementCollector(_doc)
.OfClass(typeof(FamilyInstance))
.WhereElementIsNotElementType()
.Where(x => ((FamilyInstance)x).Symbol.Id == selectedFamilyType.Id);
// Attempt to retrieve the first instance found; this will be used to access the family data
var selectedInstance = collector.FirstOrDefault() as FamilyInstance;
if (selectedInstance != null)
{
changesTracker.Clear();
Family family = selectedInstance.Symbol.Family; // Retrieve the family from the instance symbol
familyDoc = _doc.EditFamily( family ); // Open the family for editing which returns the family document
familyManager = familyDoc.FamilyManager; // Get the family manager which manages the parameters of the family
// Retrieve and print the number of parameters in the family
int n = familyManager.Parameters.Size;
//Debug.Print("\nFamily {0} has {1} parameter{2}", _doc.Title, n, Util.PluralSuffix( n ) );
// Create a dictionary to map parameter names to their corresponding FamilyParameter objects
Dictionary<string, FamilyParameter> fps = new Dictionary<string, FamilyParameter>(n);
foreach (FamilyParameter fp in familyManager.Parameters)
{
string name = fp.Definition.Name;
fps.Add (name, fp );
}
// Sort the keys (parameter names) for better manageability
List<string> parameters = new List<string>( fps.Keys );
parameters.Sort();
// Print the number of types in the family and their names
n = familyManager.Types.Size;
//Debug.Print("\nFamily {0} has {1} type{2}{3}", _doc.Title, n, Util.PluralSuffix(n), Util.DotOrColon(n));
string matchName = selectedFamilyType.ToString()
.Replace("DD Mirror: ", "")
.Replace("DD Adjust: ", "");
Dictionary<string, string> parameterValues = new Dictionary<string, string>(); // Dictionary to store parameter values
// Iterate through each type in the family
foreach (Autodesk.Revit.DB.FamilyType type in familyManager.Types)
{
// Get the match name and remove "DD Mirror: " or "DD Adjust: " from it
if (type.Name == matchName)
{
activeFamilyType = type;
// For each parameter key, check if the type has a value set and print it
foreach (string key in parameters)
{
FamilyParameter fp = fps[key];
if (type.HasValue(fp))
{
string value = Util.FamilyParamValueString(type, fp, _doc);
parameterValues[key] = value; // Store the parameter value in the dictionary
//Debug.Print(" {0} = {1}", key, value);
}
}
}
else
{
continue;
}
}
// Temporarily disable change tracking
DisableChangeTracking();
// Set dimension text box values
foreach (var entry in ControlMappings.TextBoxDimensionMappings)
{
var textBox = this.Controls.Find(entry.Key, true).FirstOrDefault() as System.Windows.Forms.TextBox;
if (textBox != null)
{
textBox.Text = parameterValues.ContainsKey(entry.Value) ? parameterValues[entry.Value] : "Error";
}
}
// Set text box values
foreach (var entry in ControlMappings.TextBoxMappings)
{
var textBox = this.Controls.Find(entry.Key, true).FirstOrDefault() as System.Windows.Forms.TextBox;
if (textBox != null)
{
textBox.Text = parameterValues.ContainsKey(entry.Value) ? parameterValues[entry.Value] : "Error";
}
}
// Set checkbox values
foreach (var entry in ControlMappings.CheckBoxMappings)
{
var checkBox = this.Controls.Find(entry.Key, true).FirstOrDefault() as System.Windows.Forms.CheckBox;
if (checkBox != null)
{
checkBox.Checked = parameterValues.ContainsKey(entry.Value) && parameterValues[entry.Value] == "1";
}
}
SetupChangeTracking();
}
else
{
MessageBox.Show("No instances of the selected family type were found.");
}
}
}
Here is the Change Tracking Method
private void SetupChangeTracking()
{
// Initiate recursive setup from the top-level form controls
SetupControlTracking(this);
}
private void SetupControlTracking(System.Windows.Forms.Control control)
{
foreach (System.Windows.Forms.Control child in control.Controls)
{
if (child is System.Windows.Forms.TextBox textBox)
{
string originalText = textBox.Text; // Store initial text
textBox.Tag = originalText; // Use Tag to store the original value
EventHandler textBoxHandler = (s, e) =>
{
if (textBox.Text != originalText)
{
changesTracker[textBox.Name] = textBox.Text;
originalText = textBox.Text; // Update the original text after the change
}
};
textBox.Leave += textBoxHandler;
textBoxDelegates[textBox] = textBoxHandler;
}
else if (child is CheckBox checkBox)
{
bool originalState = checkBox.Checked; // Store initial state
checkBox.Tag = originalState; // Use Tag to store the original value
EventHandler checkBoxHandler = (s, e) =>
{
if (checkBox.Checked != originalState)
{
changesTracker[checkBox.Name] = checkBox.Checked ? "1" : "0";
originalState = checkBox.Checked; // Update the original state after the change
}
};
checkBox.CheckedChanged += checkBoxHandler;
checkBoxDelegates[checkBox] = checkBoxHandler;
}
// Recursively handle child controls
if (child.HasChildren)
{
SetupControlTracking(child);
}
}
}
Here is the method calling the IExternalEventHandler from the form
public partial class MainFormEditor : System.Windows.Forms.Form
{
//private Dictionary<string, string> originalParameterValues; // Dictionary to store parameter values
private Dictionary<string, string> changesTracker = new Dictionary<string, string>();
private Document _doc;
private Document familyDoc; // Add class-level family document
private FamilyManager familyManager; // Add class-level family manager
private ElementId _initialSelectedId = null; // If a DualDeck is selected in the document when the program is launched, store it here
private FamilyType activeFamilyType = null;
private ExternalEvent exEvent;
private ParameterUpdateHandler handler;
private Dictionary<System.Windows.Forms.Control, EventHandler> textBoxDelegates = new Dictionary<System.Windows.Forms.Control, EventHandler>();
private Dictionary<System.Windows.Forms.Control, EventHandler> checkBoxDelegates = new Dictionary<System.Windows.Forms.Control, EventHandler>();
public MainFormEditor(Document doc, UIDocument uidoc)
{
InitializeComponent();
_doc = doc;
InitializeDualDeckSelection(uidoc);
comboBoxFamilyType.SelectedIndexChanged += comboBoxFamilyType_SelectedIndexChanged; // Attach the event handler for updating the data based on DualDeck selection
textBoxDD_Depth.Leave += textBoxDD_Depth_Leave;
//SetupChangeTracking();
handler = new ParameterUpdateHandler(); // Initially empty, setup later
exEvent = ExternalEvent.Create(handler);
//btnSave.Click += btnSave_Click;
}
private void btnSave_Click(object sender, EventArgs e)
{
Debug.Print("Changes to save: " + changesTracker.Count);
if (familyDoc != null && familyManager != null)
{
// Update the handler with current documents and parameters
handler.Setup(familyDoc, familyManager, activeFamilyType, changesTracker);
}
exEvent.Raise();
}
}
And finally here is the external event handler
public class ParameterUpdateHandler : IExternalEventHandler
{
private Document doc;
private FamilyManager familyManager;
private FamilyType currentFamilyType;
private Dictionary<string, string> changesTracker;
public void Setup(Document doc, FamilyManager manager, FamilyType familyType , Dictionary<string, string> tracker)
{
this.doc = doc;
this.familyManager = manager;
this.changesTracker = new Dictionary<string, string>(tracker);
this.currentFamilyType = familyType; // Setup with current family type
}
public void Execute(UIApplication app)
{
MessageBox.Show("Executing update with changes count: " + changesTracker.Count); // Check if this shows and has count > 0
using (Transaction tx = new Transaction(doc, "Update Parameters"))
{
tx.Start();
// Process text box changes
foreach (var textBoxEntry in ControlMappings.TextBoxDimensionMappings.Concat(ControlMappings.TextBoxMappings))
{
if (changesTracker.TryGetValue(textBoxEntry.Key, out string newValue))
{
var parameter = familyManager.get_Parameter(textBoxEntry.Value);
if (parameter != null)
{
UpdateFamilyParameter(parameter, newValue);
}
}
}
// Process checkbox changes
foreach (var checkBoxEntry in ControlMappings.CheckBoxMappings)
{
if (changesTracker.TryGetValue(checkBoxEntry.Key, out string newCheckValue))
{
var parameter = familyManager.get_Parameter(checkBoxEntry.Value);
if (parameter != null)
{
int checkboxValue = newCheckValue == "1" ? 1 : 0;
MessageBox.Show("Changing " + checkBoxEntry.Value.ToString() + " to " + checkboxValue);
UpdateFamilyParameter(parameter, checkboxValue.ToString());
}
}
}
try
{
MessageBox.Show("Committing Tx");
// parameter setting code
tx.Commit();
}
catch (Exception ex)
{
MessageBox.Show("Failed to Commit. Rolling back.");
Debug.Print("Failed to commit transaction: " + ex.Message);
tx.RollBack();
}
}
}
private void UpdateFamilyParameter(FamilyParameter parameter, string value)
{
if (parameter.StorageType == StorageType.String)
{
MessageBox.Show("Parameter type: String. Updating Paramater: " + parameter.ToString() + " as " + value);
familyManager.Set(parameter, value);
}
else if (parameter.StorageType == StorageType.Integer)
{
MessageBox.Show("Parameter type: Integer. Updating Paramater: " + parameter.ToString() + " as " + value);
if (int.TryParse(value, out int intValue))
{
familyManager.Set(parameter, intValue);
}
}
else if (parameter.StorageType == StorageType.Double)
{
MessageBox.Show("Parameter type: Double. Updating Paramater: " + parameter.ToString() + " as " + value);
if (double.TryParse(value, out double doubleValue))
{
familyManager.Set(parameter, doubleValue);
}
}
// Add other storage types handling as needed
}
public string GetName()
{
return "Parameter Update Handler";
}
}