views:

229

answers:

2

The link below shows a listview composite control in its most basic form however I can't seem to extend this to what I'm trying to do.

http://stackoverflow.com/questions/92689/how-to-define-listview-templates-in-code

My listview has 1 tablerow with 2 fields. The first field contains 1 element whilst the second field contains 2 elements as shown below.

<asp:ListView ID="lstArticle" runat="server">
    <LayoutTemplate>
            <table id="itemPlaceholder" runat="server">

            </table>
    </LayoutTemplate>
    <ItemTemplate>
    <div class="ctrlArticleList_rptStandardItem">
        <table width="100%">
            <tr>
                <td valign="top" class="ctrlArticleList_firstColumnWidth">
                    <a href="<%# GetURL(Container.DataItem) %>">
                        <%# GetImage(Container.DataItem)%>
                    </a>
                </td>
                <td valign="top">
                    <span class="ctrlArticleList_title">
                        <a href="<%# GetURL(Container.DataItem) %>">
                            <%# DataBinder.Eval(Container.DataItem, "Title") %>
                        </a>
                    </span>                            
                    <span class="ctrlArticleList_date">
                        <%# convertToShortDate(DataBinder.Eval(Container.DataItem, "ActiveDate"))%>
                    </span>                                
                    <div class="ctrlArticleList_standFirst">
                        <%# DataBinder.Eval(Container.DataItem, "StandFirst")%>
                    </div>
                </td>
            </tr>
        </table>
    </div>
    </ItemTemplate>

So to convert this I have to use the InstantiateIn method of the ITemplate for the layouttemplate and the ItemTemplate. Data is bound to the itemtemplate using the DataBinding event.

My question at the moment is how do I setup the ItemTemplate class and bind the values from the above ListView ItemTemplate. This is what I've managed so far:

private class LayoutTemplate : ITemplate
    {
        public void InstantiateIn(Control container)
        {
            var table = new HtmlGenericControl("table") { ID = "itemPlaceholder" };
            container.Controls.Add(table);
        }
    }

    private class ItemTemplate : ITemplate
    {
        public void InstantiateIn(Control container)
        {
            var tableRow = new HtmlGenericControl("tr");

            //first field in row
            var tableFieldImage = new HtmlGenericControl("td");
            var imageLink = new HtmlGenericControl("a");
            imageLink.ID = "imageLink";
            tableRow.Controls.Add(imageLink);

            // second field in row
            var tableFieldTitle = new HtmlGenericControl("td");
            var title = new HtmlGenericControl("a");
            tableFieldTitle.Controls.Add(title);
            tableRow.Controls.Add(tableFieldTitle);

            //Bind the data with the controls
            tableRow.DataBinding += BindData;

            //add the controls to the container
            container.Controls.Add(tableRow);
        }

        public void BindData(object sender, EventArgs e)
        {
            var container = (HtmlGenericControl)sender;
            var dataItem = ((ListViewDataItem)container.NamingContainer).DataItem;

            container.Controls.Add(new Literal() { Text = dataItem.ToString() });
        }

One of the functions in the code behind used in the ListView just for an example is:

        public string GetURL(object objArticle)
    {
        ArticleItem articleItem = (ArticleItem)objArticle;

        Article article = new Article(Guid.Empty, articleItem.ContentVersionID);

        return GetArticleURL(article);
    }

Summary

What do I need to do to convert the following into a composite control:

GetURL(Container.DataItem) GetImage(Container.DataItem)

GetURL(Container.DataItem) DataBinder.Eval(Container.DataItem, "Title")

convertToShortDate(DataBinder.Eval(Container.DataItem, "ActiveDate"))

DataBinder.Eval(Container.DataItem, "StandFirst")

A: 

Hey,

In your control, you don't need to generate the <%# binding; rather, you can just get the bound URL on the server and assign it to a link, create a HyperLink and do:

var link = new HyperLink();
link.NavigateUrl = GetUrl(dataItem);
link.ImageUrl = GetImage(dataItem);

Where the dataItem is the data item for the specific row, which you get within the item template binddata method, right?

Am I off base?

HTH.

Brian
Thanks for the response, not sure what you mean "Where the dataItem is the data item for the specific row, which you get within the item template binddata method"
Paul
I mean BindData has this: var container = (HtmlGenericControl)sender; var dataItem = ((ListViewDataItem)container.NamingContainer).DataItem; Where dataitem is the underlying data source. data item would be your undering data object. If you are having trouble extracting fields out of this, you could use DataBinder.Eval or DataBinder.GetPropertyValue to extract the value out of the object generically.
Brian
I'm lost :)1. Am I on the right track with the ItemTemplate class? - I've since created the table/row/td/spans/divs elements and their corresponding attributes so now it should reflect the design page.2.Do I use server control instead of html control (eg HyperLink instead of <a>)3.Do I bind the data at the same points as in the design page. So there are 6 times when I need to bind the data.4.In the design code there are 2 types of data binding used. The first passes a data object to a function and the second gets the field name from the data object. How is this done in ITemplate class?
Paul
1) I'm not sure the reason for an ItemTemplate class; is this for code reuse? I personally don't like to create programmatic templates unless I have to, because they are a lot more work. But you can programmably create controls and elements like you are doing, which leads to 2) You can use either, but where you bind or set data values to, I think it's easier to use the server cotnrols. 3) It depends, do you need to? You can prevent entra bindings by storing a flag, and skipping when its true. 4) Data can be passed in via ItemTemplate constructor (field name can be).
Brian
Paul
You could create a base class that implements ITemplate, make instantiatein method abstract, and put these default properties here with getters and setters. That way, when you create the template, you can use C#'s new property setting features with {} to assign all of the values, and not have to use constructor overloads for convenience. What else are you still having issues with?
Brian
I guess just 2 final questions. The first regards your comment about properties. To confirm, I would still have to pass the values of the properties (that were dynamically set by someone using the composite control) into this base class via the base class constructor. Then the ItemTemplate and Alternating template implement the base class and these default properties. The second question:As I'm using the method 'public void InstantiateIn(Control container)' then should I be using the 'container' parameter? ATM I'm passing a data object and just need to know how to iterate through it-foreach...
Paul
2) The container property is the parent control to add controls to... so yes, you would have to. Also, one other thing I noticed; you identified the table as the placeholder, but I'm not sure the ListView replaces the placeholder with the item content. So make sure that it is rendering the table element. 1) Yes, you would have to pass as properties; you do that when you go to assign templates to the ItemTemplate and Alternating template (also in case you don't want to, the alternating template is optional). HTH. Let me know if its different.
Brian
Thanks. I can display a page that is styled exactly the same as the listview. I use the same ItemTemplate class to pass the itemtemplate properties and the alternating properties. Since the 'public void InstantiateIn(Control container)' method is run for itemtemplate and alternateitemtemplate I use a static counter variable to access the data eg...articleList[counter].ActiveDate...However I would have preferred to use the container parameter to iterate through the data like foreach (((ListViewDataItem)container.NamingContainer).DataItem item in container)...But all I keep getting is nulls.
Paul
Huh, DataItem should have a value during the initial binding, which is weird. It would be null on postbacks, if you don't bind. But then, it would be loaded from viewstate...
Brian
A: 

This isn't the answer - I'm adding to the previous comment - I don't know Yeah its weird, i just can't find any data associated with 'container' but will look into it later on. I've created a DataPager within the composite control but it doesn't page the ListView and doesn't show up at all (just the span element and ID)...I've raised another question but doubt if anyone will answer so hoped you might be able to help me out.

http://stackoverflow.com/questions/2424416/how-to-convert-a-datapager-in-a-composite-control

insanepaul