Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations dencom on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Problems with template property in custom web control

Status
Not open for further replies.

jgd1234567

Programmer
May 2, 2007
68
GB
Hi, i have created my own custom repeater which inherits from the standard repeater and the only difference is that it contains an EmptyDataTemplate.

public class Repeater : System.Web.UI.WebControls.Repeater
{
private ITemplate _emptyDataTemplate;

[Browsable(false)]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate EmptyDataTemplate
{
get { return _emptyDataTemplate; }
set { _emptyDataTemplate = value; }
}

protected override void OnPreRender(EventArgs e)
{
// If there is no data then we don't wish to render anything (including the header and the footer)
if (this.Items.Count == 0)
{
// If an empty data template has been provided then display it
if (this.EmptyDataTemplate != null)
{
Control templateContainer = new Control();
this.EmptyDataTemplate.InstantiateIn(templateContainer);
this.Controls.Add(templateContainer);
}
}
else
base.OnPreRender(e);
}
}

Then on my page i am able to say:

<custom:Repeater ID="rptEvents" runat="server" DataSourceID="objEvents">
<ItemTemplate>
event info
</ItemTemplate>
<EmptyDataTemplate>
<custom:Repeater ID="rptCategories" runat="server" DataSourceID="objCategories">
<ItemTemplate>
category info
</ItemTemplate>
<EmptyDataTemplate>
<p>No events or categories.</p>
</EmptyDataTemplate>
</custom:Repeater>
<asp:ObjectDataSource ID="objCategories" runat="server" SelectMethod="GetCategories" TypeName="Category">
</asp:ObjectDataSource>
</EmptyDataTemplate>
</custom:Repeater>
<asp:ObjectDataSource ID="objEvents" runat="server" SelectMethod="GetEvents" TypeName="Event">
</asp:ObjectDataSource>

However if the outer repeater is empty the inner repeater always renders as if it was empty. I tried replacing the inner repeater with a standard repeater and it rendered fine but this doesn't allow me to have an EmptyDataTemplate for my inner repeater.

Appreciate if someone can help.

Thanks
 
this behavior should be expected. the rptcategories repeater is contained within the rptevents.emptydatatemplate. so if no events are returned the rptevents.emptydatatemplate is rendered. results from objcategories are bound to rptcategories. if the results are empty the header, footer and emptydatatemplate are rendered.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Hi sorry i just re read my post and probably didn't make it clear. I only want the rptCategories to render if the rptEvents is empty therefore i placed it in the EmptyDataTemplate of rptEvents. However the above code does not work as when there are no events then no categories are displayed. If i use the standard <asp:Repeater instead of <custom:Repeater for the categories repeater then the categories are shown but i wish to use my custom repeater as i need to have an EmptyDataTemplate if no events and no categories are found.

Sorry if that still doesn't make sense but i can't think how i can explain it clearer.
 
does the categories repeater still render when there are events? if so the problem is the rendering logic within the custom repeater.

you'll need to post the custom repeater logic if this is the case and you need help finding the problem.

Jason Meckley
Programmer
Specialty Bakers, Inc.
 
Hi, damn no events are being displayed at all now even though there are some. I tried stepping through the code and it appears the problem is that this.Items.Count == 0 always returns true (therefore the EmptyDataTemplate is always rendered). I have executed the code in the Pre_Render which i assumed was after the databinding but i don't know where else to do it. Further help greatly appreciated. Thanks so far.
 
I recognise that blog flixon.com, it's mine :). It was a temporary solution i come up with until i realized this new problem. I actually based my solution around the second link you posted. I tried implementing the solution someone suggests in the comments by moving my code to the item created event like so:

protected override void OnItemCreated(RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Footer && this.Items.Count == 0)
{
// If an empty data template has been provided then display it
if (this.EmptyDataTemplate != null)
{
Control templateContainer = new Control();
this.EmptyDataTemplate.InstantiateIn(templateContainer);
this.Controls.Add(templateContainer);
}
}
else
base.OnItemCreated(e);
}

but the header and footer templates still rendered so i added:

this.Controls.Clear();

above where i add the template to the controls collection and it removed the header template but i am still getting a footer template.

Appreciate further help.
 
What I'd do is override the DataBind() method and examine if the DataSource has any items in it (you may have to assume the source implements a choice of interfaces [ICollection, IList, IListSource]), then set a private boolean variable called "HasItems" that's stored in ViewState.

I'd then override the Render() method thusly:

Code:
if( HasItems )
    base.Render();
else
    //render empty template.



MCP, MCTS - .NET Framework 2.0 Web Applications
 
Hi i like this idea but i can't see how i can detect whether the data source is empty in the databind method. I tried:

public override void DataBind()
{
int numItems = this.Items.Count;
bool isEmpty = this.DataSource == null;

base.DataBind();
}

but when there was some events and when there wasn't numItems equalled 0 and isEmpty was always true.

Appreciate further help. Thanks
 
You'd have to do something like this (untested):

Code:
public bool IsEmpty
{
    get { return ViewState["IsEmpty"] as bool? ?? false; }
    set { ViewState["IsEmpty"] = value; }
}

public override void DataBind()
{
    if (DataSource == null)
        IsEmpty = true;
    else if (DataSource is IList)
        IsEmpty = ((IList)DataSource).Count == 0;
    else if (DataSource is ICollection)
        IsEmpty = ((ICollection)DataSource).Count == 0;
    else if (DataSource is IListSource)
        IsEmpty = ((IListSource)DataSource).GetList().Count == 0;

    base.DataBind();
}

