Parameter offers a few set methods to assign values to different data types:
Set(ElementId)
Set(Double)
Set(Int32)
Set(String)
Obviously, the first signature is used to assign value for Parameter with ElementId StorageType and the last is for String StorageType. The middle two may confuse people: what will happen if a value of 5 is provided to the Set method of a Double StorageType Parameter and what if a value of 5.0 to of Integer StorageType?
Revit Parameter Organizer can organize various Revit parameters such as Shared Parameters, Family Parameters, Project Parameters and Built-in Parameter in many good ways.
Let’s do a simple test:
Wall wall = SelectWall(cmdData.Application.ActiveUIDocument.Selection);
List<Parameter> pLst = (from Parameter p in wall.Parameters select p).ToList();
string str = string.Empty;
//Double StorageType
Parameter baseOffsetP = pLst.First(p => p.Definition.Name == "Base Offset");
str += "\nDouble StorageType:\n";
str += string.Format("Before set:{0} \t\tTo:{1} \tRet:{2} \tAfter set:{3}\n", baseOffsetP.AsDouble(), "2*3", baseOffsetP.Set(2*3), baseOffsetP.AsDouble());
str += string.Format("Before set:{0} \t\tTo:{1} \tRet:{2} \tAfter set:{3}\n", baseOffsetP.AsDouble(), "5", baseOffsetP.Set(5), baseOffsetP.AsDouble());
str += string.Format("Before set:{0} \t\tTo:{1} \tRet:{2} \tAfter set:{3}\n", baseOffsetP.AsDouble(), "\"3.3\"", baseOffsetP.Set("3.3"), baseOffsetP.AsDouble());
str += string.Format("Before set:{0} \t\tTo:{1} \tRet:{2} \tAfter set:{3}\n", baseOffsetP.AsDouble(), "3/1.5", baseOffsetP.Set(3/1.5), baseOffsetP.AsDouble());
//Integer StorageType
Parameter strucUsageP = pLst.First(p => p.Definition.Name == "Structural Usage");
str += "\nInteger StorageType:\n";
str += string.Format("Before set:{0} \t\tTo:{1} \tRet:{2} \tAfter set:{3}\n", strucUsageP.AsInteger(), "\"2\"", strucUsageP.Set("2"), strucUsageP.AsInteger());
str += string.Format("Before set:{0} \t\tTo:{1} \tRet:{2} \tAfter set:{3}\n", strucUsageP.AsInteger(), "1.0", strucUsageP.Set(1.0), strucUsageP.AsInteger());
str += string.Format("Before set:{0} \t\tTo:{1} \tRet:{2} \tAfter set:{3}\n", strucUsageP.AsInteger(), "3*2.1", strucUsageP.Set(3*2.1), strucUsageP.AsInteger());
//String StorageType
Parameter markP = pLst.First(p => p.Definition.Name == "Mark");
str += "\nString StorageType:\n";
str += string.Format("Before set:{0} \t\tTo:{1} \tRet:{2} \tAfter set:{3}\n", markP.AsString(), "2", markP.Set(2), markP.AsString());
str += string.Format("Before set:{0} \t\tTo:{1} \tRet:{2} \tAfter set:{3}\n", markP.AsString(), "\"3.1\"", markP.Set("3.1"), markP.AsString());
MessageBox.Show(str, "Parameter Set Signatures and Results");
Some information like the following may be outputted:
The results are not as expected or documented, are they? For a Double StorageType Parameter, its Set method can accept an INTEGER argument! For an Integer StorageType Parameter, its Set method accepts a double value, 1.0!
The trickiest part is the return values in all these cases are all TRUE!
It is good that the Parameter.Set() method offers some flexibility but at the same time it brings confusion. Similar situation is with the SetValueString() method.
The following might not make much sense but they do make things clearer:
SetElementId(ElementId)
SetDouble(Double)
SetInteger(Int32)
SetString(String)
If the flexibility has higher priority, a method like the following is obviously better:
public static void SetParameterValue(Parameter p, object value)
{
try
{
if (value.GetType().Equals(typeof(string)))
{
if (p.SetValueString(value as string))
return;
}
switch (p.StorageType)
{
case StorageType.None:
break;
case StorageType.Double:
if (value.GetType().Equals(typeof(string)))
{
p.Set(double.Parse(value as string));
}
else
{
p.Set(Convert.ToDouble(value));
}
break;
case StorageType.Integer:
if (value.GetType().Equals(typeof(string)))
{
p.Set(int.Parse(value as string));
}
else
{
p.Set(Convert.ToInt32(value));
}
break;
case StorageType.ElementId:
if (value.GetType().Equals(typeof(ElementId)))
{
p.Set(value as ElementId);
}
else if (value.GetType().Equals(typeof(string)))
{
p.Set(new ElementId(int.Parse(value as string)));
}
else
{
p.Set(new ElementId(Convert.ToInt32(value)));
}
break;
case StorageType.String:
p.Set(value.ToString());
break;
}
}
catch
{
throw new Exception("Invalid Value Input!");
}
}
All possible good values will be accepted and the best intelligence can be achieved. The following code can be used to exercise the help method:
Wall wall = SelectWall(cmdData.Application.ActiveUIDocument.Selection);
List<Parameter> pLst = (from Parameter p in wall.Parameters select p).ToList();
//Double StorageType
Parameter baseOffsetP = pLst.First(p=>p.Definition.Name == "Base Offset");
SetParameterValue(baseOffsetP, "2'3\"");
SetParameterValue(baseOffsetP, "15");
SetParameterValue(baseOffsetP, "14.7");
SetParameterValue(baseOffsetP, 11);
SetParameterValue(baseOffsetP, 7.1);
try
{
SetParameterValue(baseOffsetP, "abc");
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
//Integer StorageType
Parameter strucUsageP = pLst.First(p => p.Definition.Name == "Structural Usage");
SetParameterValue(strucUsageP, "2");
SetParameterValue(strucUsageP, 1.0);
SetParameterValue(strucUsageP, 2L);
SetParameterValue(strucUsageP, 0);
try
{
SetParameterValue(strucUsageP, wall);
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
//String StorageType
Parameter markP = pLst.First(p => p.Definition.Name == "Mark");
SetParameterValue(markP, "Test by Spiderinnet");
SetParameterValue(markP, 123456.789);
SetParameterValue(markP, 2L);
SetParameterValue(markP, wall);
try
{
SetParameterValue(markP, null);
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
If an extension method for the Parameter class seems better, here it is:
#region Namespaces
using System;
using System.Text;
using System.Linq;
using System.Xml;
using System.Reflection;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Forms;
using System.IO;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Events;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Electrical;
using Autodesk.Revit.DB.Plumbing;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.UI.Events;
using Autodesk.Revit.Collections;
using Autodesk.Revit.Exceptions;
using Autodesk.Revit.Utility;
using RvtApplication = Autodesk.Revit.ApplicationServices.Application;
using RvtDocument = Autodesk.Revit.DB.Document;
#endregion
namespace RevitAddinCS
{
public static class ParameterExtension
{
public static void SetValue(this Parameter p, object value)
{
try
{
if (value.GetType().Equals(typeof(string)))
{
if (p.SetValueString(value as string))
return;
}
switch (p.StorageType)
{
case StorageType.None:
break;
case StorageType.Double:
if (value.GetType().Equals(typeof(string)))
{
p.Set(double.Parse(value as string));
}
else
{
p.Set(Convert.ToDouble(value));
}
break;
case StorageType.Integer:
if (value.GetType().Equals(typeof(string)))
{
p.Set(int.Parse(value as string));
}
else
{
p.Set(Convert.ToInt32(value));
}
break;
case StorageType.ElementId:
if (value.GetType().Equals(typeof(ElementId)))
{
p.Set(value as ElementId);
}
else if (value.GetType().Equals(typeof(string)))
{
p.Set(new ElementId(int.Parse(value as string)));
}
else
{
p.Set(new ElementId(Convert.ToInt32(value)));
}
break;
case StorageType.String:
p.Set(value.ToString());
break;
}
}
catch
{
throw new Exception("Invalid Value Input!");
}
}
}
}
And the following code can be used to exercise the extension method:
Wall wall1 = SelectWall(cmdData.Application.ActiveUIDocument.Selection);
List<Parameter> pLst1 = (from Parameter p in wall1.Parameters select p).ToList();
//Double StorageType
Parameter baseOffsetP1 = pLst1.First(p => p.Definition.Name == "Base Offset");
baseOffsetP1.SetValue("2'3\"");
baseOffsetP1.SetValue("15");
baseOffsetP1.SetValue("14.7");
baseOffsetP1.SetValue(11);
baseOffsetP1.SetValue(7.1);
try
{
baseOffsetP1.SetValue("abc");
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
//Integer StorageType
Parameter strucUsageP1 = pLst1.First(p => p.Definition.Name == "Structural Usage");
strucUsageP1.SetValue("2");
strucUsageP1.SetValue(1.0);
strucUsageP1.SetValue(2L);
strucUsageP1.SetValue(0);
try
{
strucUsageP1.SetValue(wall);
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
//String StorageType
Parameter markP1 = pLst1.First(p => p.Definition.Name == "Mark");
markP1.SetValue("Test by Spiderinnet");
markP1.SetValue(123456.789);
markP1.SetValue(2L);
markP1.SetValue(wall);
try
{
markP1.SetValue(null);
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
Parameter Writer of RevitAddinWizard will provide the intelligence in no time.
Links to some related articles:
Parameter of Revit API - BuiltInParameter
Parameter Retriever of RevitAddCoder
Parameter of Revit API - Parameter Information
Parameter Infoer of RevitAddCoder
Parameter of Revit API - ParameterType And StorageType
Parameter Typer of RevitAddCoder
Parameter of Revit API - BuiltInParameterGroup
Parameter Grouper of RevitAddCoder
Parameter Filter of RevitAddCoder
Recent Comments