Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Render RadioButtonList using nested divs instead of tables or flow

I am generating a radio button list programatically in C#.NET 3.5 and using a RadioButtonList in order to do so. However, something I find very frustrating is the RepeatLayout property, which can only be set to Flow or Table.

Tables are for tabular data, not for displaying forms. And flow isn't helpful because it doesn't lend itself well to styling.

What I really want is a nest of divs I can address with CSS. Can this be done?

Some examples to illustrate what I'm talking about and example code below.

Example of flow

<span id="s1">
    <input id="s1_0" type="radio" value="Answer A" name="s1">
    <label for="s1_0">Answer A</label>
    <br>
    <input id="s1_1" type="radio" value="Answer B" name="s1">
    <label for="s1_1">Answer B</label>
</span>

Example of table

<table border="0" id="s1">
    <tbody>
        <tr>
            <td><input type="radio" value="Answer A" name="s1" id="s1_0"><label for="s1_0">Answer A</label></td>
        </tr>
        <tr>
            <td><input type="radio" value="Answer B" name="s1" id="s1_1"><label for="s1_1">Answer B</label></td>
        </tr>
    </tbody>
</table>

What I actually want

<div id="s1">
    <div>
        <input type="radio" value="Answer A" name="s1" id="s1_0">
        <label for="s1_0">Answer A</label>
    </div>
    <div>
        <input type="radio" value="Answer B" name="s1" id="s1_1">
        <label for="s1_1">Answer B</label>
    </div>
</div>

C# Code I'm using to generate the list

I know whatever the solution is, won't be as quick and easy as this, but I'm putting it here so you can get an idea of the context in which I'm using it.

RadioButtonList rbl = new RadioButtonList { RepeatLayout = RepeatLayout.Table };
foreach (ControlValue cv in MasterControl.listControlValues)
{
    rbl.Items.Add(new ListItem(cv.name, cv.value));
}
ControlContainer.Controls.Add(rbl);
like image 450
Iain Fraser Avatar asked Feb 17 '23 12:02

Iain Fraser


1 Answers

You may want to use WebControlAdapter to change the way of how RadioButtonList is rendered.

Below is the complete code for your case:

public class RadioButtonListAdapter : WebControlAdapter
{
    protected override void RenderBeginTag(HtmlTextWriter writer)
    {
        writer.RenderBeginTag("div");
    }

    protected override void RenderEndTag(HtmlTextWriter writer)
    {
        writer.RenderEndTag();
    }

    protected override void RenderContents(HtmlTextWriter writer)
    {
        var adaptedControl = (RadioButtonList) Control;
        int itemCounter = 0;

        writer.Indent++;
        foreach (ListItem item in adaptedControl.Items)
        {
            var inputId = adaptedControl.ClientID + "_" + itemCounter++;

            // item div
            writer.RenderBeginTag("div");
            writer.Indent++;

                // input
                writer.AddAttribute("type", "radio");
                writer.AddAttribute("value", item.Value);
                writer.AddAttribute("name", adaptedControl.ClientID);
                writer.AddAttribute("id", inputId);

                if (item.Selected)
                {
                    writer.AddAttribute("checked", "checked");
                }

                writer.RenderBeginTag("input");
                writer.RenderEndTag();

                // label
                writer.AddAttribute("for", inputId);

                writer.RenderBeginTag("label");

                writer.Write(item.Value);

                writer.RenderEndTag();

            // div
            writer.Indent--;
            writer.RenderEndTag();
        }
        writer.Indent--;
    }
}

To connect this adapter to you application you have two options. First is to do it programmatically in page constructor:

var adapters = Context.Request.Browser.Adapters;
var radioButtonListType = typeof(RadioButtonList).AssemblyQualifiedName;
var adapterType = typeof(RadioButtonListAdapter).AssemblyQualifiedName;
if (!adapters.Contains(radioButtonListType))
{
    adapters.Add(radioButtonListType, adapterType);
}

Second option is using .browser file:

Add new Browser File to your project. It will reside in App_Browsers folder. Then replace contents with following declaration:

<browsers>
    <browser refID="Default">
            <controlAdapters>
                <adapter controlType="System.Web.UI.WebControls.RadioButtonList" 
                         adapterType="<your namespace>.RadioButtonListAdapter" />
            </controlAdapters>
    </browser>
</browsers>

As a reference you can use links from ScottGu's post ASP.NET 2.0 Control Adapter Architecture they can give a good basement in this topic.

Also there is an open source CSS Friendly Control Adapters project that adresses some of rendering issues of default ASP.NET controls.

like image 80
Alexander Manekovskiy Avatar answered Apr 06 '23 19:04

Alexander Manekovskiy