Sunday, 1 July 2012

SharePoint 2010 : ListView control with SPDataSource, ObjectDataSource


Whatever breath I am getting from hectic office work, I am spending on small tasks such as learning/ exploring few new/old asp.net controls such as ListView, DetailsView etc and how they can be integrated with SharePoint. I will be sharing my findings (rather I should say learnings) along with the working examples that I created in this post.

Note that these controls have been explored before by many people and hence don’t forget to google for them. However, I cannot miss to mention two people who has discovered near about all the hidden SharePoint controls making SharePoint development so easy.
1.  CAML girl Karine Bosch
2.  Zevenseas Robin
Robin loves to explore the OOB SharePoint controls and has blogged about SharePoint controls which Karine has missed. Search through his posts and you will come to know many SharePoint hidden gems

My focus however is more towards asp.net controls and today I will be writing on ListView control. It is new addition in asp.net 3.5 framework. ListView control is very powerful, flexible and it provides greater control than its counterparts GridView and DataList. It possess capabilities of both GridView and DataList controls to a such extend that I don’t think I will use GridView or DataList henceforth.

I created three examples as follows.

A. ListView control with SPDataSource
This is no code behind implementation. Here SPDataSource acts as DataSource to ListView control. If you are not aware of SPDataSource control, have a look at Chris O’brien’s series of posts

Code:

<style type="text/css">
    .TableCSS
    {
        border-style: solid;
        border: 1px solid #507CD1;
        width: 400px;
    }
    .TableCSS td
    {
        height: 25px;
    }
    .TableHeader
    {
        background-color: #507CD1;
        color: Black;
        font-weight: bold;
        font-family: calibri;
        font-size: 16px;
    }
    .noramlRowData
    {
        background-color: #EFF3FB;
        color: Black;
        font-size: 15px;
        font-weight: normal;
        font-family: calibri;
    }
    .alternatingRowData
    {
        background-color: White;
        color: Black;
        font-size: 15px;
        font-weight: normal;
        font-family: calibri;
    }
    .dataPager
    {
        color: BLUE;
        background-color: White;
        text-align: center;
        font-size: 14px;
        font-family: calibri;
    }
</style>
<SharePoint:SPDataSource runat="server" ID="dsProducts" DataSourceMode="List" SelectCommand="<Query><OrderBy><FieldRef Name='ID'/></OrderBy></Query>">
    <SelectParameters>
        <asp:Parameter Name="ListName" DefaultValue="Products" />
    </SelectParameters>
</SharePoint:SPDataSource>
<asp:ListView ID="listView" runat="server" DataSourceID="dsProducts">
    <LayoutTemplate>
        <table class="TableCSS" cellpadding="0" cellspacing="0">
            <tr class="TableHeader">
                <td align="center">
                    Product ID
                </td>
                <td>
                    Product Name
                </td>
                <td>
                    Product Cost
                </td>
            </tr>
            <tr id="ItemPlaceholder" runat="server">
            </tr>
            <tr>
                <td colspan="3" class="dataPager">
                    <asp:DataPager ID="DataPager1" runat="server" PageSize="5">
                        <Fields>
                            <asp:NextPreviousPagerField ButtonType="Link" ShowFirstPageButton="false" ShowPreviousPageButton="true"
                                ShowNextPageButton="False" PreviousPageText="<< Previous" RenderDisabledButtonsAsLabels="false" />
                            <asp:NumericPagerField />
                            <asp:NextPreviousPagerField ButtonType="Link" ShowLastPageButton="false" ShowNextPageButton="true"
                                ShowPreviousPageButton="False" FirstPageText="Previous" NextPageText="Next >>"
                                RenderDisabledButtonsAsLabels="false" />
                        </Fields>
                    </asp:DataPager>
                </td>
            </tr>
        </table>
    </LayoutTemplate>
    <ItemTemplate>
        <tr class="noramlRowData">
            <td align="center">
                <asp:Label ID="Label1" runat="server" Text='<%# Eval("ID")%>'> 
                </asp:Label>
            </td>
            <td>
                <asp:Label ID="Label2" runat="server" Text='<%# Eval("Title")%>'> 
                </asp:Label>
            </td>
            <td>
                रु&nbsp;<asp:Label ID="Label3" runat="server" Text='<%# Eval("Cost")%>'> 
                </asp:Label>
            </td>
        </tr>
    </ItemTemplate>
    <AlternatingItemTemplate>
        <tr class="alternatingRowData">
            <td align="center">
                <asp:Label ID="Label1" runat="server" Text='<%# Eval("ID")%>'> 
                </asp:Label>
            </td>
            <td>
                <asp:Label ID="Label2" runat="server" Text='<%# Eval("Title")%>'> 
                </asp:Label>
            </td>
            <td>
                रु&nbsp;<asp:Label ID="Label3" runat="server" Text='<%# Eval("Cost")%>'> 
                </asp:Label>
            </td>
        </tr>
    </AlternatingItemTemplate>
