Question

I am trying to make my own version of a repeater control. It is not a DataBoundControl; it won't be bound to a DataSource, DataSourceID, DataBind method etc etc. It has is a template nested inside it, and, a property called RepeatCount, and it will repeat whatever is inside the template as specified by the afore-mentioned property.

However, if you add a textbox, and an autocomplete extender associated with it inside the template, the functionality of auto-completing is broken. Here is an image showing the spate of problems:

enter image description here

  • Notice the textbox ids generated. All I've added is a TextBox with ID="TextBox1" inside the template, but the same name/id got repeated multiple times.

  • You can see that the textbox is visible. To understand why it is visible, look in the code behind and view the ItemPreRender event
    I've handled (on the aspx page). If I had not written code like that I'd get an exception saying:

enter image description here

  • However writing code as mentioned in pt2 doesn't get me anywhere. The auto-complete functionality fails to work appropriately.

What could be the problem here? If this was properly put in a repeater control and made to work in a similar fashion it would work...

So what am I missing here?

Here are parts of the source code I am sharing:

aspx page

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="MyRepeaterComplex.aspx.cs" Inherits="RelationalGridView.Web.MyRepeaterComplex" %>

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="cc2" %>

<%@ Register Assembly="WebGui.Extensions" Namespace="WebGui.Extensions.MyRepeater"
    TagPrefix="cc1" %>

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

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>MyRepeater Sample</title>
    <style type="text/css">
        /*AutoComplete flyout */

        .autocomplete_completionListElement 
        {  
            margin : 0px!important;
            background-color : inherit;
            color : windowtext;
            border : buttonshadow;
            border-width : 1px;
            border-style : solid;
            cursor : 'default';
            overflow : auto;
            height : 200px;
            text-align : left; 
            list-style-type : none;
        }

        /* AutoComplete highlighted item */

        .autocomplete_highlightedListItem
        {
            background-color: #ffff99;
            color: black;
            padding: 1px;
        }

        /* AutoComplete item */

        .autocomplete_listItem 
        {
            background-color : window;
            color : windowtext;
            padding : 1px;
        }

    </style>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <div>
        <cc1:MyRepeater ID="MyRepeater1" runat="server" RepeatCount="10">
            <Template>
                <p>Enter Name: <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox></p>                
                <cc2:AutoCompleteExtender 
                    ID="AutoCompleteExtender1" 
                    runat="server" 
                    TargetControlID="TextBox1"
                    ServicePath="AutoComplete.asmx" 
                    ServiceMethod="GetCompletionList"
                    MinimumPrefixLength="2" 
                    CompletionInterval="1000"
                    EnableCaching="true"
                    CompletionSetCount="20"
                    CompletionListCssClass="autocomplete_completionListElement" 
                    CompletionListItemCssClass="autocomplete_listItem" 
                    CompletionListHighlightedItemCssClass="autocomplete_highlightedListItem"
                    DelimiterCharacters=";, :"
                    ShowOnlyCurrentWordInCompletionListItem="true">
                </cc2:AutoCompleteExtender>
            </Template>
        </cc1:MyRepeater>
    </div>
    </form>
</body>
</html>

Apsx Code Behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using AjaxControlToolkit;

namespace RelationalGridView.Web
{
    public partial class MyRepeaterComplex : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            //MyRepeater1.ItemPreRender += new WebGui.Extensions.MyRepeater.MyRepeaterItemPreRenderDelegate(MyRepeater1_ItemPreRender);
        }

        protected void MyRepeaterItemPrerender(object sender, WebGui.Extensions.MyRepeater.MyRepeaterItemEventArgs e)
        {
            TextBox tbx = (TextBox)e.Container.FindControl("TextBox1");
            AutoCompleteExtender ace = (AutoCompleteExtender)e.Container.FindControl("AutoCompleteExtender1");
            ScriptManager.GetCurrent(this).RegisterExtenderControl(ace, tbx);
        }


    }
}

Code for MyRepeater

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI.WebControls;
using System.Web.UI;
using System.Collections;

namespace WebGui.Extensions.MyRepeater
{
    public delegate void MyRepeaterItemPreRenderDelegate(object sender, MyRepeaterItemEventArgs e);
    public class MyRepeater: WebControl
    {
        List<Control> Containers;
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [TemplateContainer(typeof(MyRepeaterTemplateContainer))]
        public ITemplate Template { get; set; }

        public event MyRepeaterItemPreRenderDelegate ItemPreRender;

        public MyRepeater()
        {
            RepeatCount = 0;
            Containers = new List<Control>();
        }

        public int RepeatCount { get; set; }       

        protected override void OnPreRender(EventArgs e)
        {
            MyRepeaterItemEventArgs ex = new MyRepeaterItemEventArgs();
            for (int i = 0; i < RepeatCount; i++)
            {
                MyRepeaterTemplateContainer container = new MyRepeaterTemplateContainer();
                container.Page = Page;
                Template.InstantiateIn(container);
                if (ItemPreRender != null)
                {
                    ex.Container=container;
                    ItemPreRender(this, ex);
                }
                Containers.Add(container);
            }            
        }

        protected override void Render(HtmlTextWriter writer)
        {            
            foreach (Control ctrl in Containers)
            {                
                ctrl.RenderControl(writer);
                writer.WriteLine();
            }
        }
    }
}
Was it helpful?

Solution

Instead of instantiating your template in OnPreRender method you need to use CreateChildControls method. This method is used to create child control. The cause of the problem is that your extender is added to controls collection too late in Page life cycle and that's why it can't be registered.

I can advise to use Composite control as base one instead of WebControl. Dino Esposito has a very good article related to this area: http://msdn.microsoft.com/en-us/library/aa479016.aspx.

One more note: if you create custom server control which inherited from WebControl and want that two instance in the same naming container (like page or etc) have different child controls ids then you need to mark you custom control with INamingContainer interface. More information can be found in this article: http://msdn.microsoft.com/en-us/library/system.web.ui.inamingcontainer.aspx

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top