Failing to read/write UserData from Object.Attribute

I’m trying to save an attribute to Object.Attributes.UserData. The data is saved during the session, but when reloading the .3dm file it is not. (The color change to Attributes.ObjectColor is saved, but the custom dat added to Attributes.UserData is not found after reloading)

I appreciate any input on what I might be missing or doing wrong here. I tried adding a GUID to the custom UserData class as per this thread, but it did not solve the problem.

Below is the code snippet that should save the data. The code is inspired by this GitHub example.

// Mark selected objects with the selected surface type
foreach (var objRef in goObject.Objects())
{
    var obj = objRef.Object();
    if (obj != null)
    {
        var userData = obj.Attributes.UserData.Find(typeof(SurfaceTypeUserData)) as SurfaceTypeUserData;

        if (userData != null)
        {
            RhinoApp.WriteLine("Current SurfaceType is: {0}", userData.SurfaceType);
        }

        // Set the selected surface type
        userData = new SurfaceTypeUserData
        {
            SurfaceType = selectedSurfaceType
        };

        // Apply the color to the object's display attributes
        obj.Attributes.ObjectColor = selectedColor; 
        obj.Attributes.ColorSource = ObjectColorSource.ColorFromObject;

        obj.Attributes.UserData.Add(userData);

        obj.CommitChanges();
    }
}
doc.Views.Redraw();
return Result.Success;

And here is the code for the custom UserData type that I’m attempting to save:

using Formulight.DataObjects;
using Rhino.DocObjects.Custom;
using Rhino.FileIO;
using System;

[Serializable]
public class SurfaceTypeUserData : UserData
{
    public SurfaceType SurfaceType { get; set; }

    public override string Description => "SurfaceType User Data";

    public override bool ShouldWrite => true;

    protected override void OnDuplicate(UserData source)
    {
        if (source is SurfaceTypeUserData src)
        {
            SurfaceType = src.SurfaceType;
        }
    }

    protected override bool Read(BinaryArchiveReader archive)
    {
        SurfaceType = (SurfaceType)archive.ReadInt();
        return true;
    }

    protected override bool Write(BinaryArchiveWriter archive)
    {
        archive.WriteInt((int)SurfaceType);
        return true;
    }
}

Hi @Alex61,

I see your user data class does not define the required GUID attribute.

See the following example

SampleCsUserDataObject.cs

Use Guidgen.exe, that comes with Visual Studio, to a unique GUID. In VS, click Tools > Create GUID.

– Dale

1 Like

Thank you for the timely response, Dale.

As mentioned in the post I did add GUID without solving the problem. However thanks to your response I did add it back in and figured out the problem. I was erroneously adding a new UserData even if UserData already existed, rather than updating the existing entry.

This seems to have made the duplicate entries added get discarded and simply maintained the original instance of the UserData, which is why adding the GUID did not seem to make a difference to me (the empty placeholder SurfaceType was being retained even when I added a GUID which made the program behave the same as when the GUID was not there and no UserData was being saved.)

Replacing this line from my sample above:

obj.Attributes.UserData.Add(userData);

With the following lines fixed the problem.

if (surfaceData == null)
{
    // If no surface data exists, create and add it to the UserData
    surfaceData = new SurfaceTypeUserData();
    obj.Attributes.UserData.Add(surfaceData);
}

surfaceData.SurfaceType = selectedSurfaceType; // Set the new selected surface type