</asp:ListView>



Output:

Figure 1 : ListView with SPDataSource


B. ListView control with SPDataSource (Data grouping - rows flowing in multiple columns)
Unlike above example which matches GridView behavior, this example matches DataList control behavior where you can control the flow of rows into multiple columns.

Code:


<style type="text/css">
    .ImagePageHeader
    {
        width: 100%;
        text-align: center;
    }
   
    .ImagePageItem
    {
        border: 1px solid black;
        width: 100%;
        text-align: center;
        padding-top: 5px;
        margin: 5px 5px 5px 5px;
        font-weight: bold;
    }
   
    .ImagePageGroup
    {
        width: 30%;
        float: left;
        margin: 2px;
        padding: 2px;
    }
   
    .ImagePageMainLayout
    {
        padding: 2px;
        width: 500px;
    }
</style>
<SharePoint:SPDataSource runat="server" ID="dsProducts" DataSourceMode="List" SelectCommand="<Query><OrderBy><FieldRef Name='ID'/></OrderBy></Query>">
    <SelectParameters>
        <asp:Parameter Name="ListName" DefaultValue="Products" />
    </SelectParameters>
</SharePoint:SPDataSource>
<table width="520px" cellpadding="0" cellspacing="0">
    <tr>
        <td align="center">
            <asp:ListView ID="listViewProducts" GroupItemCount="3" runat="server" GroupPlaceholderID="GroupsGoHere"
                ItemPlaceholderID="ItemsGoHere" Orientation="Horizontal" DataSourceID="dsProducts">
                <LayoutTemplate>
                    <div runat="server" id="Main" class="ImagePageMainLayout">
                        <div runat="server" id="GroupsGoHere">
                        </div>
                    </div>
                </LayoutTemplate>
                <GroupTemplate>
                    <div runat="server" id="Images" class="ImagePageGroup">
                        <div runat="server" id="ItemsGoHere">
                        </div>
                    </div>
                </GroupTemplate>
                <ItemTemplate>
                    <div id="Item" align="center" runat="server" class="ImagePageItem" style="width: 100%">
                        <table cellpadding="0" cellspacing="0" width="100%">
                            <tr>
                                <td>
                                    <asp:Image ID="imgProduct" runat="server" AlternateText='<%# Eval("Title")%>' Height="100"
                                        Width="100" ImageUrl='<%# Eval("Product Image")%>' />
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    <asp:Label ID="lblProduct" runat="server" Text='<%# Eval("Title")%>'> 
                                    </asp:Label>
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    रु&nbsp;<asp:Label ID="lblProductCost" runat="server" Font-Size="15px" Text='<%# Eval("Cost")%>'> 
                                    </asp:Label>
                                </td>
                            </tr>
                        </table>
                    </div>
                </ItemTemplate>
                <ItemSeparatorTemplate>
                    <%-- <br />--%>
                </ItemSeparatorTemplate>
                <EmptyDataTemplate>
                    No Products found.
                </EmptyDataTemplate>
            </asp:ListView>
        </td>
    </tr>
    <tr>
        <td align="center">
            <asp:DataPager ID="ImagesDataPager" runat="server" PageSize="10" PagedControlID="listViewProducts">
                <Fields>
                    <asp:NextPreviousPagerField ButtonType="Link" ShowFirstPageButton="false" ShowPreviousPageButton="true"
                        ShowNextPageButton="False" PreviousPageText="<< Previous" RenderDisabledButtonsAsLabels="false" />
                    <asp:NumericPagerField />
                    <asp:NextPreviousPagerField ButtonType="Link" ShowLastPageButton="false" ShowNextPageButton="true"
                        ShowPreviousPageButton="False" FirstPageText="Previous" NextPageText="Next >>"
                        RenderDisabledButtonsAsLabels="false" />
                </Fields>
            </asp:DataPager>
        </td>
    </tr>
</table>


