Archives

Archives / 2004 / November
  • Why you shouldn't expose public properties from your pages

    We often have users asking us how they can access some variable that's in their page class from their user or custom controls.
    The answer is that your page class can expose public properties, and then any control can cast its Page property to your specific Page-inherited class and gain access to the new properties.
    But the second half of the answer is that you should really not do that even though it's possible.
    There is a double reason for that.
    The first is that it's your page that should orchestrate your controls (by accessing their properties and methods), not your controls orchestrating your page.
    And the second, which is very close, is that your controls should not depend on your page implementing special properties or methods or containing specific controls. Otherwise, you're breaking one of the most important qualities of WebControls, that is their reusability. Any control should have the ability to be dropped on any page and just work.
    Your user and custom controls should be components, that is, they should be independant, encapsulated and reusable entities. It's your page (or containing controls) only that should orchestrate the controls and glue them together. The glue should stay outside and should never ooze inside.
    A consequence of that is that your Page generally has no good reason to expose new public properties, because no one should have to consume them.

    Read more...

  • How to split server styles

    If you've been developing custom WebControls, in some cases, you may have had to split a server-side style on two HTML elements. Usually we want to apply the border and similar properties to a container like a div or td, and the Font properties and ForeColor to a text element such as a link (because a link forces the color and text-decoration, for example).

    If the style is an inline server style (specified in the aspx or ascx using a style property directly on the server control), we know everything about it and we know how to split the properties on the two elements.

    Now, if the style just specifies a CssClass, we are doomed because we have no way of knowing what’s inside the class (it’s purely client-side) and even less to split it correctly.

    Applying it to only one element does not work because some styles just won’t apply at this level, and applying it to both induces nice effects like double borders or proportional font-sizes being squared:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" >
        <head>
            <style type="text/css">
                .foo {font-size:2em;border-color:black;border-width:1px;border-style:solid}
            </style>
        </head>
        <body>
            <table>
                <tr>
                    <td class="foo">
                        <a href="foo.htm" class="foo">This is 4em</a> but this is 2em.
                    </td>
                </tr>
            </table>
        </body>
    </html>

    Renders as:

    This is 4em but this is 2em.

    You can partially solve this problem by applying the CssClass to both elements, and adding an inline style to the inner tag to suppress the second border and the size multiplication if a CssClass is present:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" >
        <head>
            <style type="text/css">
                .foo {font-size:2em;border-color:black;border-width:1px;border-style:solid}
            </style>
        </head>
        <body>
            <table>
                <tr>
                    <td class="foo">
                        <a href="foo.htm" class="foo" style="border-style:none;font-size:1em">This is 2em</a> and this too.
                    </td>
                </tr>
            </table>
        </body>
    </html>

    Renders as:

    This is 2em and this too.

    You could also add a second property (something like TextCssClass) on relevant Style-derived classes to enable the splitting of the style class by the users. This would be a good solution because it matches exactly what you are doing on inline styles, but it adds a new property, participating in a complexification of your control's object model, possibly confusing users who may not know about this style-splitting problem. It may simplify the implementation of the rendering of your control, though. So if you can do it, it's probably the best solution.

    There is a workaround that solves the problem just by writing the CSS file a certain way, though, splitting the styles from there instead of from the control. It requires no modification of the object model but requires some knowledge of the rendering for the control (we need the name of the tag for the container and for the text HTML nodes to be predictable and stable):

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" >
        <head>
            <style type="text/css">
                td.bar {border-color:black;border-width:1px;border-style:solid;color:red}
                a.bar {font-size:2em}
            </style>
        </head>
        <body>
            <table>
                <tr>
                    <td class="bar">
                        <a href="foo.htm" class="bar">This is 2em</a> but this is not.
                    </td>
                </tr>
            </table>
        </body>
    </html>

    Renders as:

    This is 2em but this is not.

    Here, we just split the definition of the bar class in two classes with the same name, but applying only to one tag name at a time.

    This is a suitable workaround if you can't use the second solution (the additional CssClass property on the style class) because it gives maximum flexibility to the user while requiring no modification on the WebControl's object model. It requires good documentation, though, so that the users can learn about this workaround when they hit the problem.

    Read more...