locked
RadioButtonList adapter (for Yuval) RRS feed

  • Question

  • User-534056067 posted

    In an earlier posting in this forum, an author asked how to fix the Login adapter so its "remember me" checkbox worked properly.  See http://forums.asp.net/thread/1400630.aspx. Towards the end of that thread, Yuval (ykarj) asked a somewhat different question about how to build a RadioButtonList adapter that handled event validation properly. Rather than continuing to answer Yuval's question in the "remember me" posting, I'm starting this new thread.

    Here is the answer for Yuval...

    Start with this test page:

    <%@ Page Language="C#" %>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <script runat="server">

    </script>

    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
             <asp:RadioButtonList id=RadioButtonList1 runat="server">
                <asp:ListItem>Item 1</asp:ListItem>
                <asp:ListItem>Item 2</asp:ListItem>
                <asp:ListItem>Item 3</asp:ListItem>
                <asp:ListItem>Item 4</asp:ListItem>
                <asp:ListItem>Item 5</asp:ListItem>
                <asp:ListItem>Item 6</asp:ListItem>
             </asp:RadioButtonList>
             <asp:Button runat="server" UseSubmitBehavior="true" Text="Submit" />
        </div>
        </form>
    </body>
    </html>

    Then you can add this adapter:

    using System;
    using System.Data;
    using System.Collections;
    using System.Configuration;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;

    namespace CSSFriendly
    {
        public class RadioButtonListAdapter : System.Web.UI.WebControls.Adapters.WebControlAdapter
        {
            public RadioButtonListAdapter()
            {
            }

            protected override void RenderBeginTag(HtmlTextWriter writer)
            {
            }

            protected override void RenderEndTag(HtmlTextWriter writer)
            {
            }

            protected override void RenderContents(HtmlTextWriter writer)
            {
                RadioButtonList ButtonList = Control as RadioButtonList;
                if (null != ButtonList)
                {
                    int i = 0;
                    foreach (ListItem li in ButtonList.Items)
                    {
                        string itemID = string.Format("{0}_{1}", ButtonList.ClientID, i++);
                        writer.AddAttribute("for", itemID);
                        writer.RenderBeginTag(HtmlTextWriterTag.Label);
                        writer.AddAttribute("id", itemID); writer.AddAttribute("type", "radio");
                        writer.AddAttribute("name", ButtonList.UniqueID);
                        writer.AddAttribute("value", li.Value);
                        if (li.Selected)
                        {
                            writer.AddAttribute("checked", "checked");
                        }
                        writer.RenderBeginTag(HtmlTextWriterTag.Input);
                        writer.RenderEndTag();
                        writer.Write(li.Text);
                        writer.RenderEndTag();
                        writer.WriteLine();
                        Page.ClientScript.RegisterForEventValidation(ButtonList.UniqueID, li.Value);
                    }
                    Page.ClientScript.RegisterForEventValidation(ButtonList.UniqueID);
                }
            }
        }
    }

    Notice that I've got two distinct calls to RegisterForEventValidation.  Yuval's code was missing the first of these two calls but I only figured it out by looking at disassembled code so it certainly isn't something that is obvious to anyone!

    Using this adapter (with the proper additions to refer to the adapter through the .browser file in App_Browsers) you can submit the test page and it won't throw exceptions due to validation problems.

    Friday, September 22, 2006 7:38 PM