Output:
Figure 2 : ListView control flowing rows into multiple columns


C. ListView control with ObjectDataSource
Here I created custom ObjectDataSource as a data source for ListView control.

Steps:
1.       First I created two SharePoint lists Books and BookAuthors. Books list contains a look up column to BookAuthors list.

BookAuthors list:

Figure 3 : Book Authors list

Books list:

Figure 4 : Books list


2.       Then I created a custom class BookShop which provides combined data from above two lists. I used Linq to SharePoint provider to fetch the data from SharePoint.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Runtime.InteropServices;
using Microsoft.SharePoint.Security;
using System.Security.Permissions;
namespace ASP.NETControlsInSharePoint
{
   
    [Guid("fb6ec799-882c-4815-a6f0-3ecb95f711f9")]
    [SharePointPermissionAttribute(SecurityAction.InheritanceDemand, ObjectModel = true)]
    [SharePointPermissionAttribute(SecurityAction.LinkDemand, ObjectModel = true)]
    public class BookShop
    {
        public List<BooksInformation> GetBooksByAuthorId(string authorId)
        {
            using (SharepointCustomDevelopmentDataContext dtContext = new SharepointCustomDevelopmentDataContext(SPContext.Current.Web.Url))
            {
                //To improve performance during readonly operations
                dtContext.ObjectTrackingEnabled = false;

                var results = from book in dtContext.Books
                              where book.AuthorName.Id.ToString() == authorId
                              select new BooksInformation
                              {
                                  BookId = (int)book.Id ,
                                  BookName = book.Title,
                                  AuthorName = book.AuthorName.Title,
                                  AuthorAlias = book.AuthorName.AuthorAlias != null ? book.AuthorName.AuthorAlias : "",
                                  BookCost = book.BookCost.ToString()

                              };
                return results.ToList();
            }
        }
    }

    public class BooksInformation
    {
        public int BookId { get; set; }
        public string BookName { get; set; }
        public string AuthorName { get; set; }
        public string AuthorAlias { get; set; }
        public string BookCost { get; set; }
    }
}


3.       After that I created a dropdown list and populated it with Book Authors using SPDataSource.


<SharePoint:SPDataSource runat="server" ID="dsBookAuthors" DataSourceMode="List"
                SelectCommand="<Query><OrderBy><FieldRef Name='Title'/></OrderBy></Query>">
                <SelectParameters>
                    <asp:Parameter Name="ListName" DefaultValue="BookAuthors" />
                </SelectParameters>
</SharePoint:SPDataSource>

<asp:DropDownList runat="server" ID="ddlAuthorTitles" DataSourceID="dsBookAuthors"
                DataTextField="Title" DataValueField="ID" AutoPostBack="true">
</asp:DropDownList>


4.       Then I created ObjectDataSource which points to GetBooksByAuthorId method of BookShop class. Note that it passes dropdown value as parameter to GetBooksByAuthorId method.


<asp:ObjectDataSource ID="dsBookShop" runat="server" SelectMethod="GetBooksByAuthorId"
                TypeName="ASP.NETControlsInSharePoint.BookShop,ASP.NETControlsInSharePoint, Version=1.0.0.0, Culture=neutral, PublicKeyToken=67027cb88f6143d4">
                <SelectParameters>
                    <asp:ControlParameter ControlID="ddlAuthorTitles" Name="authorId" PropertyName="SelectedValue"
                        Type="String" />
                </SelectParameters>
</asp:ObjectDataSource>



5.       Then I created ListView control pointing to the ObjectDataSource created above.


