none
Displaying ListItems in a Visual Web Part

    Question

  • I am trying to create a visual web Part to display the latest 5 announcement list items.

    I need the Announcement List Item Title to show up as a link and any attachments(only pictures) to be displayed right above it. I am planning on refreshing the web part every 15-20 minutes to be able to show the latest announcement. 

    Something like this:

    I dont know how and what the best asp control and page design would be to display these items.

    Here is the CAML Query with the rest of the code I have:

    using (SPSite oSPsite = new SPSite("http://mySharePointWebApp:Port#/"))
                {

                    using (SPWeb oSPWeb = oSPsite.OpenWeb())
                    {
                        oSPWeb.AllowUnsafeUpdates = true;
                       
                        // Fetch the List
                        SPList list = oSPWeb.Lists["Announcements"];
                       
                        SPQuery spQuery = new SPQuery();
                        //spQuery.Query = "<Where> <Eq> <FieldRef Name='Title' /> <Value Type='Text'></Value> </Eq> </Where>";
                        spQuery.Query = "";
                        spQuery.RowLimit = 5;
                       
                        // Show item in text box
                        SPListItemCollection oListCollection = list.GetItems(spQuery);
                        foreach (ListItem oListItem in oListCollection)
                        {
                            // What should I go with here?
                        }
                       
                    }
                }

    Thursday, March 15, 2012 6:28 PM

