Search code examples
c#asp.net.net-3.5servercontrols

The correct pattern for passing data to child controls in a server control


I'm working with a 3rd party system to implement some forms in a website.

The 3rd party system provides me with XML definitions for these forms. e.g.

<form>
    <segment>
        <label>The header</label>
        <fields>
            ...
            <field>
                <id>field_Dob</id>
                <type>Date</type>
                <label>Date of Birth</label>
                <required>1</required>
            </field>
            ...
        </fields>
    </segment>
    ...
</form>

I am parsing this XML in a Server Control and programatically generating a tree of controls. The labels of the controls are passed through in the XML.

It is part of our proposal to "inject" little help texts into this form.

Ideally I would like to pass these help texts in from the markup of the top level control so that non-developers (HTML monkies) can change the help texts, and associate them with the field by it's ID. Something like so

<controls:MyCrazyForm runat="server">
    <helpTexts>
        <helpText for="field_Dob">
Some rambling nonsense to do with the DOB field
        </helpText>
        ...
    </helpTexts>
</controls:MyCrazyForm>

The controls are parsed recursively.

The Form creates a fieldset for each segment, fieldsets create many FieldXXX (where XXX = date, text, combobox etc) depending on the data type.

The FieldXXX types create a div and then several standard .net controls (TextBox, DropDownList, etc) to actually render themselves. It is at this point, within the containing div that I need to output the help text.

My Question

What is the "best" way to get these texts from the top-level form control to these child controls which are 3 or 4 levels deeper in the control tree.

There will only ever be one of these forms on a page. Should I make the top level form as Singleton and get it like so...?

if(MyCrazyForm.Instance.HelpTexts.ContainsKey("theIdOfTheCurrentField"))
{
    this.HelpText = MyCrazyForm.Instance.HelpTexts["theIdOfTheCurrentField"];
}

Should I pass a reference to the form into every control all the way down the tree (this seems messy)?

Am I miles of target with my architecture of this (although it's working realyl nicely at the moment) form and should I look at a different method of implementation?

Thanks


Solution

  • It may be more complicated at first, but makes it easier to maintain, why not run the xml file through an xsl procesor? The xslt file would assign the helptext nodes of your helptexts file to the corresponding field nodes.

     <?xml version="1.0" encoding="ISO-8859-1"?>
    <form>
        <segment>
            <label>The header</label>
            <fields>
                <field>
                    <id>field_name</id>
                    <type>string</type>
                    <label>Name</label>
                    <required>1</required>
                </field>
                <field>
                    <id>field_Dob</id>
                    <type>Date</type>
                    <label>Date of Birth</label>
                    <required>1</required>
                </field>
            </fields>
        </segment>
    </form>
    

    XSLT file:

    <?xml version="1.0" encoding="ISO-8859-1"?>
    <!-- Edited by XMLSpy® -->
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
    
      <xsl:template match="/form/segment/fields/field[id='field_name']">
        <xsl:copy>
          <xsl:element name="helptext">This is a Name helptext.</xsl:element> 
          <xsl:apply-templates/>
        </xsl:copy>
      </xsl:template>
    
      <xsl:template match="/form/segment/fields/field[id='field_Dob']">
        <xsl:copy>
          <xsl:element name="helptext">This is a Date of birth helptext.</xsl:element> 
          <xsl:apply-templates/>
        </xsl:copy>
      </xsl:template>
    
        <xsl:template match="node() | text()">
            <xsl:copy>
                <xsl:copy-of select="@*"/>
                <xsl:apply-templates/>
            </xsl:copy>
        </xsl:template>
    
    </xsl:stylesheet>
    

    yields this:

    <form>
        <segment>
            <label>The header</label>
            <fields>
                <field>
    <helptext>This is a Name helptext.</helptext>
                    <id>field_name</id>
                    <type>string</type>
                    <label>Name</label>
                    <required>1</required>
                </field>
                <field>
    <helptext>This is a Date of birth helptext.</helptext>
                    <id>field_Dob</id>
                    <type>Date</type>
                    <label>Date of Birth</label>
                    <required>1</required>
                </field>
            </fields>
        </segment>
    </form>
    

    This xml file can now be parsed like before, but now you can get the help text at the same time as you are generating the form elements. Your HTML monkies then only need to edit the XSLT file, or you simply include another file :

      <xsl:template match="/form/segment/fields/field[id='field_Dob']">
        <xsl:copy>
          <xsl:element name="helptext">
            <xsl:copy-of select="document('field_Dob.txt')"/> 
          </xsl:element> 
          <xsl:apply-templates/>
        </xsl:copy>
      </xsl:template>
    

    You can try out XSL online here