I am using a LayerTable event listener in a Grasshopper plugin, and trying to access the state properties to categorize the type of event that just occurred at a more granular level than is provided by e.EventType. I am running into issues with OldState in quite a few cases, particularly when generating events from the Grasshopper side.
Let’s start with the simple to more complex:
-
Rhino-generated + GH-generated events
This appears to be a Rhino bug – OldState reports the “Name” of a layer in the “FullPath” field. NewState reports properly. This sample comes from a Layer move.
-
GH-generated visibility event
If I change the visibility of a parent layer from a GH plugin component, I get an invalid OldState. I am currently calling CommitChanges from outside SolveInstance because this was causing Memory Access Violations (Rhino 5 crashes) when called from within SolveInstance while my Layer listener component was active.
Note that:
-
the Name is empty (causes crashes if accessed)
-
IsValid is invalid (d’oh).
-
For some reason, the HasUserData property actually changes during the execution of the layer listener component, changing from false to true during the course of an if-else statement. (No layers have user data at any time in this example.)
-
Setting GH layer visibility - Case 2
Occasionally getting a corrupted user dictionary. It’s bizarre. HasUserData for NewState and OldState flip flops in the middle of execution.
The code for SetLayerVisibility:
public class LayerTools_SetLayerVisibility : GH_Component
{
/// <summary>
/// Initializes a new instance of the LayerTools_SetLayerVisibility class.
/// </summary>
public LayerTools_SetLayerVisibility()
: base("Set Layer Visibility", "SetLayerVisibility",
"Set the visibility of a Rhino layer.",
"Squirrel", "Layer Tools")
{
}
/// <summary>
/// Registers all the input parameters for this component.
/// </summary>
protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
{
pManager.AddBooleanParameter("Active", "A", "Set to true to change the active layer in Rhino.", GH_ParamAccess.item, false);
pManager.AddTextParameter("Path", "P", "Full path of the layer to be activated.", GH_ParamAccess.item);
pManager.AddBooleanParameter("Visibility", "V", "Visibility of the layer.", GH_ParamAccess.item, true);
}
/// <summary>
/// Registers all the output parameters for this component.
/// </summary>
protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
{
pManager.AddIntegerParameter("Layer ID", "ID", "Index of layer that has been modified.", GH_ParamAccess.item);
pManager.AddBooleanParameter("Status", "St", "True when the layer has been modified.", GH_ParamAccess.item);
}
// Set up a list of layers outside SolveInstance
List<Layer> layersToModify = new List<Layer>();
// Clear any layers from previous solve
protected override void BeforeSolveInstance()
{
layersToModify.Clear();
}
// After solve instance, commit all the changes
protected override void AfterSolveInstance()
{
foreach (Layer layer in layersToModify)
{
layer.CommitChanges();
}
}
/// <summary>
/// This is the method that actually does the work.
/// </summary>
/// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param>
protected override void SolveInstance(IGH_DataAccess DA)
{
bool active = false;
string path = "";
bool V = true;
if (!DA.GetData(0, ref active)) return;
if (!DA.GetData(1, ref path)) return;
if (!DA.GetData(2, ref V)) return;
bool status = false;
int layer_index = -1;
if (path != null)
{
RhinoDoc doc = RhinoDoc.ActiveDoc;
Rhino.DocObjects.Tables.LayerTable layertable = doc.Layers;
layer_index = layertable.FindByFullPath(path, true);
if (active)
{
if (layer_index > 0 && layer_index < layertable.Count && layer_index != layertable.CurrentLayerIndex)
{ // if exists
// Get the layer
Layer thisLayer = layertable[layer_index];
thisLayer.IsVisible = V;
layersToModify.Add(thisLayer);
// thisLayer.CommitChanges();
status = true;
}
}
}
DA.SetData(0, layer_index);
DA.SetData(1, status);
}