Answers

  • "But I am having a hard time understanding the repAnnouncements_ItemDataBound function though."

    Here is the basic idea.  You attach a collection of some sort to the data source.  That collection contains some number of items.  The template defined for the repeater describes how one single item should be rendered.  Each item in the collection will be rendered according to that template and placed, sequentially, where the Repeater is defined.  The ItemDataBound is an event that is fired every time an item is bound to the repeater.  Essentially the DataBind method we call in CreateChildControls will look something like this (obviously with a whole lot of other stuff omitted:

    public void DataBind()
    {
      foreach(object item in DataSource)
      {
        Template canvas = generateBlankTemplate();//this will basically be what is defined in the ascx page.  
        //types/methods are all made up
        ItemDataBound(canvas, item);//Note: not proper syntax -- here we are binding the item in the list to a blank template.  
        //This is where the code we're discussing is run.  It is a way of taking the information in one item from the data source 
        //and applying it to a blank template.
        addToPage(canvas);//or something else that is done with the canvas now that it's been populated.
      }
    }

    Try not to get hung up on the syntax of that example, it's all very made up.  If you looked at the real source code it would be overwhelming and probably not terribly helpful.

    The idea of databinding (here) is that you only need to define how to display a single item, and then you can give the repeater an entire collection of items and let it take care of applying your transformation for every single one.  This is much cleaner than say having 5 different Image controls on the page, 5 text hyperlinks, giving them all different names, setting each one, and so on.  That's just a whole lot of clutter.

    Moving on:

    repAnnouncements.DataSource = oListCollection; // IS this right?

    Yes.  Or rather, it's one correct option of several choices, and given what I know of your requirements this seems to be the best choice in this specific instance.

     string data = (string)e.Item.DataItem;//this will be typed as whatever item is bound to this item.  
                //It could be a DataRow (from a datatable), a SPListItem, etc.  You'll grab the url out of that.
    //For ex I have the announcements list with a title and an attachment so do you mean --> OlistItem["Url"];?

    Not really.  I mean it will look like:

    SPListItem item = (SPListItem) e.Item.DataItem;

    You will then use 'item' to get at whatever data you need.  If the items in your query will have a 'Url' property then you can add the following line right after the one above:

    string url = (string) item["Url"];

    You don't need to store it in another variable, but since we're using it in a number of places here it makes sense.

    "//TODO: set image url //Could you provide me with an example?"

    Well, it'll be almost exactly the same as dealing with the hyperlinks with only minor modifications, so I'd really rather not.  Let's break it down into each of the component steps; maybe I'll even do a few of them for you.

    1. We probably want to add a new variable for the Image element.  In some cases you won't need to, but it's really rather rare to grab a control and use it once without putting it in a variable.  Since there can often be a lot of them for this reason, it's often best to keep them together (at the top of the method usually) and often to sort them (by Control type, name, order they are seen in, or whatever else works best for you).
    2. The image will be a control of type "Image" (as opposed to "Hyperlink" in the previous cases).  Whenever we're listing the type (i.e. in the declaration and the cast) we'll use image.
    3. Variable name.  By convention, I always like to make sure that the variable name is identical to the Control's ID; it makes things easy to find, I can CTRL+F for it in both files and get the same element, etc.  So now we know what to call our variable.
    4. As for actually fetching the control, it's pretty much just copy/paste from the hyperlink example but using the ID of the image, not the hyperlink.  (Go look for it in the .ascx file, grab the ID, and put that as the string parameter).
    5. Now we need to determine what the URL of the image should be.  We have the relevant SPListItem from earlier, so we can call .Attachments on that.  If you spend some time looking through Intellisense + MSDN + Google you should be able to come up with the URL to that file.  I spend a LOT of time on the MSDN documentation pages when coding in C#, and you probably should to.  I can't say from memory, but my guess is there is simply some property somewhere that gives you exactly what you need, possibly with a little extra validation logic.
    6. In the same way that you found the URL of the attachment, you can find the property of Image that you need to set to set the source of the image.  Just look at the documentation, and if you can't figure it out based on the names, descriptions, and remarks alone, then move onto a Google search.
    7. As a last ditch effort for 5 and 6 you can re-post here if you really can't figure it out.
    • Marked as answer by Shimin Huang Friday, March 23, 2012 7:21 AM
    Tuesday, March 20, 2012 9:27 PM
  • I wouldn't think of databinding in a sharepoint context.  Just do research into databinding in general and do whatever you need to do to conform your sharepoint data to those examples.

    To be more specific: if you lookup databinding examples I'm sure you're going to see DataTables used heavily.  In a traditional ASP application that's often the primary means of storing a database query (rather than an SPListItemCollection).  DataTables are designed to play very well with databinding.  SPListItemCollection has a nice handy .ToDataTable method, so you can just use that to convert your query into a DataTable and then just follow examples you see all over the web.  In particular you're clearly seeing examples related to definding the binding in the markup (the use of <% Eval() %>).  I'm not an expert on the subject; I mostly just follow along with some of the few code samples that I've seen my co-workers use (or samples from the net).  Since those cases always use a DataTable when using the 'Eval' function and they use the column name as the argument, that's how I would do it here.  I would convert the SPListItemCollection into a datatable if I intended to use Eval().

    The ToDataTable method is nice, and often effective, but sometimes there will be custom formatting of the data, or for some other reason you need to alter this table significantly.  In that case it's easy enough to just create a blank datatable, add columns to it, and then add a row to the dataTable for each SPListItem that the query returns.  (This is more or less what the ToDataTable method is doing for you.)  This allows you to add columns that contain derived information, extra static field (i.e. a link/label), different datetime or numeric formatting, etc.    In your case the image url wouldn't be a column of the datatable, so you'd need to make your own so that you can calculate the image url for each item and ensure that the url is a column.  As long as the data isn't too large (here it's clearly not) the duplication of data isn't a major issue.  Depending on the specifics of your page, it may also be worth caching the DataTable at this point (just before you bind it to the Control).

    Now assuming you don't use the 'Eval()' in markup and instead stick with the code behind method I used in my previous example:

    the Attachments property is a collection, not a single item.  You'll need to ensure that there is at least one attachment, and then determine which one you want (the first one, the most recent one, the oldest one, some random one, error if there is more than one, whatever).

    Looking at the documentation for the SPAttachmentCollection Class we can see that it is just a collection of strings that are file name, so once you pick one you can just use the indexer (or whatever) which you can combine with the UrlPrefix (that's the folder that all of the files are in) and you have the URL.


    Wednesday, March 21, 2012 5:11 PM
  • I did see an example of binding it to the data table, but it looks like I dont really need to do it given my case.

    As I said, I 'm not an expert, but you brought up the concept of doing the binding in markup using Eval. As far as I know, you can't do that with an SPListItemCollection, you'll need a datatable. If you want to continue to do the binding in code as I stated you out with, then a datatable isn't needed.

    I am not sure if Im using the right search queries on google but I cant seem to find any sites explaining binding list items to Repeater controls.This is just a thought but could I possibly use the code that I already have and simply modify the controls to which it is bound?

    var editformUrl = list.Forms[PAGETYPE.PAGE_EDITFORM].ServerRelativeUrl;
                foreach (SPListItem item in oListCollection)
                {
                    var fullEditUrl = String.Format("{0}?ID={1}&source={2}",
                        editformUrl,
                        item.ID,
                        HttpUtility.UrlEncode(HttpContext.Current.Request.Url.OriginalString));
                   
                    if (item.Attachments.Count > 0)
                    {
                        Controls.Add(new HyperLink()
                        {
                            ImageUrl = SPUrlUtility.CombineUrl(item.Attachments.UrlPrefix, item.Attachments[0]),
                            NavigateUrl = fullEditUrl
                        });
                    }
                    Controls.Add(new LiteralControl(string.Format("<h2><a href='{1}'>{0}</a></h2>", item.Title, fullEditUrl)));
                }

    You can think of the item binding method as the body of a for loop such as that, yes.  I have already shown you how to get at the SPListItem for the 'Current iteration'.  The primary difference is that rather than always adding controls there is a template that already has a number of controls in it.  All of the static information can be entered there in a much more readable manor, and one in which HTML syntax can be checked at compile time.  You merely need to access the Controls from that template and modify them to incorporate the data from 'this item' rather than adding new controls.  The code between the two examples does parallels fairly well though, which is why it concerns me that you're having so much trouble converting it.

    IS there a way I could use this:


    foreach (String attachmentName in myListItem.Attachments)
       {
        String attachmentAbsoluteURL =
        myListItem.Attachments.UrlPrefix // gets the containing directory URL
        + attachmentName;
     
       //to get the file
        SPFile attachmentFile = myweb.GetFile(attachmentAbsoluteURL);

    }
    But will this actually render the images once bound?

    This doesn't seem particularly relevant to this, no.

    In all the commotion of figuring this out I have a simple question:

    Say I converted <asp:HyperLink ID="hypTextEditLink" runat="server"/> to

         <asp:Label ID="Title" runat="server" Text="Label"></asp:Label>
    how would I actually bind the repeaterControl itemTemplate item (Title) to myListItem["Title"]
    All the work I have been doing was pretty straightforward and involved
    1. Title.text = myListItem["Title"];   --> To pull from the list
    2. myListItem["Title"] = Title.text   --> To write to a list provided there was something in Title.Text.

    What would the equivalent of this be in regard to binding to a repeater control?
    Thanks

    Changing the hyperlink to a label really wouldn't change much of anything.  You would store it as a Label object, not as a Hyperlink, and cast it appropriately.  The SPListItem has been fetched in the binding event, so you now have the label and the list item and the line of code to set the text, as you have it, will run just fine (barring that it's 'Text' not 'test'

    As for setting the item based on the value; it's a label, you aren't inputting data, so I don't see how it's relevant.  If we're going to be having input fields for each line that can be edited and then saved back to the database we'll have some more work to do.

    • Marked as answer by Shimin Huang Friday, March 23, 2012 7:20 AM
    Wednesday, March 21, 2012 8:32 PM
  • You've been able to get the image url into a string now, yes, now you just need to set the actual Image control's property using that value, or you can set the ImageUrl property of one of the 'Hyperlink' controls like you did in your original code, but with one of the hyperlinks from the template.
    • Marked as answer by Shimin Huang Friday, March 23, 2012 7:20 AM
    Thursday, March 22, 2012 4:38 PM

All replies

  • The asp.net repeater control would be a good control to render the template. The repeater is one of the server control that can use databinding. It would be the best pick, because it has minimal overhead.

    You can databind the SharePoint list items using oListCollection.GetDataTable()

    There is more information about the repeater control on MSDN:
    http://msdn.microsoft.com/en-us/library/zzx23804(v=vs.85).aspx


    SharePoint Developer | @zeemanj | Blog

    Thursday, March 15, 2012 8:21 PM
  • I will take a look at the link you sent me.

    Could I possibly use an Image viewer web part (OOTB) or even a Silverlight web part to achieve this?

    Thursday, March 15, 2012 8:51 PM
  • hi,

          I am thinking that dataview webpart/content query webpart is a very much suitable solution for your case with out writing any single line of code.and you can use javascript to referesh your item in particular interval time.Please refer below url .

    http://www.c-sharpcorner.com/uploadfile/Roji.Joy/working-with-data-view-web-part-in-sharepoint-list/

    Thursday, March 15, 2012 9:21 PM
  • Balamurugan

    I cant seem to find the sata view web part under the web part gallery.

    Should I be looking elsewhere?

    Friday, March 16, 2012 6:05 PM
  • I don't know about your environment that how to deploy the solution or are you able to access sharepoint site via sharepoint designer in production server. But, if you can use SPD in your environment..Please refer below url how to insert dataview webpart using sharepoint desinger 2010 and it will give more flexible way to develop your current requirement without time consuming.

    http://onlinecoder.blogspot.com/2011/05/using-sharepoint-2010-data-view-web.html

    Friday, March 16, 2012 7:11 PM
  • Balamurugan

    That is a great link thanks!!

    But when I drag attachments into the dta view it simply shows the number of attachments but not the attachemtn content. I have attached images and Id like to show them above each title text.

    Any idea?

    Friday, March 16, 2012 8:22 PM
  • I've had lots of good experiences with both the Repeater (mentioned earlier) and the GridView controls for rendering data from queries in custom code.  GridViews are nice for the really simple cases; you can either bind the SPListItemCollection itself to the GridView, call .ToDataTable on the results and bind that, or create your own DataTable, populate it in your foreach, and then bind that to the GridView (that last one would be if there is a fair bit of custom formatting of the data).

    I haven't used the repeater a LOT, but when I have I've been quite satisfied with it's simplicity and effectiveness for extremely customized formatting of data.  I'd be a bit more concerned with the performance of the repeater for larger amounts of data but since you're setting a row limit of 5 that's clearly not going to be an issue in this case.  Since it appears you have a fair bit of customization of the results the repeater probably is the right choice here.

    Monday, March 19, 2012 5:08 PM
  • Hey Servy42

    Thanks for the input.

    Here is what I have been able to put together so far from various sources.

    It does not use a repeater but Im still trying to get them to align side by side as shown in my question (First post)

    right now it is aligned one below the other or should I say stacked.

    Im also tryin gto see if I can write a javascript to resize attached images automatically and create paging so users can click right and left to see earlier announcements as well.

    Any suggestions would greatly help.

    Thanks


     protected void Page_Load(object sender, EventArgs e)
            {
                SPWeb oSPWeb = SPContext.Current.Web;

                // Fetch the List 
                SPList list = oSPWeb.Lists["Announcements"];

                SPQuery spQuery = new SPQuery();
                spQuery.Query = "<Where> <Eq> <FieldRef Name='Title' /> <Value Type='Text'></Value> </Eq> </Where>";
                spQuery.Query = "";
                spQuery.RowLimit = 3;

                // Show item in text box 
                SPListItemCollection oListCollection = list.GetItems(spQuery);

                var editformUrl = list.Forms[PAGETYPE.PAGE_EDITFORM].ServerRelativeUrl;
                foreach (SPListItem item in oListCollection)
                {
                    var fullEditUrl = String.Format("{0}?ID={1}&source={2}",
                        editformUrl,
                        item.ID,
                        HttpUtility.UrlEncode(HttpContext.Current.Request.Url.OriginalString));
                    Controls.Add(new LiteralControl(string.Format("<h2><a href='{1}'>{0}</a></h2>", item.Title, fullEditUrl)));
                    if (item.Attachments.Count > 0)
                    {
                        Controls.Add(new HyperLink()
                        {
                            ImageUrl = SPUrlUtility.CombineUrl(item.Attachments.UrlPrefix, item.Attachments[0]),
                            NavigateUrl = fullEditUrl
                        });
                    }
                }

           }

    Monday, March 19, 2012 5:46 PM
  • So there are a number of issues with doing what you're doing there.

    1. (and most importantly) all of the controls that you've just added there will go away on post back.  If anything on the page posts back for any reason that information won't be there; if you need it you'll need to regenerate it.  Even just having a button that does something and doesn't need to read/edit this content will still wipe it, forcing it to be re-generated if you want it to stay there.  
    2. It all goes to the very bottom of the webpart.  At the very least you should add a Panel and add your controls to that panel so that there can be something after the dynamic content on the page without needing to worry about the ordering of content in page load.
    3. It's a lot harder to read; it doesn't break up into smaller pieces.  This is obviously more important when you have more columns/information to display and have more complicated logic for handling it, but being able to separate out the presentation from the data access is very helpful in understanding the code.  In particular, being able to put all of the HTML that's static for each row into markup is something I find rather useful.  (This is one of the few contexts in which I find dealing with markup when appropriate is preferable to trying to use code).
    Monday, March 19, 2012 6:16 PM
  • I understand what you are saying, but I have no issues rendering any of the information on post back. All I really want is to tjust be able align them side to side and see if I can actually use a paging mechanism and a method to refresh every 10minutes or so along with an Image resize function .

    Is this possible?

    Monday, March 19, 2012 6:22 PM
  • To align them horizontally put them in two cells in one row of a table.

    Paging will be much harder using the mechanism you've described, for exactly the reasons listed in section #1 of my previous reply.  If you're using a gridview you could simply use the gridview's built in paging mechanisms.  If you wanted to use your own, or use a repeater, then you could hold onto the SPListItemCollectionPosition object that you need to keep your place and re-generate the content.  You could probably do that with what you're doing here, but it would be harder.  Just adding a button to the page that fires a click event is harder if you're adding it dynamically.

    Refreshing is pretty easy; you just need to modify the header or add some javascript to handle it; Google should show you how to do that.  Note that doing so will reset you back to page 1 though.  If you want to just refresh the data of the current page that complicates matters significantly (if you're using this design); again, see reason #1 as you'll have to have a timer posting back, remembering the page you were on, etc.

    Not sure what you mean when you say image resize.  Do you just want the attached image to be something other than what it is?  You can edit the CSS for the image easily enough in either markup or code.

    Monday, March 19, 2012 7:14 PM
  • I am going to be frank with you. Im fairly new to the game and it has taken me a considerable amount of time to put this together and understand the process.

    Neverthless, yes if it makes sense to use a grid view or repeater I will do just that, but could you possibly point me in the right directions with links or code snippets to get me started.

    And by Image resize I meeant I would like to resize the images that users will attach while adding announcements by using a function (javascript,C#...I dont know which one will work) as I want all the images to be of the same size while displaying.

    I basically want it to look exaclty like my first post.

    Thanks again.

    Monday, March 19, 2012 7:20 PM
  • Markup on the .ascx page:

    <table>
        <tr>
            <asp:Repeater ID="repAnnouncements" runat="server">
                <ItemTemplate>
                    <td>
                        <asp:HyperLink ID="hypImageEditLink" runat="server">
                            <asp:Image ID="imgLink" runat="server" Width="100px" Height="100px"/>
                        </asp:HyperLink>
                        <asp:HyperLink ID="hypTextEditLink" runat="server" />
                    </td>
                </ItemTemplate>
            </asp:Repeater>
        </tr>
    </table>

    In the code behind (ascx.cs):

    protected override void CreateChildControls()
    {
        base.CreateChildControls();
    
        web = SPContext.Current.Web;
    
        repAnnouncements.ItemDataBound += new RepeaterItemEventHandler(repAnnouncements_ItemDataBound);
    
        //For you the data source will probably be the SPListItemCollection returned from your query.
        repAnnouncements.DataSource = new List<string>() { "http://google.com", "http://foo.com", "http://nowhere.net" };
        repAnnouncements.DataBind();
    }
    
    private void repAnnouncements_ItemDataBound(object sender, RepeaterItemEventArgs e)
    {
        HyperLink hypImageEditLink = (HyperLink)e.Item.FindControl("hypImageEditLink");
        HyperLink hypTextEditLink = (HyperLink)e.Item.FindControl("hypTextEditLink");
        string data = (string)e.Item.DataItem;//this will be typed as whatever item is bound to this item.  
        //It could be a DataRow (from a datatable), a SPListItem, etc.  You'll grab the url out of that.
        hypImageEditLink.NavigateUrl = data;
        hypTextEditLink.NavigateUrl = data;
        hypTextEditLink.Text = "Link";
    
        //TODO: set image url
    }

    So all of the static HTML content (formatting/CSS/display type stuff) can all go in the .ascx file just adding/manipulating content in the ItemTemplate.  (Normally you'd have a new row inside the template, not outside, but you want it all on the same row here.)  Anything that's dependent on data bound to that item goes in the code behind in the item data bound event.  There you can set the image url, change the link/link text, or whatever else you might need to do.

    To show the next page just get an SPListItemCollection that represents the next page (if you don't know how to do that you can look up paging in sharepoint; I can help you out if you have trouble) and set that new SPListItemCollection as the dataSource of the Repeater and call .DataBind to bind the new items.

    Monday, March 19, 2012 7:55 PM
  • Servy32

    This is great info. Let me understand the process, try it and get back to you.

    Oh and I am not sure if you understood me correctly with regard to paging. What I meant exactly was the possibility of having two arrows (left and right) inside my web part right beside the "table" (In this case) so when users click on, lets say the right arrow, the cells shift (in sets of 3 which is my row limit) to show the earlier three announcements. I hope this makes it a bit more clear.

    Thanks

    Monday, March 19, 2012 8:39 PM
  • Servy32

    This is great info. Let me understand the process, try it and get back to you.

    Oh and I am not sure if you understood me correctly with regard to paging. What I meant exactly was the possibility of having two arrows (left and right) inside my web part right beside the "table" (In this case) so when users click on, lets say the right arrow, the cells shift (in sets of 3 which is my row limit) to show the earlier three announcements. I hope this makes it a bit more clear.

    Thanks

    That's exactly what I thought you meant.  Unfortunately, SharePoint doesn't have the greatest paging support so you may be fighting against that a bit, but exposing what SharePoint does support to the presentation layer isn't hard at all using this technique.
    Monday, March 19, 2012 8:47 PM
  • protected override void CreateChildControls()
    {
       
    base.CreateChildControls();

        web
    = SPContext.Current.Web;

        repAnnouncements
    .ItemDataBound += new RepeaterItemEventHandler(repAnnouncements_ItemDataBound);

       
    //For you the data source will probably be the SPListItemCollection returned from your query.
        repAnnouncements
    .DataSource = new List<string>() { "http://google.com", "http://foo.com", "http://nowhere.net" };
        repAnnouncements
    .DataBind();
    }

    private void repAnnouncements_ItemDataBound(object sender, RepeaterItemEventArgs e)
    {
       
    HyperLink hypImageEditLink = (HyperLink)e.Item.FindControl("hypImageEditLink");
       
    HyperLink hypTextEditLink = (HyperLink)e.Item.FindControl("hypTextEditLink");
       
    string data = (string)e.Item.DataItem;//this will be typed as whatever item is bound to this item. 
       
    //It could be a DataRow (from a datatable), a SPListItem, etc.  You'll grab the url out of that.
        hypImageEditLink
    .NavigateUrl = data;
        hypTextEditLink
    .NavigateUrl = data;
        hypTextEditLink
    .Text = "Link";

       
    //TODO: set image url
    }

    Could you give me a quick sample on how to set the IMage URL or could I follow the same procedure from my previous code.
    I am also not very sure as to where exactly I would put my CAML query.

    Sorry to bother you with this again.

    Monday, March 19, 2012 9:07 PM
  • Your CAML query would go in CreateChildControls.  If you take a look at my comments, you'll see I tell you that the DataSource will be an SPListItemCollection for your case, so you'll perform a query just before databinding and bind the results of the query.  I used a List just as an example since my query won't match yours.  If a single query won't get you all of the information you need then you can use whatever data structure you want (DataTables are nice for this).  You can then pull out an SPListItem in the item binding method to access all of the data for that item.  You can follow the example of the hyperlinks for loading the Image control, and intellisense or the MSDN page for Image should tell you what property you need to set for the image URL.
    Tuesday, March 20, 2012 1:56 PM
  • Servy42

    Thanks

    But I am having a hard time understanding the repAnnouncements_ItemDataBound function though. But here are a couple of changes I have made and added comments where I may need correction.

    Could you please take a look and correct me where I am wrong.

    Here is the code:

    protected override void CreateChildControls()
            {
                base.CreateChildControls();

                
                SPWeb web = SPContext.Current.Web;
                 SPList list = web.Lists["Announcements"];

                
                 SPQuery spQuery = new SPQuery();
                          
                /*Make sure to put the <OrderBy> tag toward the end of the Query */

                spQuery.Query = " <Where><IsNotNull><FieldRef Name='Title' /></IsNotNull></Where> <OrderBy> <FieldRef Name='ID' Ascending='False' /> </OrderBy> ";
                spQuery.RowLimit = 5;
                SPListItemCollection oListCollection = list.GetItems(spQuery);


                repAnnouncements.ItemDataBound += new RepeaterItemEventHandler(repAnnouncements_ItemDataBound);
                //For you the data source will probably be the SPListItemCollection returned from your query.
                repAnnouncements.DataSource = oListCollection; // IS this right?
                repAnnouncements.DataBind();
            }

            private void repAnnouncements_ItemDataBound(object sender, RepeaterItemEventArgs e)
            {
                HyperLink hypImageEditLink = (HyperLink)e.Item.FindControl("hypImageEditLink");
                HyperLink hypTextEditLink = (HyperLink)e.Item.FindControl("hypTextEditLink");
                string data = (string)e.Item.DataItem;//this will be typed as whatever item is bound to this item.  
                //It could be a DataRow (from a datatable), a SPListItem, etc.  You'll grab the url out of that.
    //For ex I have the announcements list with a title and an attachment so do you mean --> OlistItem["Url"];?
                hypImageEditLink.NavigateUrl = data;
                hypTextEditLink.NavigateUrl = data;
                hypTextEditLink.Text = "Link";

                //TODO: set image url //Could you provide me with an example?

            }
        }
    }

    Tuesday, March 20, 2012 8:50 PM
  • "But I am having a hard time understanding the repAnnouncements_ItemDataBound function though."

    Here is the basic idea.  You attach a collection of some sort to the data source.  That collection contains some number of items.  The template defined for the repeater describes how one single item should be rendered.  Each item in the collection will be rendered according to that template and placed, sequentially, where the Repeater is defined.  The ItemDataBound is an event that is fired every time an item is bound to the repeater.  Essentially the DataBind method we call in CreateChildControls will look something like this (obviously with a whole lot of other stuff omitted:

    public void DataBind()
    {
      foreach(object item in DataSource)
      {
        Template canvas = generateBlankTemplate();//this will basically be what is defined in the ascx page.  
        //types/methods are all made up
        ItemDataBound(canvas, item);//Note: not proper syntax -- here we are binding the item in the list to a blank template.  
        //This is where the code we're discussing is run.  It is a way of taking the information in one item from the data source 
        //and applying it to a blank template.
        addToPage(canvas);//or something else that is done with the canvas now that it's been populated.
      }
    }

    Try not to get hung up on the syntax of that example, it's all very made up.  If you looked at the real source code it would be overwhelming and probably not terribly helpful.

    The idea of databinding (here) is that you only need to define how to display a single item, and then you can give the repeater an entire collection of items and let it take care of applying your transformation for every single one.  This is much cleaner than say having 5 different Image controls on the page, 5 text hyperlinks, giving them all different names, setting each one, and so on.  That's just a whole lot of clutter.

    Moving on:

    repAnnouncements.DataSource = oListCollection; // IS this right?

    Yes.  Or rather, it's one correct option of several choices, and given what I know of your requirements this seems to be the best choice in this specific instance.

     string data = (string)e.Item.DataItem;//this will be typed as whatever item is bound to this item.  
                //It could be a DataRow (from a datatable), a SPListItem, etc.  You'll grab the url out of that.
    //For ex I have the announcements list with a title and an attachment so do you mean --> OlistItem["Url"];?

    Not really.  I mean it will look like:

    SPListItem item = (SPListItem) e.Item.DataItem;

    You will then use 'item' to get at whatever data you need.  If the items in your query will have a 'Url' property then you can add the following line right after the one above:

    string url = (string) item["Url"];

    You don't need to store it in another variable, but since we're using it in a number of places here it makes sense.

    "//TODO: set image url //Could you provide me with an example?"

    Well, it'll be almost exactly the same as dealing with the hyperlinks with only minor modifications, so I'd really rather not.  Let's break it down into each of the component steps; maybe I'll even do a few of them for you.

    1. We probably want to add a new variable for the Image element.  In some cases you won't need to, but it's really rather rare to grab a control and use it once without putting it in a variable.  Since there can often be a lot of them for this reason, it's often best to keep them together (at the top of the method usually) and often to sort them (by Control type, name, order they are seen in, or whatever else works best for you).
    2. The image will be a control of type "Image" (as opposed to "Hyperlink" in the previous cases).  Whenever we're listing the type (i.e. in the declaration and the cast) we'll use image.
    3. Variable name.  By convention, I always like to make sure that the variable name is identical to the Control's ID; it makes things easy to find, I can CTRL+F for it in both files and get the same element, etc.  So now we know what to call our variable.
    4. As for actually fetching the control, it's pretty much just copy/paste from the hyperlink example but using the ID of the image, not the hyperlink.  (Go look for it in the .ascx file, grab the ID, and put that as the string parameter).
    5. Now we need to determine what the URL of the image should be.  We have the relevant SPListItem from earlier, so we can call .Attachments on that.  If you spend some time looking through Intellisense + MSDN + Google you should be able to come up with the URL to that file.  I spend a LOT of time on the MSDN documentation pages when coding in C#, and you probably should to.  I can't say from memory, but my guess is there is simply some property somewhere that gives you exactly what you need, possibly with a little extra validation logic.
    6. In the same way that you found the URL of the attachment, you can find the property of Image that you need to set to set the source of the image.  Just look at the documentation, and if you can't figure it out based on the names, descriptions, and remarks alone, then move onto a Google search.
    7. As a last ditch effort for 5 and 6 you can re-post here if you really can't figure it out.
    • Marked as answer by Shimin Huang Friday, March 23, 2012 7:21 AM
    Tuesday, March 20, 2012 9:27 PM
  • Servy42

    Thanks for the explanation. I have been trying to just look up on how to bind repeater controls with sharepoint list items and from most of the sites I hit, this is what I got.  I figured this is the best way to get started, than trying to understand your code.

    SPQuery spQuery = new SPQuery();

    spQuery.Query = //Whatever Query I need

    SPListItemCollection items = list.GetItems(spQuery);

    DocumentRepeater.DataSource = items;

    DocumentRepeater.DataBind();   

    I understand this just fine. And I also took a look at how they were bound to the repeater controls. for example:

    <asp:Repeater ID="repAnnouncements" runat="server">
                <ItemTemplate>
                    <td>
                        <asp:HyperLink ID="hypImageEditLink" runat="server">
                            <asp:Image ID="imgLink" runat="server" Width="100px" Height="100px"/>
                        </asp:HyperLink>
                       
                    <%# DataBinder.Eval(Container.DataItem, "identifier") %> // This would display all values under that particular identifier. Is it possible for me to use this to render the announcements 'Title' like this: item["Title"] in place of 'identifier'

                        <asp:HyperLink ID="hypTextEditLink" runat="server" Associated />
                      
                    </td>
                </ItemTemplate>
            </asp:Repeater>

     

    I am assuming this would probably not be possible as we are trying to get and set the url of the image and trying to provide a link to edit them as well.

    I cannot seem to wrap my head around this part though:

    private void repAnnouncements_ItemDataBound(object sender, RepeaterItemEventArgs e)
            {
                HyperLink hypImageEditLink = (HyperLink)e.Item.FindControl("hypImageEditLink");
                HyperLink hypTextEditLink = (HyperLink)e.Item.FindControl("hypTextEditLink");
                SPListItem item = (SPListItem)e.Item.DataItem;//this will be typed as whatever item is bound to this item. 
                string url = (string) item["Url"];
                //It could be a DataRow (from a datatable), a SPListItem, etc.  You'll grab the url out of that.
                hypImageEditLink.NavigateUrl = data;
                hypTextEditLink.NavigateUrl = data;
                hypTextEditLink.Text = "Link";

                //TODO: set image url
                string imageUrl= item.Attachments.UrlPrefix; // I am taking a long shot at this?

            }

    It will be great if you can point me to a  link(s) that illustrates/explains "Binding data to Repeater controls from Sharepoint lists".

    I have been developing two other visual web parts for which I have just been using controls like labels, buttons, textboxes and retrieving or writing data to and from the lists seem pretty straightforward. Unfortunately I am just not able to figure this retrieval structure out.

    Thanks

     

    Wednesday, March 21, 2012 4:37 PM
  • I wouldn't think of databinding in a sharepoint context.  Just do research into databinding in general and do whatever you need to do to conform your sharepoint data to those examples.

    To be more specific: if you lookup databinding examples I'm sure you're going to see DataTables used heavily.  In a traditional ASP application that's often the primary means of storing a database query (rather than an SPListItemCollection).  DataTables are designed to play very well with databinding.  SPListItemCollection has a nice handy .ToDataTable method, so you can just use that to convert your query into a DataTable and then just follow examples you see all over the web.  In particular you're clearly seeing examples related to definding the binding in the markup (the use of <% Eval() %>).  I'm not an expert on the subject; I mostly just follow along with some of the few code samples that I've seen my co-workers use (or samples from the net).  Since those cases always use a DataTable when using the 'Eval' function and they use the column name as the argument, that's how I would do it here.  I would convert the SPListItemCollection into a datatable if I intended to use Eval().

    The ToDataTable method is nice, and often effective, but sometimes there will be custom formatting of the data, or for some other reason you need to alter this table significantly.  In that case it's easy enough to just create a blank datatable, add columns to it, and then add a row to the dataTable for each SPListItem that the query returns.  (This is more or less what the ToDataTable method is doing for you.)  This allows you to add columns that contain derived information, extra static field (i.e. a link/label), different datetime or numeric formatting, etc.    In your case the image url wouldn't be a column of the datatable, so you'd need to make your own so that you can calculate the image url for each item and ensure that the url is a column.  As long as the data isn't too large (here it's clearly not) the duplication of data isn't a major issue.  Depending on the specifics of your page, it may also be worth caching the DataTable at this point (just before you bind it to the Control).

    Now assuming you don't use the 'Eval()' in markup and instead stick with the code behind method I used in my previous example:

    the Attachments property is a collection, not a single item.  You'll need to ensure that there is at least one attachment, and then determine which one you want (the first one, the most recent one, the oldest one, some random one, error if there is more than one, whatever).

    Looking at the documentation for the SPAttachmentCollection Class we can see that it is just a collection of strings that are file name, so once you pick one you can just use the indexer (or whatever) which you can combine with the UrlPrefix (that's the folder that all of the files are in) and you have the URL.


    Wednesday, March 21, 2012 5:11 PM
  • I did see an example of binding it to the data table, but it looks like I dont really need to do it given my case.

    I am not sure if Im using the right search queries on google but I cant seem to find any sites explaining binding list items to Repeater controls.This is just a thought but could I possibly use the code that I already have and simply modify the controls to which it is bound?

    var editformUrl = list.Forms[PAGETYPE.PAGE_EDITFORM].ServerRelativeUrl;
                foreach (SPListItem item in oListCollection)
                {
                    var fullEditUrl = String.Format("{0}?ID={1}&source={2}",
                        editformUrl,
                        item.ID,
                        HttpUtility.UrlEncode(HttpContext.Current.Request.Url.OriginalString));
                   
                    if (item.Attachments.Count > 0)
                    {
                        Controls.Add(new HyperLink()
                        {
                            ImageUrl = SPUrlUtility.CombineUrl(item.Attachments.UrlPrefix, item.Attachments[0]),
                            NavigateUrl = fullEditUrl
                        });
                    }
                    Controls.Add(new LiteralControl(string.Format("<h2><a href='{1}'>{0}</a></h2>", item.Title, fullEditUrl)));
                }

    OR

    IS there a way I could use this:


    foreach (String attachmentName in myListItem.Attachments)
       {
        String attachmentAbsoluteURL =
        myListItem.Attachments.UrlPrefix // gets the containing directory URL
        + attachmentName;
     
       //to get the file
        SPFile attachmentFile = myweb.GetFile(attachmentAbsoluteURL);

    }
    But will this actually render the images once bound?
    In all the commotion of figuring this out I have a simple question:

    Say I converted <asp:HyperLink ID="hypTextEditLink" runat="server"/> to

         <asp:Label ID="Title" runat="server" Text="Label"></asp:Label>
    how would I actually bind the repeaterControl itemTemplate item (Title) to myListItem["Title"]
    All the work I have been doing was pretty straightforward and involved
    1. Title.text = myListItem["Title"];   --> To pull from the list
    2. myListItem["Title"] = Title.text   --> To write to a list provided there was something in Title.Text.

    What would the equivalent of this be in regard to binding to a repeater control?
    Thanks

    Wednesday, March 21, 2012 6:44 PM
  • I did see an example of binding it to the data table, but it looks like I dont really need to do it given my case.

    As I said, I 'm not an expert, but you brought up the concept of doing the binding in markup using Eval. As far as I know, you can't do that with an SPListItemCollection, you'll need a datatable. If you want to continue to do the binding in code as I stated you out with, then a datatable isn't needed.

    I am not sure if Im using the right search queries on google but I cant seem to find any sites explaining binding list items to Repeater controls.This is just a thought but could I possibly use the code that I already have and simply modify the controls to which it is bound?

    var editformUrl = list.Forms[PAGETYPE.PAGE_EDITFORM].ServerRelativeUrl;
                foreach (SPListItem item in oListCollection)
                {
                    var fullEditUrl = String.Format("{0}?ID={1}&source={2}",
                        editformUrl,
                        item.ID,
                        HttpUtility.UrlEncode(HttpContext.Current.Request.Url.OriginalString));
                   
                    if (item.Attachments.Count > 0)
                    {
                        Controls.Add(new HyperLink()
                        {
                            ImageUrl = SPUrlUtility.CombineUrl(item.Attachments.UrlPrefix, item.Attachments[0]),
                            NavigateUrl = fullEditUrl
                        });
                    }
                    Controls.Add(new LiteralControl(string.Format("<h2><a href='{1}'>{0}</a></h2>", item.Title, fullEditUrl)));
                }

    You can think of the item binding method as the body of a for loop such as that, yes.  I have already shown you how to get at the SPListItem for the 'Current iteration'.  The primary difference is that rather than always adding controls there is a template that already has a number of controls in it.  All of the static information can be entered there in a much more readable manor, and one in which HTML syntax can be checked at compile time.  You merely need to access the Controls from that template and modify them to incorporate the data from 'this item' rather than adding new controls.  The code between the two examples does parallels fairly well though, which is why it concerns me that you're having so much trouble converting it.

    IS there a way I could use this:


    foreach (String attachmentName in myListItem.Attachments)
       {
        String attachmentAbsoluteURL =
        myListItem.Attachments.UrlPrefix // gets the containing directory URL
        + attachmentName;
     
       //to get the file
        SPFile attachmentFile = myweb.GetFile(attachmentAbsoluteURL);

    }
    But will this actually render the images once bound?

    This doesn't seem particularly relevant to this, no.

    In all the commotion of figuring this out I have a simple question:

    Say I converted <asp:HyperLink ID="hypTextEditLink" runat="server"/> to

         <asp:Label ID="Title" runat="server" Text="Label"></asp:Label>
    how would I actually bind the repeaterControl itemTemplate item (Title) to myListItem["Title"]
    All the work I have been doing was pretty straightforward and involved
    1. Title.text = myListItem["Title"];   --> To pull from the list
    2. myListItem["Title"] = Title.text   --> To write to a list provided there was something in Title.Text.

    What would the equivalent of this be in regard to binding to a repeater control?
    Thanks

    Changing the hyperlink to a label really wouldn't change much of anything.  You would store it as a Label object, not as a Hyperlink, and cast it appropriately.  The SPListItem has been fetched in the binding event, so you now have the label and the list item and the line of code to set the text, as you have it, will run just fine (barring that it's 'Text' not 'test'

    As for setting the item based on the value; it's a label, you aren't inputting data, so I don't see how it's relevant.  If we're going to be having input fields for each line that can be edited and then saved back to the database we'll have some more work to do.

    • Marked as answer by Shimin Huang Friday, March 23, 2012 7:20 AM
    Wednesday, March 21, 2012 8:32 PM
  • Servy 42

    Would this work?

    protected override void CreateChildControls()
    {
      base.CreateChildControls();
               
                 SPWeb web = SPContext.Current.Web;
                  SPList list = web.Lists["Announcements"];
                
                  SPQuery spQuery = new SPQuery();
                          
                 /*Make sure to put the <OrderBy> tag toward the end of the Query */
     
                spQuery.Query = " <Where><IsNotNull><FieldRef Name='Title' /></IsNotNull></Where> <OrderBy> <FieldRef Name='ID'              Ascending='False' /> </OrderBy> ";
                 spQuery.RowLimit = 5;
                 SPListItemCollection oListCollection = list.GetItems(spQuery);
     
                repeatMyAnnouncements.ItemDataBound += new RepeaterItemEventHandler(repAnnouncements_ItemDataBound);
                 //For you the data source will probably be the SPListItemCollection returned from your query.
                 repAnnouncements.DataSource = oListCollection;
        repeatMyAnnouncements.DataBind();
    }


    private void repeatMyAnnouncements_ItemDataBound(object sender, RepeaterItemEventArgs e)
    {
        HyperLink hypImageEditLink = (HyperLink)e.Item.FindControl("hypImageEditLink");
        HyperLink hypTextEditLink = (HyperLink)e.Item.FindControl("hypTextEditLink");
        SPListItem item = (SPListItem)e.Item.DataItem;
        SPAttachmentCollection attachments = item.Attachments;
        string url = (string) item["Url"];
     
        hypImageEditLink.NavigateUrl = data;
        hypTextEditLink.NavigateUrl = data;
        hypTextEditLink.Text = "Link";
     
        //setting to _layouts
        //string imageUrl = item.Web.Url + "/_layouts/images/defaultannouncementimage.png"
        if (attachments.Count > 0)
        {
            //TODO: set image url
            imageUrl= attachments.UrlPrefix + attachments[0]
        }
    }

    Thursday, March 22, 2012 2:06 PM
  • You've been able to get the image url into a string now, yes, now you just need to set the actual Image control's property using that value, or you can set the ImageUrl property of one of the 'Hyperlink' controls like you did in your original code, but with one of the hyperlinks from the template.
    • Marked as answer by Shimin Huang Friday, March 23, 2012 7:20 AM
    Thursday, March 22, 2012 4:38 PM