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