Inherited Extender Property Defaults

I've created a new extender component which extends a "Participates" property to other controls on a form.  If I drop that on a form (form A) along with a toolbar I'm able to set the toolbar's Participates property - no problem. Both the toolbar and the extender component are set to "Protected".

When I derive a form B from form A I have a problem.  If I set the toolbar's Participates property to true (the default) in form A all is well (form B can reset it as desired) but if I set it to false in form A then formB is unable to set it to true.  Recompiling always resets it to false.   Ideas  Thx...


Answer this question

Inherited Extender Property Defaults

  • matshelander

    I'm not sure what he means by "The only solution is to record the correct "default" values after the base class is constructed,".  My guess would be: hide the component as private and just expose it's properties with the correct default attributes  Sounds like a good idea but probably not for my extender provider scenario.

    But I will investigate the Property Descriptors...Thanks, Joey. 

  • homez99

    I did not know that. I was going to suggest you put a breakpoint and look at the call stack at that point, to see what might be higher in the stack.
  • Interdit

    Ahhh..nevermind.  It appears that ShouldSerializeXXX can take the same parameters as the GetXXX methods so (in theory) my problem is solved.  Working on it...
  • SanJain

    Yeah, it would appear that Brian Pepin mentioned a potential work around here  
    <a href="http://groups-beta.google.com/group/microsoft.public.dotnet.framework.windowsforms.designtime/browse_thread/thread/cd55afaa44505cc7/6edce12bee6ae315 q=pepin+shouldserialize&_done=%2Fgroups%3Fq%3Dpepin+shouldserialize%26qt_s%3DSearch+Groups%26&_doneTitle=Back+to+Search&&d#6edce12bee6ae315">in the newsgroups</a> but his aproach is a little over my head at the moment.

    Can you or Frank describe what he meant by this
    <i>
    "...Normally, the code generator keys off of ShouldSerializeValue, but unfortunately as you pointed out this is broken in VS 2003.  Instead, changing the attributes so they indicate the property should never be serialized will do the trick. 

    However, PropertyDescriptor is abstract and public, meaning that you can create your own.  You could create your own property descriptor that just delegated to the original property descriptor for everything, but had two sets of attributes to return:  the original set, and a set with serialization turned off.  Then, toggle between which set to return based on the result of the ShouldSerializeValue method.  That should work like a champ."</i>

    Sounds like in my Form B (in the contructor ) I could create a property descriptor that would cause my Form B to serialize when needed   

  • Ashton

    Very cool. I am glad it worked out!
  • Krazon

    I love it when a plan comes together. Thanks for the help, guys.  This appears to be working for inherited forms 3 levels deep. The "RecordParticipatesDftValues" method gets called after Initialize in each base form's contructor. This is partial code for my extender provider object:internal Hashtable htParticipatesDftValues = new Hashtable();

    /// <summary>
    /// This used to record values in base forms to make sure derived 
    /// forms serialize extender properties properly.
    /// </summary>
    public void RecordParticipatesDftValues(Form frm)
    {
    htParticipatesDftValues.Clear();
    RecordParticipatesForControl(frm);
    }

    // recursively record controls "Participates" value
    private void RecordParticipatesForControl(Control control)
    {
    foreach (Control childControl in control.Controls)
    {
    RecordParticipatesForControl(childControl);
    }
    if (!(control is System.Windows.Forms.Form))
    {
    if (this.GetParticipates(control))
    htParticipatesDftValues.Add(control.Name, true);
    else
    htParticipatesDftValues.Add(control.Name, false);
    }
    }

    #region EXTENDER PROPERTY IMPLEMENTATION

    #region Participates

    /// <summary>
    /// This returns whether this control participates in ...
    /// </summary>
    /// <param name="ctrl"></param>
    /// <returns></returns>
    [Category(global.CategoryName)]
    [Description("This returns whether this control participates in ...")]
    public bool GetParticipates(Control ctrl) 

    if (htProvidedProperties.Contains(ctrl)) 

    return ((PermissionsMgrProvidedProperties)htProvidedProperties[ctrl]).Participates; 

    else 

    return true; 



    /// <summary>
    /// This sets whether this control participates in ...
    /// </summary>
    /// <param name="ctrl"></param>
    /// <param name="value"></param>
    [Category(global.CategoryName)]
    [Description("This sets whether this control participates in ...")]
    public void SetParticipates(Control ctrl, bool value) 

    GetAddControl(ctrl).Participates = value; 


    /// <summary>
    /// This is needed to determine how the extended property was used on the baseForm and
    /// determine if the property needs to be serialized for this control on a derived form.
    /// </summary>
    /// <param name="ctrl"></param>
    private bool ShouldSerializeParticipates(Control ctrl) 
    {
    if (!htParticipatesDftValues.ContainsKey(ctrl.Name)) return true;
    if (GetParticipates(ctrl) != (bool)htParticipatesDftValues[ctrl.Name])
    return true;
    else
    return false;
    }

    /// <summary>
    /// This method returns the property to the default property as it was used on the base form.
    /// </summary>
    /// <param name="ctrl"></param>
    private void ResetParticipates(Control ctrl)
    {
    if (htParticipatesDftValues.ContainsKey(ctrl.Name))
    SetParticipates(ctrl, (bool)htParticipatesDftValues[ctrl.Name]);
    else
    SetParticipates(ctrl, true);
    }

    #endregion


  • Ben Ogle

    Yep, I was using only the DefaultValue attributes. I will give the ShouldSerialize...methods a try (with code to save off and later use original defaults).  Thanks. 
  • KLC

    Okay, wondering if I understand: The ShouldSerializeXXX methods are used by the code generator to decide whether or not to write code to set the property's value, correct   Will ShouldSerializeXXX get called once for the provider object or once for each control that is extended   (I guess I could put a breakpoint in there to find out, duh) I was just wondering how I will know which extended control ShouldSerializeXXX is firing for...


  • tseiy

    That post is not related. It is about design-time property values always being serialized to .resx files, causing bloat. It is not about IExtenderProvider properties.

    Firstly, to specify the default values for these properties, you must be supplying a DefaultValue attribute  I know this does work when you put it on the GetXXX method for an extender provider. Now how is the correct default value going to get picked up for the derived class... a good question. There are ShouldSerializeXXX and ResetXXX methods you could use on the extender provider, I am not sure they work, but even if they do, how will you trigger the correct default value, without looking at some data avaliable from the base class, specifying default values

    This is a tricky problem made trickier by the IExtenderPovider. I don't see how ICustomTypeDescriptor will help you, since the "property" is on the toolbar and not on the derived class.

    Perhaps in the constructor for form A, after the InitializeComponent calls, you could call a method on your IExtenderProvider in the form A, that records the correct "default value" for all the objects extended. That is, go through all the extended objects, and save the current value somewhere (hash table or on the objects tag property). Then when you need the default value, when ShouldSerializeXXX and ResetXXX are called (assuming those work), you look it up. This would work to any level of derivation, as you just have to call that "RecordDefaultValues" method on your extender provider at the end of any constructor for a derived class.

  • Martijn Iseger

    Actually, this was the issue I raised on the <a href="http://groups-beta.google.com/group/microsoft.public.dotnet.framework.windowsforms.designtime/browse_thread/thread/74ea085516fd793f/8ce2fd7887b09dc4 q=defaultvalue&_done=%2Fgroup%2Fmicrosoft.public.dotnet.framework.windowsforms.designtime%2Fsearch%3Fgroup%3Dmicrosoft.public.dotnet.framework.windowsforms.designtime%26q%3Ddefaultvalue%26qt_g%3D1%26&_doneTitle=Back+to+Search&&d#8ce2fd7887b09dc4">newsgroups</a> with regard to defaultvalue attribute and inheritance.

    i later found out using Reflector that MS has already handled the said scenario using a number of PropertyDescriptors (InheritedPropertyDescriptor in my scenario and perhaps ExtendedPropertyDescriptor in yours).

    The issue is really on the defaultvalue.  Inheritance will be broken if MS follows your desired results.  For your second scenario, form A is set to FALSE so code is generated for the said property.  For form B, it is set to true, but since it is the defaultvalue that you defined (in your case, what you wanted), it will not be code-gened so your formB will not be reproduced properly by InitializeComponent.

    The Framework somehow tracks the values of properties with specified defaultvalues down the hierarchy to properly serialize what needs to be serialized.  I haven't really analyzed the whole process but am pretty sure that the disassembled code illustrates this.

  • Wenbin Zhang

    I just checked. It looks like the ShouldSerialize and Reset technique does work for IExtenderProviders.
  • SMOwais

    I just tested this and I was surprised on the results.

    Unlike inherited properties with default values, the Default value supplied by the ShouldSerialize methods are not changed down the hierarchy for Extenders.  An inherited form with an extended control's property Reset-ed will reset it to the specified value on the ShouldSerialize method of the extender.  It will not be code-gened but can*space
    space*the reconstruction of the form at runtime if the immediate base form had it set to a non-default value.

    Perhaps you shouldn't include a ShouldSerialize method for your extended property so it will always get serialized but your Reset will always work (which is what you wanted)

  • Inherited Extender Property Defaults