All replies

  • User1159918542 posted

    Thanks! That solves the problem.

    Friday, September 22, 2006 8:02 PM
  • User-1548791341 posted

    Sorry to resurrect an old thread, but this seems pretty relevant to the discussion above.

     

    1    using System;
    2    using System.Data;
    3    using System.Collections;
    4    using System.Configuration;
    5    using System.Web;
    6    using System.Web.Security;
    7    using System.Web.UI;
    8    using System.Web.UI.WebControls;
    9    using System.Web.UI.WebControls.WebParts;
    10   using System.Web.UI.HtmlControls;
    11   
    12   namespace WebServices.CssFriendlyAdapters
    13   {
    14       public class RadioButtonListAdapter : System.Web.UI.WebControls.Adapters.WebControlAdapter
    15       {
    16           public RadioButtonListAdapter()
    17           {
    18           }
    19   
    20           protected override void RenderBeginTag(HtmlTextWriter writer)
    21           {
    22               writer.WriteBeginTag("ul");
    23               if (Control.CssClass != null && Control.CssClass.Length > 0)
    24               {
    25                   writer.WriteAttribute("class", Control.CssClass);
    26               }
    27               writer.Write(HtmlTextWriter.TagRightChar);
    28           }
    29   
    30           protected override void RenderEndTag(HtmlTextWriter writer)
    31           {
    32               writer.WriteEndTag("ul");
    33           }
    34   
    35           protected override void RenderContents(HtmlTextWriter writer)
    36           {
    37               RadioButtonList ButtonList = Control as RadioButtonList;
    38               if (null != ButtonList)
    39               {
    40                   int i = 0;
    41                   foreach (ListItem li in ButtonList.Items)
    42                   {
    43                       string itemID = string.Format("{0}_{1}", ButtonList.ClientID, i++);
    44                       writer.WriteBeginTag("li");
    45                       writer.Write(HtmlTextWriter.TagRightChar);
    46                       writer.WriteBeginTag("input");
    47                       writer.WriteAttribute("id", itemID);
    48                       writer.WriteAttribute("type", "radio");
    49                       writer.WriteAttribute("name", ButtonList.UniqueID);
    50                       writer.WriteAttribute("value", li.Value);
    51                       if (li.Selected)
    52                       {
    53                           writer.WriteAttribute("checked", "checked");
    54                       }
    55                       writer.Write(HtmlTextWriter.TagRightChar);
    56                       writer.WriteEndTag("input");
    57                       writer.WriteBeginTag("label");
    58                       writer.WriteAttribute("for", itemID);
    59                       writer.Write(HtmlTextWriter.TagRightChar);
    60                       writer.Write(li.Text);
    61                       writer.WriteEndTag("label");
    62                       writer.WriteEndTag("li");
    63                       writer.WriteLine();
    64                       Page.ClientScript.RegisterForEventValidation(ButtonList.UniqueID, li.Value);
    65                   }
    66                   Page.ClientScript.RegisterForEventValidation(ButtonList.UniqueID);
    67               }
    68           }
    69       }
    70   }
    

     

    That's my RadioButtonList adapter. It's a bit different from the one above, but pretty similar. My problem is that a RequiredFieldValidator won't catch when nothing is selected in the adapted RadioButtonList. The RequiredFieldValidator works on an unadapted RadioButtonList just fine. What am I missing? Could someone point me in the right direction?

    Much appreciated! =)
     

    Friday, April 13, 2007 3:48 PM
  • User-1548791341 posted
    An update:

    It turns out that my code works with server-side validation. It's just that client-side validation won't find anything wrong so the error doesn't show up until all of the client-side errors are gone. I guess I can live with that, even though it would be nice if the client-side validation would work too.
    Friday, April 13, 2007 4:02 PM
  • User-1385398420 posted

    Go to the CodePlex site for the CSSFriendly adapters and you'll see someone proposed something similar: http://www.codeplex.com/cssfriendly/Thread/View.aspx?ThreadId=8165

    Earlier this week I adapted the code in that post and tested it with both server and client-side validation and postback events. I'm hoping to test it more tonight and check it in.

    Among the implementation I have for it:

    • The RadioButtonList is rendered as an unordered list <ul>, with each radio button rendered in a list item <li> tag.
    • A <div> wraps the markup, and is assigned the CSS class AspNet-RadioButtonList.
    • Each list item is assigned the CSS class AspNet-RadioButtonList-Item.
    • The CssClass defined for the <asp:RadioButtonList> control is applied to the unordered list (<ul> tag).

    Of course the TFS is down in CodePlex, so I couldn't check it in tonight if I wanted to, but the implementation (very similar to yours) works. The only significant difference I can see is I wrap the entire content in a DIV tag.

    Here's the code of the RadioButtonListAdapter I am working on.

    using System;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    
    namespace CSSFriendly
    {
    	/// <summary>
    	/// Overrides the default table layout of the <see cref="System.Web.UI.WebControls.RadioButtonList"/>
    	/// control, to a XHTML unordered list layout (structural markup).
    	/// </summary>
    	public class RadioButtonListAdapter : System.Web.UI.WebControls.Adapters.WebControlAdapter
    	{
    		private const string WRAPPER_CSS_CLASS = "AspNet-RadioButtonList";
    		private const string ITEM_CSS_CLASS = "AspNet-RadioButtonList-Item";
    
    		protected override void RenderBeginTag(HtmlTextWriter writer)
    		{
    			// Div
    			writer.AddAttribute(HtmlTextWriterAttribute.Class, WRAPPER_CSS_CLASS);
    			writer.AddAttribute(HtmlTextWriterAttribute.Id, this.Control.ClientID);
    			writer.RenderBeginTag(HtmlTextWriterTag.Div);
    			writer.Indent++;
    
    			// Ul
    			if (!string.IsNullOrEmpty(this.Control.CssClass))
    			{
    				writer.AddAttribute(HtmlTextWriterAttribute.Class, this.Control.CssClass);
    			}
    			writer.RenderBeginTag(HtmlTextWriterTag.Ul);
    		}
    
    		protected override void RenderEndTag(HtmlTextWriter writer)
    		{
    			writer.RenderEndTag(); // Ul
    			writer.Indent--;
    			writer.RenderEndTag(); // Div
    		}
    
    		protected override void RenderContents(HtmlTextWriter writer)
    		{
    			RadioButtonList buttonList = this.Control as RadioButtonList;
    			if (buttonList != null)
    			{
    				int item = 0;
    
    				foreach (ListItem li in buttonList.Items)
    				{
    					string itemID = string.Format("{0}_{1}", buttonList.ClientID, item);
    
    					// Li
    					writer.AddAttribute(HtmlTextWriterAttribute.Class, ITEM_CSS_CLASS);
    					writer.RenderBeginTag(HtmlTextWriterTag.Li);
    
    					if (buttonList.TextAlign == TextAlign.Right)
    					{
    						RenderRadioButtonListInput(writer, buttonList, li, itemID);
    						RenderRadioButtonListLabel(writer, li, itemID);
    					}
    					else // TextAlign.Left
    					{
    						RenderRadioButtonListLabel(writer, li, itemID);
    						RenderRadioButtonListInput(writer, buttonList, li, itemID);
    					}
    
    					writer.RenderEndTag(); // </li>
    					Page.ClientScript.RegisterForEventValidation(buttonList.UniqueID, li.Value);
    					item++;
    				}
    
    				Page.ClientScript.RegisterForEventValidation(buttonList.UniqueID);
    			}
    		}
    
    		private void RenderRadioButtonListInput(HtmlTextWriter writer, RadioButtonList buttonList, ListItem item, string itemID)
    		{
    			// Input
    			writer.AddAttribute(HtmlTextWriterAttribute.Id, itemID);
    			writer.AddAttribute(HtmlTextWriterAttribute.Type, "radio");
    			writer.AddAttribute(HtmlTextWriterAttribute.Name, buttonList.UniqueID);
    			writer.AddAttribute(HtmlTextWriterAttribute.Value, item.Value);
    			if (item.Selected)
    			{
    				writer.AddAttribute(HtmlTextWriterAttribute.Checked, "checked");
    			}
    			writer.RenderBeginTag(HtmlTextWriterTag.Input);
    			writer.RenderEndTag(); // </input>
    		}
    
    		private void RenderRadioButtonListLabel(HtmlTextWriter writer, ListItem item, string itemID)
    		{
    			// Label
    			writer.AddAttribute("for", itemID);
    			writer.RenderBeginTag(HtmlTextWriterTag.Label);
    			writer.Write(item.Text);
    			writer.RenderEndTag(); // </label>
    		}
    	}
    }
      
    Friday, April 13, 2007 10:25 PM
  • User1881411084 posted
    Hello everybody,
     
    I'm new in this forum.
     
    First of all I'd like to thanko you for sharing the source code with us. It was very helpful to me. When using the control adapter I noticed, that no postbacks are done, even if I set the AutoPostback of the RadioButtonList to "true" and assign an OnSelectedIndexChanhe-event. As I'm a greenhorn with control adapters I wanted to kindly ask you, if someone has a hint for me, where the problem might be.
     
    Best wishes from Switzerland,
     
    Richie
    Tuesday, April 17, 2007 5:17 AM
  • User-1385398420 posted

    I've confirmed that AutoPostBack does not work. Not sure why. I'll do some research.

     

    Wednesday, April 18, 2007 12:52 AM
  • User-1385398420 posted

    Check out changeset 957 (http://www.codeplex.com/cssfriendly/SourceControl/DownloadSourceCode.aspx?changeSetId=957)

    I hacked the AutoPostBack support. Let me know if it works in your situation.

    Note that AutoPostBack seems to disregard client-side validation (though server-side validation works). Not sure if this is normal behavior (it's late and I'm tired). Let me know if it isn't. ;)


     

    Wednesday, April 18, 2007 1:06 AM
  • User-1385398420 posted

    Changeset 986 has some more mods to the RadioButtonListAdapter:

    • Disabled list items are now disabled in markup.
    • AutoPostBack only applied when controls are enabled.
    • Added walkthru tests for autopostback and onselectedindexchanged events.
    Feedback appreciated. :)
    Wednesday, April 18, 2007 11:31 AM
  • User1069916412 posted
    I have made some mods to this adapter for checkboxlist and am running into an issue. The list renders as it should however when I try and post the data I get ERROR:

    startIndex cannot be larger than length of string.
    Parameter name: startIndex

    Has anyone run into this issue before using this adapter?

    Monday, April 30, 2007 4:04 PM
  • User-1385398420 posted

    Can you post your code? Looking at it will help us figure out the problem (as well as potentially incorporate it into the adapters).

     

    Monday, April 30, 2007 4:24 PM
  • User1069916412 posted

    The code for the adapter below, I am creating the asp:checkboxlist controls in code and am wondering if maybe there is some sort of viewstate issue with the adapter that I need to address.

     

    using System;
    using System.IO;
    using System.Web;
    using System.Web.Configuration;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.HtmlControls;

    namespace Common.Lib.Web.UI.Adaptors
    {
        public class CheckBoxListAdaptor : System.Web.UI.WebControls.Adapters.WebControlAdapter
        {
            private const string ITEM_CSS_CLASS = "checklist-item";


            private WebControlAdapterExtender _extender = null;
            private WebControlAdapterExtender Extender
            {
                get
                {
                    if (((_extender == null) && (Control != null)) ||
                        ((_extender != null) && (Control != _extender.AdaptedControl)))
                    {
                        _extender = new WebControlAdapterExtender(Control);
                    }

                    System.Diagnostics.Debug.Assert(_extender != null, "CSS Friendly adapters internal error", "Null extender instance");
                    return _extender;
                }
            }

            protected override void OnInit(EventArgs e)
            {
                base.OnInit(e);
            }

            protected override void RenderBeginTag(HtmlTextWriter writer)
            {
                if (Extender.AdapterEnabled)
                {
                    Extender.RenderBeginTag(writer, "checkbox-list-" + Control.ID.ToString());
                    // ul
                    if (!string.IsNullOrEmpty(this.Control.CssClass))
                    {
                        writer.AddAttribute(HtmlTextWriterAttribute.Class, string.Format("{0}-list-{1}", this.Control.CssClass, this.Control.ID));
                    }
                    writer.RenderBeginTag(HtmlTextWriterTag.Ul);
                }
                else
                {
                    base.RenderBeginTag(writer);
                }
            }

            protected override void RenderEndTag(HtmlTextWriter writer)
            {
                if (Extender.AdapterEnabled)
                {
                    writer.RenderEndTag(); // Ul
                    writer.Indent--;
                    Extender.RenderEndTag(writer);
                }
                else
                {
                    base.RenderEndTag(writer);
                }
            }

            protected override void RenderContents(HtmlTextWriter writer)
            {
                if (Extender.AdapterEnabled)
                {
                    CheckBoxList checkList = this.Control as CheckBoxList;
                    if (checkList != null)
                    {
                        int item = 0;

                        foreach (ListItem li in checkList.Items)
                        {
                            string itemID = string.Format("{0}_{1}", checkList.ClientID, item);

                            // Li
                            writer.AddAttribute(HtmlTextWriterAttribute.Class, ITEM_CSS_CLASS);
                            writer.RenderBeginTag(HtmlTextWriterTag.Li);

                            if (checkList.TextAlign == TextAlign.Right)
                            {
                                RenderCheckBoxListInput(writer, checkList, li, itemID, item);
                                RenderCheckBoxListLabel(writer, li, itemID);
                            }
                            else // TextAlign.Left
                            {
                                RenderCheckBoxListLabel(writer, li, itemID);
                                RenderCheckBoxListInput(writer, checkList, li, itemID, item);
                            }

                            writer.RenderEndTag(); // </li>
                            Page.ClientScript.RegisterForEventValidation(checkList.UniqueID, li.Value);
                            item++;
                        }

                        Page.ClientScript.RegisterForEventValidation(checkList.UniqueID);
                    }

                }
                else
                {
                    base.RenderContents(writer);
                }
            }

            private void RenderCheckBoxListInput(HtmlTextWriter writer, CheckBoxList checkList, ListItem li, string itemID, int item)
            {
                // Input
                writer.AddAttribute(HtmlTextWriterAttribute.Id, itemID);
                writer.AddAttribute(HtmlTextWriterAttribute.Type, "checkbox");
                writer.AddAttribute(HtmlTextWriterAttribute.Name, checkList.UniqueID);
                writer.AddAttribute(HtmlTextWriterAttribute.Value, li.Value);
                if (li.Selected)
                {
                    writer.AddAttribute(HtmlTextWriterAttribute.Checked, "checked");
                }
                if (checkList.AutoPostBack)
                {
                    writer.AddAttribute(HtmlTextWriterAttribute.Onclick,
                        String.Format(@"setTimeout('__doPostBack(\'{0}${1}\',\'\')', 0)",
                            checkList.ClientID, item.ToString()));
                }
                writer.RenderBeginTag(HtmlTextWriterTag.Input);
                writer.RenderEndTag(); // </input>
            }

            private void RenderCheckBoxListLabel(HtmlTextWriter writer, ListItem item, string itemID)
            {
                // Label
                writer.AddAttribute("for", itemID);
                writer.RenderBeginTag(HtmlTextWriterTag.Label);
                writer.Write(item.Text);
                writer.RenderEndTag(); // </label>
            }
        }
    }

    Monday, April 30, 2007 4:50 PM
  • User-1385398420 posted

    I'm adding a CheckBoxList adapter shortly, heavily based on the RadioButtonListAdapter. The test page works fine with postbacks and events.

     

    Tuesday, May 1, 2007 9:58 AM
  • User-1385398420 posted

    And FYI, the problem was rooted here:

     writer.AddAttribute(HtmlTextWriterAttribute.Name, checkList.UniqueID);

    The name needs to be unique for the item not the check list. See the updated code (I'm checking it in after I post this) for more details.

     

    Tuesday, May 1, 2007 10:35 AM
  • User624304815 posted

    Hi,

    I'm new to this ControlAdapter.

    I have been assigned a task to create readonly controls(textbox, checkbox, radiobutton, linkbutton) based on a key setting in database.

    I have managed to do that but the events are not firing up for checkbox.

    Can you guys help me?

    Many thanks in advance,

    vndotnet79

    Monday, July 23, 2007 7:12 AM
  • User-305810911 posted

    Old thread but anyway. I think my solution is simpler and lets you keep all the goodies such as validaiton and autopostback etc.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Web.UI.WebControls.Adapters;
    using System.Web.UI;
    using System.IO;
    using System.Web.UI.Adapters;
    
    namespace EN2010.Web.Controls.Adapters
    {
        public class TableToUnsortedListAdapter : ControlAdapter
        {
            protected override void Render(System.Web.UI.HtmlTextWriter writer)
            {
    
                System.Diagnostics.Debug.WriteLine(this.Control.GetType());
    
                base.Render(new UnorderedListWriter(writer));
            }
    
            private class UnorderedListWriter : HtmlTextWriter
            {
                public UnorderedListWriter(TextWriter writer)
                    : base(writer) { }
    
                public override void RenderBeginTag(HtmlTextWriterTag tagKey)
                {
                    HtmlTextWriterTag render = tagKey;
                    if (tagKey == HtmlTextWriterTag.Table)
                        render = HtmlTextWriterTag.Ul;
                    else if (tagKey == HtmlTextWriterTag.Tr)
                        render = HtmlTextWriterTag.Li;
                    else if (tagKey == HtmlTextWriterTag.Td)
                        render = HtmlTextWriterTag.Span;
                            
                    base.RenderBeginTag(render);
                }
    
                public override void AddAttribute(HtmlTextWriterAttribute key, string value)
                {
                    if(key != HtmlTextWriterAttribute.Border && key != HtmlTextWriterAttribute.Cellpadding && key != HtmlTextWriterAttribute.Cellspacing)
                        base.AddAttribute(key, value);
                }
            }
        }
    }
    


    Tuesday, February 23, 2010 11:09 AM