using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; using Autodesk.Revit.DB; // ================= DATA HOLDING CLASS ============================================================================================= /// /// This class contains information discovered about a (shared or non-shared) project parameter /// internal class ProjectParameterData { public Definition Definition = null; public ElementBinding Binding = null; public bool IsSharedStatusKnown = false; // Will probably always be true when the data is gathered public bool IsShared = false; public string GUID = null; } // ================= MAIN DATA COLLECTION CODE (HELPER FUNCTIONS ARE LISTED BELOW) ================================================== // Get the (singleton) element that is the ProjectInformation object. It can only have instance // parameters bound to it, but it's always guaranteed to exist. Element projectInfoElement = new FilteredElementCollector(projectDocument).OfCategory(BuiltInCategory.OST_ProjectInformation).FirstElement(); // Get the first wall type element. It can only have type // parameters bound to it, but there's always guaranteed to be at least one of these. Element firstWallTypeElement = new FilteredElementCollector(projectDocument).OfCategory(BuiltInCategory.OST_Walls).WhereElementIsElementType().FirstElement(); CategorySet categories = null; Parameter foundParameter = null; // Get the list of information about all project parameters. List origProjectParametersData = GetProjectParameterData(projectDocument); // In order to be able to query whether or not a project parameter is shared or not, and if it is shared // then what it's GUID is, we must ensure it exists in the Parameters collection of an element. // This is because we can not query this information directly from the project parameter bindings object. // // So each project parameter will attempt to be temporarily bound to a known object so // a Parameter object created from it will exist and can be queried for this additional information. foreach (ProjectParameterData projectParameterData in origProjectParametersData) { if (projectParameterData.Definition != null) { categories = projectParameterData.Binding.Categories; if (!categories.Contains(projectInfoElement.Category)) { // This project parameter is not already bound to the ProjectInformation category, // so we must temporarily bind it so we can query that object for it. using (Transaction tempTransaction = new Transaction(projectDocument, "Temporary")) { tempTransaction.Start(); // Try to bind the project parameter do the project information category. if (AddProjectParameterBinding(projectDocument, projectParameterData, projectInfoElement.Category)) { // successfully bound foundParameter = projectInfoElement.get_Parameter(projectParameterData.Definition); if (foundParameter == null) { // Must be a shared type parameter, which the API reports that it binds // to the project information category via the API, but doesn't // ACTUALLY bind to the project information category. (Sheesh!) // So we must use a different, type based object known to exist, and try again if (!categories.Contains(firstWallTypeElement.Category)) { // Add it to walls category as we did with project info for the others if (AddProjectParameterBinding(projectDocument, projectParameterData, firstWallTypeElement.Category)) { // Successfully bound foundParameter = firstWallTypeElement.get_Parameter(projectParameterData.Definition); } } else { // The project parameter was already bound to the Walls category. foundParameter = firstWallTypeElement.get_Parameter(projectParameterData.Definition); } if (foundParameter != null) { PopulateProjectParameterData(foundParameter, projectParameterData); } else { // Wouldn't bind to the walls category or wasn't found when already bound // This should probably never happen? projectParameterData.IsSharedStatusKnown = false; // Throw exception? } } else { // Found the correct parameter instance on the Project Information object, so use it. PopulateProjectParameterData(foundParameter, projectParameterData); } } else { // The API reports it couldn't bind the parameter to the ProjectInformation category // This only happens with non-shared Project parameters, which have no GUID anyway projectParameterData.IsShared = false; projectParameterData.IsSharedStatusKnown = true; } tempTransaction.RollBack(); } } else { // The project parameter was already bound to the Project Information category. foundParameter = projectInfoElement.get_Parameter(projectParameterData.Definition); if (foundParameter != null) { PopulateProjectParameterData(foundParameter, projectParameterData); } else { // This will probably never happen. projectParameterData.IsSharedStatusKnown = false; // Throw exception? } } } // Whether or not the Definition object could be found } // For each original project parameter definition // ================= HELPER METHODS ====================================================================================== /// /// Returns a list of the objects containing references to the project parameter definitions /// /// The project document being quereied /// private static List GetProjectParameterData(Document projectDocument) { // Following good SOA practices, first validate incoming parameters if (projectDocument == null) { throw new ArgumentNullException("projectDocument"); } if (projectDocument.IsFamilyDocument) { throw new Exception("projectDocument can not be a family document."); } List result = new List(); BindingMap map = projectDocument.ParameterBindings; DefinitionBindingMapIterator it = map.ForwardIterator(); it.Reset(); while (it.MoveNext()) { ProjectParameterData newProjectParameterData = new ProjectParameterData(); newProjectParameterData.Definition = it.Key; newProjectParameterData.Binding = it.Current as ElementBinding; result.Add(newProjectParameterData); } return result; } /// /// This method takes a category and information about a project parameter and /// adds a binding to the category for the parameter. It will throw an exception if the parameter /// is already bound to the desired category. It returns whether or not the API reports that it /// successfully bound the parameter to the desired category. /// /// The project document in which the project parameter has been defined /// Information about the project parameter /// The additional category to which to bind the project parameter /// private static bool AddProjectParameterBinding(Document projectDocument, ProjectParameterData projectParameterData, Category category) { // Following good SOA practices, first validate incoming parameters if (projectDocument == null) { throw new ArgumentNullException("projectDocument"); } if (projectDocument.IsFamilyDocument) { throw new Exception("projectDocument can not be a family document."); } if (projectParameterData == null) { throw new ArgumentNullException("projectParameterData"); } if (category == null) { throw new ArgumentNullException("category"); } bool result = false; CategorySet cats = projectParameterData.Binding.Categories; if (cats.Contains(category)) { // It's already bound to the desired category. Nothing to do. string errorMessage = string.Format("The project parameter '{0}' is already bound to the '{1}' category.", projectParameterData.Definition.Name, category.Name); throw new Exception(errorMessage); } cats.Insert(category); // See if the parameter is an instance or type parameter. InstanceBinding instanceBinding = projectParameterData.Binding as InstanceBinding; if (instanceBinding != null) { // Is an Instance parameter InstanceBinding newInstanceBinding = projectDocument.Application.Create.NewInstanceBinding(cats); if (projectDocument.ParameterBindings.ReInsert(projectParameterData.Definition, newInstanceBinding)) { result = true; } } else { // Is a type parameter TypeBinding typeBinding = projectDocument.Application.Create.NewTypeBinding(cats); if (projectDocument.ParameterBindings.ReInsert(projectParameterData.Definition, typeBinding)) { result = true; } } return result; } /// /// This method populates the appropriate values on a ProjectParameterData object with information from /// the given Parameter object. /// /// The Parameter object with source information /// The ProjectParameterData object to fill private static void PopulateProjectParameterData(Parameter parameter, ProjectParameterData projectParameterDataToFill) { // Following good SOA practices, validat incoming parameters first. if (parameter == null) { throw new ArgumentNullException("parameter"); } if (projectParameterDataToFill == null) { throw new ArgumentNullException("projectParameterDataToFill"); } projectParameterDataToFill.IsSharedStatusKnown = true; projectParameterDataToFill.IsShared = parameter.IsShared; if (parameter.IsShared) { if (parameter.GUID != null) { projectParameterDataToFill.GUID = parameter.GUID.ToString(); } } } // end of PopulateProjectParameterData