<asp:ListView ID="listView" runat="server" DataSourceID="dsBookShop">
    <LayoutTemplate>
        <table class="TableCSS" cellpadding="0" cellspacing="0" width="100%">
            <tr class="TableHeader">
                <td align="center">
                    Book Id
                </td>
                <td>
                    Book Name
                </td>
                <td>
                    Author Name
                </td>
                <td>
                    Book Cost
                </td>
            </tr>
            <tr id="ItemPlaceholder" runat="server">
            </tr>
            <tr>
                <td colspan="4" class="dataPager">
                    <asp:DataPager ID="DataPager1" runat="server" PageSize="5">
                        <Fields>
                            <asp:NextPreviousPagerField ButtonType="Link" ShowFirstPageButton="false" ShowPreviousPageButton="true"
                                ShowNextPageButton="False" PreviousPageText="<< Previous" RenderDisabledButtonsAsLabels="false" />
                            <asp:NumericPagerField />
                            <asp:NextPreviousPagerField ButtonType="Link" ShowLastPageButton="false" ShowNextPageButton="true"
                                ShowPreviousPageButton="False" FirstPageText="Previous" NextPageText="Next >>"
                                RenderDisabledButtonsAsLabels="false" />
                        </Fields>
                    </asp:DataPager>
                </td>
            </tr>
        </table>
    </LayoutTemplate>
    <ItemTemplate>
        <tr class="noramlRowData">
            <td align="center">
                <asp:Label ID="Label1" runat="server" Text='<%# Eval("BookId")%>'> 
                </asp:Label>
            </td>
            <td>
                <asp:Label ID="Label2" runat="server" Text='<%# Eval("BookName")%>'> 
                </asp:Label>
            </td>
            <td>
                <asp:Label ID="Label4" runat="server" Text='<%#FormatAuthorName(Eval("AuthorName"), Eval("AuthorAlias")) %>'> 
                </asp:Label>
            </td>
            <td>
                रु&nbsp;<asp:Label ID="Label3" runat="server" Text='<%# Eval("BookCost")%>'> 
                </asp:Label>
            </td>
        </tr>
    </ItemTemplate>
    <AlternatingItemTemplate>
        <tr class="alternatingRowData">
            <td align="center">
                <asp:Label ID="Label1" runat="server" Text='<%# Eval("BookId")%>'> 
                </asp:Label>
            </td>
            <td>
                <asp:Label ID="Label2" runat="server" Text='<%# Eval("BookName")%>'> 
                </asp:Label>
            </td>
            <td>
                <asp:Label ID="Label4" runat="server" Text='<%#FormatAuthorName(Eval("AuthorName"), Eval("AuthorAlias")) %>'> 
                </asp:Label>
            </td>
            <td>
                रु&nbsp;<asp:Label ID="Label3" runat="server" Text='<%# Eval("BookCost")%>'> 
                </asp:Label>
            </td>
        </tr>
    </AlternatingItemTemplate>
    <EmptyDataTemplate>
        <table cellpadding="0" cellspacing="0" width="100%" class="TableCSS">
            <tr class="TableHeader">
                <td align="center">
                    Book Id
                </td>
                <td>
                    Book Name
                </td>
                <td>
                    Author Name
                </td>
                <td>
                    Book Cost
                </td>
            </tr>
            <tr class="noramlRowData">
                <td colspan="4" align="center">
                    No Books found. He is not a writer.
                </td>
            </tr>
        </table>
    </EmptyDataTemplate>
</asp:ListView>



Output:

Figure 5 : ListView control with ObjectDataSource


In addition, below are the two steps we need to do to make ListView control and ObjectDataSource work in Visual web part.

1.       By default, Visual web part does not support asp.net 3.5 controls. To enable them, you will need to add following registration line in the user control (.ascx)


<%@ Register Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
    Namespace="System.Web.UI.WebControls" TagPrefix="asp" %>


2.       ObjectDataSource TypeName property
As by default, assemblies in SharePoint are stored in GAC, fully qualified name of the custom class needs to be mentioned while binding it to the ObjectDataSource in TypeName property. For BIN deployment this is not true. However, as a good practice, I suggest to mention fully qualified name even in case of BIN deployment. E.g.


<asp:ObjectDataSource ID="dsBookShop" runat="server" SelectMethod="GetBooksByAuthorId"                TypeName="ASP.NETControlsInSharePoint.BookShop,ASP.NETControlsInSharePoint, Version=1.0.0.0, Culture=neutral, PublicKeyToken=67027cb88f6143d4"/>


Note that similar to the every SharePoint control, SPDataSource control does not work in SandBox solutions.


Updated (7/7/2012):
Updating this post so that visitors would also get to know about SharePoint Designer capabilities of integrating asp.net controls in SharePoint. Matthew Bramer has written a series of posts on this.


Even though posts are for MOSS 2007, they still apply to SharePoint 2010.



For the reference, I have uploaded the source code here. It also contain List Templates of the lists I used. Hope this proves a good read!

2 comments:

  1. I've written something similar using SPD instead. I'd love for you to check it out and offer your comments:

    http://mattbramer.blogspot.com/2010/09/sharepoint-integrate-aspnet-controls.html

    ReplyDelete
    Replies
    1. Hi Matthew,

      I already went through your blog 2 days back. Good read! I've updated my post...have a look.

      Delete