Announcement

Collapse

Looking for a User App or Add-On built by the NinjaTrader community?

Visit NinjaTrader EcoSystem and our free User App Share!

Have a question for the NinjaScript developer community? Open a new thread in our NinjaScript File Sharing Discussion Forum!
See more
See less

Partner 728x90

Collapse

NT Does Not De-Serialize Property With [Browsable(false)] Attribute

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    NT Does Not De-Serialize Property With [Browsable(false)] Attribute

    I am trying to use NT's default serialization to save and restore primitive values that are not browsable. It doesn't work. Is there some way to do this without having to write my own serialization? -- They are all primitives.

    Test case:

    Create a new indicator with the wizard, with no properties and no plots. Compile. Edit the code to match that below. Compile.

    Create a new workspace. open a chart, and add the indicator. The output window will show that OnBarUpdate has seen the default value. Save and close the workspace.

    You can confirm in the XML file that NT has serialized the new value (true).

    Re-open the workspace. NT has invoked the property setter; yet OnBarUpdate is still seeing the default value! What is even going on here?

    Any help?


    Code:
    namespace NinjaTrader.Indicator
    {
    	[Description("Enter the description of your new custom indicator here")]
    	public class TestIndicator : Indicator
    	{
    		private bool testProperty = false;
    		private bool setOnce = false;
    		private bool whatOnBarUpdateSees = false;
    		
    		private readonly Font font = new Font("Arial Unicode MS", 8F);
    		private readonly SolidBrush brush = new SolidBrush(Color.White);
    		
    
    		protected override void Initialize() {
    		}
    
    		protected override void OnBarUpdate() {
    			if (!setOnce) {
    				whatOnBarUpdateSees = testProperty;
    				setOnce = true;
    				Print("whatOnBarUpdateSees=" + whatOnBarUpdateSees);
    			}
    			testProperty = true;
    		}
    
    
    		[Browsable(false)]
    		public bool TestProperty {
    			get {
    				Print("TestProperty.get: testProperty=" + testProperty);
    				return testProperty;
    			}
    			set {
    				Print("TestProperty.set: old=" + testProperty + " new=" + value);
    				testProperty = value;
    			}
    		}
    	}
    }

    #2
    steevcoco, thanks for the post. We will check into it with development further and get back to you.

    With your comment 'The output window will show that OnBarUpdate has seen the default value. ' Do you mean after first adding the indicator you actually seen a true default value printed?
    BertrandNinjaTrader Customer Service

    Comment


      #3
      Thanks for the reply Bertrand.

      Specifically, that is just saying that before the indicator is ever serialized, we simply see a confirmation that the OnBarUpdate method is reading the field, and it's still at it's default value. Here is what the indi simply does:

      The field initializer sets the default value to false. OnBarUpdate reads the field, and caches the very first value that it reads from the field. After that the property is set to true, so that when serialized, we are sure that we should expect true to come back in when de-serialized.

      In effect, NT is behaving as if it does not de-serialize the value: once the indi has run, we are ensured the value has been changed to true. When serialized, the workspace's xml file does confirm that NT has serialized the changed value (true). But when the workspace is re-opened, that value is not de-serialized into the field: the next time OnBarUpdate reads the field, it still gets the default value. So NT is saving the value, but somehow not setting it back when de-serialized.

      It is extra strange in that on de-serialization, we can see that the setter is being invoked; and the setter's argument value is the changed (true) value that was serialized. Yet still -- after that initial read of the default field mentioned in the comment above-- by the time OnBarUpdate gets invoked, it is somehow back at the default value!

      Comment


        #4
        Thanks Steve, yes we could confirm your findings and that's how it would be expected to work now. The Browsable attribute would need to be true for the property grid to apply the deserialization routine. What use case scenario did you have in mind so we could consider changes potentially in this handling?
        BertrandNinjaTrader Customer Service

        Comment


          #5
          Originally posted by steevcoco View Post
          Thanks for the reply Bertrand.

          Specifically, that is just saying that before the indicator is ever serialized, we simply see a confirmation that the OnBarUpdate method is reading the field, and it's still at it's default value. Here is what the indi simply does:

          The field initializer sets the default value to false. OnBarUpdate reads the field, and caches the very first value that it reads from the field. After that the property is set to true, so that when serialized, we are sure that we should expect true to come back in when de-serialized.

          In effect, NT is behaving as if it does not de-serialize the value: once the indi has run, we are ensured the value has been changed to true. When serialized, the workspace's xml file does confirm that NT has serialized the changed value (true). But when the workspace is re-opened, that value is not de-serialized into the field: the next time OnBarUpdate reads the field, it still gets the default value. So NT is saving the value, but somehow not setting it back when de-serialized.

          It is extra strange in that on de-serialization, we can see that the setter is being invoked; and the setter's argument value is the changed (true) value that was serialized. Yet still -- after that initial read of the default field mentioned in the comment above-- by the time OnBarUpdate gets invoked, it is somehow back at the default value!
          If the Browsable attribute is false, it implies that you do not want it to be changed from the PropertyGrid. Thus, with no way to change it (visually/manually) externally, it will always have to use the default value, which you will change in code, (as that is the only place to change it).

          Comment


            #6
            The indicator has some plotting properties that are modified by the user through mouse clicks on the chart (simple boolean toggle buttons, that turn thing on and off, for example) -- they are not exposed in the properties dialog. When the indicator serializes, I want to save those values and restore the user's last state.

            They are all primitives, so it would have been simple if this worked as-is, to save and restore those using NT's default serialization routines.

            I will look for a thread to get me started on a simple way to do the serialization myself. Could you point me to any thread with an example of a basic serialize and de-serialize routine for primitives? (I know this is considered unsupported.)

            Thanks!

            Comment


              #7
              Thanks for the details Steve, have forwarded to development for future consideration. I'm unfortunately not aware of any threads touching that subject closer with hints, perhaps this can be an aid to get started?

              An example of implementing custom serialization, how to serialize a collection, and using a File Serialization utility class
              BertrandNinjaTrader Customer Service

              Comment


                #8
                For those interested, here is how I solved it:

                I created a single property, "SaveSetings", to be browsable, so the user can select true/false to save all the changes made on-chart. I created a sub-type called PropertySettings tat contains the mutable primitive fields for those on-chart settings; and includes a bool for the user's choice of saving settings or not. As the user changes settings, my instance of PropertySettings gets the updates. And through a custom type converter and property editor, the property dialog shows a single boolean popup for the user to select save settings or don't save settings. The serializer serializes my whole object; which converts itself into a serializable string and back.

                The PropertySettingsConverter could be better: I am extending StringConverter because I haven't found documentation on extending a BooleanConverter, and I can't make that work. This does work fine, but the property editor is not the same as a regular boolean editor; so for example, double-clicking the property won't toggle the value; you need to select the popup. But it all looks and acts correctly and fits with the user's UI expectations.


                Code:
                		/// <summary>Serialization support for PropertySettings.</summary>
                                [GridCategory("Settings")]
                		[Gui.Design.DisplayNameAttribute("Save Indicator Settings")]
                		[Description("If true, the indicator will save and restore any settings you make on the chart. "
                				+ "Otherwise, each time the indicator loads, the default settings "
                				+ "will be loaded. Default is true.")]
                		[TypeConverter(typeof(PropertySettingsConverter))]
                		[XmlIgnore()]
                		public PropertySettings SaveSettings {
                			get { return props; }
                			set { props = value; }
                		}
                		
                		/// <summary>Serialization support for PropertySettings.</summary>
                		[Browsable(false)]
                		public string SaveSettingsSerialize {
                			get { return props.ToString(); }
                			set {
                				PropertySettings newProps = new PropertySettings();
                				newProps.SetAllFromString(value);
                				if (!newProps.saveSettings) return;
                				props = newProps;
                			}
                		}
                		
                		/// <summary>Serialization support for non-browsabe properties.</summary>
                		public class PropertySettings
                		{
                			// Properties saved on serialization. Read and set properties in this object.
                			// These values are the defaults.
                			public bool saveSettings = true;
                			public int xyzRate = 250;
                			public bool xyzOn = false;
                			...
                			
                			/// <summary>
                			/// Restores the object from serialization: value should be the string returned by ToString().
                			/// </summary>
                			public void SetAllFromString(string value) {
                				string[] values = value.Split(new Char[] {','}, StringSplitOptions.RemoveEmptyEntries);
                				saveSettings = bool.Parse(values[0]);
                				xyzRate = int.Parse(values[1]);
                				xyzOn = bool.Parse(values[2]);
                				...
                			}
                			
                			/// <summary>Serializes the object's properties to a string for serialization.</summary>
                			public override string ToString() {
                				return saveSettings.ToString() + "," + xyzRate + "," + xyzOn + "," +...;
                			}
                		}
                
                		/// <summary>Serialization support for PropertySettings.</summary>
                		public class PropertySettingsConverter : StringConverter
                		{
                			/// <summary>Indicates a dropdown list of elements can be used in the editor.</summary>
                			public override bool GetStandardValuesSupported(ITypeDescriptorContext context) {
                				return true;
                			}
                			
                			/// <summary>Returns the list of elements displayed in the dropdown list.</summary>
                			public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) {
                				return new StandardValuesCollection(new string[] { "true", "false" });
                			} 
                			
                			/// <summary>Convert value to string.</summary>
                			public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture,
                					object value, Type destType) {
                				if (value is PropertySettings) return ((PropertySettings) value).saveSettings.ToString();
                				else return base.ConvertTo(context, culture, value, destType);
                			}
                
                			/// <summary>Convert value to PropertySettings.</summary>
                			public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
                				if (value is string) {
                					PropertySettings ps = new PropertySettings();
                					ps.saveSettings = Boolean.Parse((string) value);
                					return ps;
                				} else return base.ConvertFrom(context, culture, value);
                			}
                		}
                This is what the code achieves: I used NT's serialization supports as much as possible. The only annoyance is that the SaveSettingsSerialize property must be public -- something MUST be public to use NT's default serialization. My properties object, which encapsulates the properties inside the custom type, has a public, browsable getter/setter. That is tagged with the custom property editor, which displays a true/false popup that effects setting the one boolean within the object -- that chooses to save or don't save all the on-chart settings. This gets the browsable boolean property into the indicator's property sheet. On serialization, NT picks up the non-browsable serialize property; and my object converts itself to a serializable string. On de-serialization, NT sets that property, and my object parses back the string and sets it' properties.

                Comment

                Latest Posts

                Collapse

                Topics Statistics Last Post
                Started by warreng86, 11-10-2020, 02:04 PM
                5 responses
                1,356 views
                0 likes
                Last Post NinjaTrader_Manfred  
                Started by Perr0Grande, Today, 08:16 PM
                0 responses
                3 views
                0 likes
                Last Post Perr0Grande  
                Started by elderan, Today, 08:03 PM
                0 responses
                5 views
                0 likes
                Last Post elderan
                by elderan
                 
                Started by algospoke, Today, 06:40 PM
                0 responses
                10 views
                0 likes
                Last Post algospoke  
                Started by maybeimnotrader, Today, 05:46 PM
                0 responses
                12 views
                0 likes
                Last Post maybeimnotrader  
                Working...
                X