protected override void Render(HtmlTextWriter writer)
{
    if( !IsEmpty )
        base.Render(writer);

    //otherwise render the empty template instead
}

MCP, MCTS - .NET Framework 2.0 Web Applications
 
FYI - in a quick Google after I posted, I figured out that IList actually implements ICollection, so one of the first tweaks you can make to the above is to remove the IList check (it's redundant since an IList is an ICollection).

MCP, MCTS - .NET Framework 2.0 Web Applications
 
Hi this gave me the error:

Using the generic type 'System.Collections.Generic.ICollection<T>' requires '1' type arguments

I've tried searching for a solution but have come up with nothing.
 
Reference System.Collections in the using directives in the class file (right click on ICollection and choose "Resolve"->"using System.Collections"). The problem is that you're referencing System.Collections.Generic, but you also need just System.Collections.

MCP, MCTS - .NET Framework 2.0 Web Applications
 
Hi cheers, i really should have spotted that one. However i have one further problem. If i have the following in my Render method:

Control templateContainer = new Control();
this.EmptyDataTemplate.InstantiateIn(templateContainer);
templateContainer.RenderControl(writer);

It doesn't work if the EmptyDataTemplate contains additional controls.

But placing the following works fine the PreRender:

Control templateContainer = new Control();
this.EmptyDataTemplate.InstantiateIn(templateContainer);
this.Controls.Add(templateContainer);

But DataBind is called after PreRender therefore the IsEmpty property is never set.

Appreciate your help, i'm sure will be the last time. Thanks
 
Two things. You should probably call DataBind() before PreRender, but I think you should be able to do something like this:

Code:
private Control templateContainer;

//in CreateChildControls
templateContainer = new Control();
if( EmptyDataTemplate != null )
    EmptyDataTemplate.InstantiateIn(templateContainer);
Controls.Add(templateContainer);

//in Render
if( !IsEmpty )
{
     Controls.Remove( templateContainer );
     base.Render();
}
else
    templateContainer.RenderControl(writer);

Tell me if it doesn't work and I'll try to actually compile, debug and run the control.

MCP, MCTS - .NET Framework 2.0 Web Applications
 
Hi apologies for not getting back to you sooner but i have been away the last few days. I appreciate your help alot but am afraid i am still having problems. For some reason IsEmpty seems to always be true but nothing renders on the page.

Appreciate your help once more. Here is the code i have been using:

Code:
public class Repeater : System.Web.UI.WebControls.Repeater
{
    private Control _templateContainer;
    private ITemplate _emptyDataTemplate;

    [Browsable(false)]
    [PersistenceMode(PersistenceMode.InnerProperty)]
    public ITemplate EmptyDataTemplate
    {
        get { return _emptyDataTemplate; }
        set { _emptyDataTemplate = value; }
    }

    public bool IsEmpty
    {
        get { return ViewState["IsEmpty"] as bool? ?? false; }
        set { ViewState["IsEmpty"] = value; }
    }

    public override void DataBind()
    {
        if (this.DataSource == null)
            this.IsEmpty = true;
        else if (this.DataSource is ICollection)
            this.IsEmpty = ((ICollection)DataSource).Count == 0;
        else if (this.DataSource is IListSource)
            this.IsEmpty = ((IListSource)DataSource).GetList().Count == 0;

        base.DataBind();
    }

    protected override void CreateChildControls()
    {
        _templateContainer = new Control();

        if (this.EmptyDataTemplate != null)
        {
            this.EmptyDataTemplate.InstantiateIn(_templateContainer);
            this.Controls.Add(_templateContainer);
        }
        
        base.CreateChildControls();
    }

    protected override void Render(HtmlTextWriter writer)
    {
        if (!this.IsEmpty)
        {
            this.Controls.Remove(_templateContainer);
            base.Render(writer);
        }
        else
            _templateContainer.RenderControl(writer);
    }
}
 
Giving a second glance at the code, I think there's an easier way to determine IsEmpty than the one I originally proposed, but this worked for me:

Code:
    public class Repeater : System.Web.UI.WebControls.Repeater
    {
        private Control _templateContainer;
        private ITemplate _emptyDataTemplate;

        [Browsable(false)]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        public ITemplate EmptyDataTemplate
        {
            get { return _emptyDataTemplate; }
            set { _emptyDataTemplate = value; }
        }

        public bool IsEmpty
        {
            get { return [b]base.Items.Count == 0;[/b] }
        }

        protected override void CreateChildControls()
        {
            _templateContainer = new Control();

            if (this.EmptyDataTemplate != null)
            {
                this.EmptyDataTemplate.InstantiateIn(_templateContainer);
                this.Controls.Add(_templateContainer);
            }

            base.CreateChildControls();
        }

        protected override void Render(HtmlTextWriter writer)
        {
            if (!this.IsEmpty)
            {
                this.Controls.Remove(_templateContainer);
                base.Render(writer);
            }
            else
                _templateContainer.RenderControl(writer);
        }
    }

Try running the above control in your environment, and write back if something's not working.

MCP, MCTS - .NET Framework 2.0 Web Applications
 
Hi cheers i am unable to test this at the moment but i will get back to you as soon as possible. Thanks
 
Hi, IsEmpty is now successfully calculated but when i place a repeater within the EmptyDataTemplate it does not render at all. Appreciate the help so far.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top