using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Windows.Forms; using Microsoft.VisualBasic.FileIO; // For SpecialDirectories // using Autodesk.Revit.ApplicationServices; using Autodesk.Revit.UI; using Autodesk.Revit.DB; // using AppRvt = Autodesk.Revit.ApplicationServices.Application; // to avoid ambiguities using DocRvt = Autodesk.Revit.DB.Document; // to avoid ambiguities using BindingRvt = Autodesk.Revit.DB.Binding; // to avoid ambiguities namespace MS.Revit.Utils { /// /// Major Helper Class for Revit Params /// public static class HelperParams { public enum BindSharedParamResult { eAlreadyBound, eSuccessfullyBound, eWrongParamType, //eWrongCategory, //not exposed //eWrongVisibility, //not exposed eWrongBindingType, eFailed } /// /// Get Element Parameter *by name*. By defualt NOT case sensitive. Use overloaded one if case sensitive needed. /// /// /// /// public static Parameter GetElemParam(Element elem, string name) { return GetElemParam(elem, name, false); } public static Parameter GetElemParam(Element elem, string name, bool matchCase) { StringComparison comp = StringComparison.CurrentCultureIgnoreCase; if (matchCase) comp = StringComparison.CurrentCulture; foreach (Parameter p in elem.Parameters) { if (p.Definition.Name.Equals(name, comp)) return p; } // if here, not found return null; } /// /// Get or Create Shared Params File /// /// /// public static DefinitionFile GetOrCreateSharedParamsFile(AppRvt app) { string fileName = string.Empty; try // generic { // Get file fileName = app.SharedParametersFilename; // Create file if not set yet (ie after Revit installed and no Shared params used so far) if (string.Empty == fileName) { fileName = SpecialDirectories.MyDocuments + "\\MyRevitSharedParams.txt"; StreamWriter stream = new StreamWriter(fileName); stream.Close(); app.SharedParametersFilename = fileName; } return app.OpenSharedParameterFile(); } catch(Exception ex) { MessageBox.Show("ERROR: Failed to get or create Shared Params File: " + ex.Message); return null; } } /// /// Get or Create Shared Parameters Group /// /// /// /// public static DefinitionGroup GetOrCreateSharedParamsGroup(DefinitionFile defFile, string grpName) { try // generic { DefinitionGroup defGrp = defFile.Groups.get_Item(grpName); if (null == defGrp) defGrp = defFile.Groups.Create(grpName); return defGrp; } catch (Exception ex) { MessageBox.Show(string.Format("ERROR: Failed to get or create Shared Params Group: {0}", ex.Message)); return null; } } /// /// Get or Create Shared Parameter Definition /// /// /// used only if creating /// used only if creating /// used only if creating /// public static Definition GetOrCreateSharedParamDefinition(DefinitionGroup defGrp, ParameterType parType, string parName, bool visible) { try // generic { Definition def = defGrp.Definitions.get_Item(parName); if (null == def) def = defGrp.Definitions.Create(parName, parType, visible); return def; } catch (Exception ex) { MessageBox.Show(string.Format("ERROR: Failed to get or create Shared Params Definition: {0}", ex.Message)); return null; } } /// /// Gets or Creates Element's shared param. /// /// Revit Element to get param for /// Parameter Name /// Param Group Name (relevant only when Creation takes place) /// Param Type (relevant only when Creation takes place) /// Param UI Visibility (relevant only when Creation takes place) /// Param Binding: Instance or Type (relevant only when Creation takes place) /// public static Parameter GetOrCreateElemSharedParam(Element elem, string paramName, string grpName, ParameterType paramType, bool visible, bool instanceBinding) { try { // Check if existing Parameter param = GetElemParam(elem, paramName); if (null != param) return param; // If here, need to create it... BindSharedParamResult res = BindSharedParam(elem.Document, elem.Category, paramName, grpName, paramType, visible, instanceBinding); if (res != BindSharedParamResult.eSuccessfullyBound && res != BindSharedParamResult.eAlreadyBound) return null; // If here, binding is OK and param seems to be IMMEDIATELY available from the very same command return GetElemParam(elem, paramName); } catch (Exception ex) { System.Windows.Forms.MessageBox.Show(string.Format("Error in getting or creating Element Param: {0}", ex.Message)); return null; } } /// /// Gets or Creates Project Information (per-doc) shared param /// /// Revit Document /// Parameter Name /// Param Group Name (relevant only when Creation takes place) /// Param Type (relevant only when Creation takes place) /// Param UI Visibility (relevant only when Creation takes place) /// public static Parameter GetOrCreateProjInfoSharedParam(DocRvt doc, string paramName, string grpName, ParameterType paramType, bool visible) { // Just delegate the call using ProjectInfo Element return GetOrCreateElemSharedParam(doc.ProjectInformation, paramName, grpName, paramType, visible, true); } /// /// /// /// /// /// /// /// /// /// /// public static BindSharedParamResult BindSharedParam(DocRvt doc, Category cat, string paramName, string grpName, ParameterType paramType, bool visible, bool instanceBinding) { try // generic { AppRvt app = doc.Application; // This is needed already here to store old ones for re-inserting CategorySet catSet = app.Create.NewCategorySet(); // Loop all Binding Definitions // IMPORTANT NOTE: Categories.Size is ALWAYS 1 !? For multiple categories, there is really one pair per each // category, even though the Definitions are the same... DefinitionBindingMapIterator iter = doc.ParameterBindings.ForwardIterator(); while (iter.MoveNext()) { Definition def = iter.Key; ElementBinding elemBind = (ElementBinding)iter.Current; // Got param name match if (paramName.Equals(def.Name, StringComparison.CurrentCultureIgnoreCase)) { // Check for category match - Size is always 1! if (elemBind.Categories.Contains(cat)) { // Check Param Type if (paramType != def.ParameterType) return BindSharedParamResult.eWrongParamType; // Check Binding Type if (instanceBinding) { if (elemBind.GetType() != typeof(InstanceBinding)) return BindSharedParamResult.eWrongBindingType; } else { if (elemBind.GetType() != typeof(TypeBinding)) return BindSharedParamResult.eWrongBindingType; } // Check Visibility - cannot (not exposed) // If here, everything is fine, ie already defined correctly return BindSharedParamResult.eAlreadyBound; } // If here, no category match, hence must store "other" cats for re-inserting else { foreach (Category catOld in elemBind.Categories) catSet.Insert(catOld); //1 only, but no index... } } } // If here, there is no Binding Definition for it, so make sure Param defined and then bind it! DefinitionFile defFile = GetOrCreateSharedParamsFile(app); DefinitionGroup defGrp = GetOrCreateSharedParamsGroup(defFile, grpName); Definition definition = GetOrCreateSharedParamDefinition(defGrp, paramType, paramName, visible); catSet.Insert(cat); BindingRvt bind = null; if (instanceBinding) { bind = app.Create.NewInstanceBinding(catSet); } else { bind = app.Create.NewTypeBinding(catSet); } // There is another strange API "feature". If param has EVER been bound in a project (in above iter pairs or even if not there but once deleted), .Insert always fails!? Must use .ReInsert in that case. // See also similar findings on this topic in: http://thebuildingcoder.typepad.com/blog/2009/09/adding-a-category-to-a-parameter-binding.html - the code-idiom below may be more generic: if (doc.ParameterBindings.Insert(definition, bind)) { return BindSharedParamResult.eSuccessfullyBound; } else { if (doc.ParameterBindings.ReInsert(definition, bind)) { return BindSharedParamResult.eSuccessfullyBound; } else { return BindSharedParamResult.eFailed; } } } catch (Exception ex) { MessageBox.Show(string.Format("Error in Binding Shared Param: {0}", ex.Message)); return BindSharedParamResult.eFailed; } //return BindSharedParamResult.eSuccessfullyBound; } } }