Yes, this is a well known (and a very bizarre) issue. I don’t think there were any solutions suggested in the old forum and I even asked scripting support people and no one had a solution. So I came up with my own solution/workaround for this as I use these Expandable Objects extensively. I will demonstrate with a simple example below. You’ll need to adapt this to your specific data structures. It adds a lot more code and complexity (especially with more complex classes with multiple variables), but it is well worth it because it produces a clean and crisp user interface.
Since the custom class saved values don’t survive after a compile, you have to take extra steps and add additional inputs (one per each class variable that you wish to save). These extra/dummy variables do survive a compile and are saved in the XML for later retrieval. You will use these to initialize the custom class parameters in your code.
I have the following class:
[TypeConverter(typeof(ExpandableObjectConverter))]
public class BrushAndOpacity
{
[XmlIgnore]
[Display(Name="Color", Order=1)]
public Brush ColorBrush
{ get; set; }
[Range(0, 100)]
[Display(Name="Opacity (0-100)", Order=2)]
public int ColorOpacity
{ get; set; }
}
Add the following to Properties section. Each of the custom class variables will need their own separate input, so add additional inputs as necessary for your case.
[Display(GroupName="Settings", Order=30, Name="Your parameter name")]
public BrushAndOpacity Input_CollectionSettings
{ get; set; }
// Below two extra/dummy variables that save the custom class values. Adapt this to your case.
public string Input_MyBrush_Serializable
{
get { return( Serialize.BrushToString(Input_CollectionSettings == null ? Default_MyBrush : Input_CollectionSettings.ColorBrush)); }
set { Input_CollectionSettings.ColorBrush = Serialize.StringToBrush(value); }
}
public int Input_MyOpacity
{
get { return(Input_CollectionSettings == null ? Default_MyOpacity : Input_CollectionSettings.ColorOpacity); }
set { Input_CollectionSettings.ColorOpacity = value; }
}
Default_MyBrush and Default_MyOpacity will need to get defined at indicator class level:
private Brush Default_MyBrush = Brushes.DarkGoldenrod;
private int Default_MyOpacity = 40;
In State.SetDefaults initialize the custom class instance using default values.
Input_CollectionSettings = new BrushAndOpacity()
{
ColorBrush = Default_MyBrush,
ColorOpacity = Default_MyOpacity
};
I believe this is all you need to get this working. Try this and let me know if I missed something and I’ll update my response.
After doing this, you’ll notice that the two new dummy inputs (Input_MyBrush_Serializable and Input_MyOpacity) will show up on the properties grid which is undesirable. You’ll need to hide those on the grid. This can be done in one of two ways that I’m aware of. You can add “[Browsable(false)]” to each input, or you can do this using propertyDescriptorCollection.Remove() inside your IndicatorBaseConverter if you have one defined already.
Obviously the first solution is much simpler if it works for you. I seem to recall there was an issue in my case with using Browsable(false) but I don’t remember the details, so I went with the other solution since I already have an IndicatorBaseConverter defined for all my tools and it was just a matter of adding an extra line per dummy input to hide them on the grid.
Try the simple method and if doesn’t work right, try the second approach. If you need code snippet for IndicatorBaseConverter, let me know and I’ll post it here.