Walkthrough: Creating a Mobile Adapter

Applies to: SharePoint Foundation 2010

This walkthrough shows how to create a mobile adapter for the UserTasksWebPart that ships with Microsoft SharePoint Foundation. For a higher-level discussion of the process of creating a mobile Web Part adapter, see How to: Create a Mobile Adapter.

Prerequisites

Review the topic SharePoint Foundation Development Tools and its child topics (especially How to: Set the Correct Target Framework and CPU) and perform the recommended tasks for setting up your development environment. In particular, this walkthrough assumes that you have performed the recommended tasks in Setting Up Mobile Device Emulators, How to: Create a Tool to Get the Public Key of an Assembly, and How to: Add Tool Locations to the PATH Environment Variable.

Developing the Adapter

The following procedures describe the process of creating a mobile adapter.

Set up the Mobile Adapter Project

  1. In Visual Studio, select New and then Project on the File menu.

  2. In the New Project dialog box, select Visual C# in the Project types box, select Class Library in the Templates box, and enter MobileAdapters in the Name box. Click OK.

  3. Right-click the References node in Solution Explorer, click Add Reference, and, while holding down the CTRL key, select System.Web, System.Web.Mobile, and Microsoft.SharePoint on the .NET tab in the Add Reference dialog box. Click OK.

  4. Right-click the project name in Solution Explorer and select Properties.

  5. On the Application tab of the Properties dialog, enter MyCompany.SharePoint.WebPartPages.MobileAdapters as the Assembly name and MyCompany.SharePoint.WebPartPages as the Default namespace. Replace MyCompany with your company's name. Throughout this walkthrough, replace MyCompanywith your company's name.

  6. On the same tab, set the Target Framework to .NET Framework 3.5.

  7. Click Assembly Information and make changes as needed on the Assembly Information dialog. This walkthrough assumes that you leave the assembly and file versions at 1.0.0.0 and that you do not change the GUID.

  8. Open the Signing tab and then select Sign the assembly.

  9. Choose <New...> from the Choose a strong name key file drop-down list box.

  10. In the Create Strong Name Key dialog, type MobileAdapters.snk in the Key file name box, and then be sure that the Protect ... check box is not checked. Click OK.

  11. Open the Build Events tab and type the following code in the Post-build event command line box. This code runs a batch file that you create in a later step.

    cd "$(ProjectDir)"
    MobileAdapterPostBuild
    
  12. Click the Save all files button on the toolbar.

  13. In Solution Explorer, rename the file Class1.cs to UserTasksWebPartMobileAdapter.cs.

Create the Adapter Control

  1. Open the UserTasksWebPartMobileAdapter.cs file of the project if it is not already open and add the following using statements.

    using System.Web.UI.MobileControls;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.WebPartPages;
    using Microsoft.SharePoint.Utilities;
    
    Imports System.Web.UI.MobileControls
    Imports Microsoft.SharePoint
    Imports Microsoft.SharePoint.WebPartPages
    Imports Microsoft.SharePoint.Utilities
    
  2. Change the namespace to MyCompany.SharePoint.WebPartPages.MobileAdapters.

  3. Replace the entire Class1 declaration with the following code.

    public class UserTasksWebPartMobileAdapter : WebPartMobileAdapter
    {
    
    }// end UserTasksWebPartMobileAdapter class
    
    Public Class UserTasksWebPartMobileAdapter
    Inherits WebPartMobileAdapter
    
    End Class ' end UserTasksWebPartMobileAdapter class
    

    Notice that your new class inherits from WebPartMobileAdapter.

  4. Add the following declaration of an override of the CreateControlsForDetailView() method.

    protected override void CreateControlsForDetailView()
    {
    
    } // end CreateControlsForDetailView
    
    Protected Overrides Sub CreateControlsForDetailView()
    
    End Sub ' end CreateControlsForDetailView
    
  5. Begin the implementation of CreateControlsForDetailView() with the following lines. This code creates a downward pointing arrowhead to the left of the Web Part title. By passing LinkToSummaryView to the helper method CreateWebPartIcon(WebPartMobileAdapter.WebPartIconLink), you make this arrowhead a toggle switch on devices whose browsers support CSS and ECMAScript 1.0 or greater. When a user clicks it, the Web Part collapses into its summary view. (If the browser does not support both CSS and ECMAScript 1.0 or greater, then a different icon is rendered and clicking it opens a page with a summary view of the Web Part.

    Image iconImage = this.CreateWebPartIcon(WebPartIconLink.LinkToSummaryView);
    iconImage.BreakAfter = false;
    this.Controls.Add(iconImage);
    
    Dim iconImage As Image = Me.CreateWebPartIcon(WebPartIconLink.LinkToSummaryView)
    iconImage.BreakAfter = False
    Me.Controls.Add(iconImage)
    
  6. Add the following lines to render the default title of the User Tasks Web Part, which is "User Tasks" in bold face.

    Label titleLabel = this.CreateWebPartLabel();
    this.Controls.Add(titleLabel);
    
    Dim titleLabel As Label = Me.CreateWebPartLabel()
    Me.Controls.Add(titleLabel)
    
  7. Add the following lines to gather the references to the objects your code will refer to in its rendering logic. Replace CustomSite with the name of the Web site that you will be accessing from a mobile device. It can be any site that contains a standard SharePoint Foundation Tasks list.

    SPSite siteCollection = SPContext.Current.Site;
    SPUser currentUser = SPContext.Current.Web.CurrentUser;
    SPList taskList = siteCollection.AllWebs["CustomSite"].Lists["Tasks"];
    SPListItemCollection allTasks = taskList.GetItems(taskList.DefaultView);
    
    Dim siteCollection As SPSite = SPContext.Current.Site
    Dim currentUser As SPUser = SPContext.Current.Web.CurrentUser
    Dim taskList As SPList = siteCollection.AllWebs("CustomSite").Lists("Tasks")
    Dim allTasks As SPListItemCollection = taskList.GetItems(taskList.DefaultView)
    
  8. Add the following LINQ query. The where clause ensures that only the current user’s tasks will be included in the mobile view. The select clause projects a new anonymous type that contains only two properties. There is no need for fields that the adapter is not going to render.

    Note

    This code uses LINQ to Objects and is filtering on data (the allTasks object) that is already in memory. To actually prevent unneeded list items and fields from coming over the wire from the content database, you would need to use the LINQ to SharePoint provider. To show the use of the latter provider, however, would lengthen this walkthrough and distract from its central purpose.

    // Use LINQ to filter out other users ... 
    var lightweightTasksOfUser = from SPListItem task in allTasks
                                 where task["AssignedTo"].ToString().EndsWith(currentUser.Name)
                                 select new {task.Title, Priority=task["Priority"]}; // ... and unneeded columns.
    
    ' Use LINQ to filter out other users ... 
    Dim lightweightTasksOfUser = From task As SPListItem In allTasks
                                 Where task("AssignedTo").ToString().EndsWith(currentUser.Name)
                                 Select New With {Key task.Title, Key .Priority=task("Priority")} '... and unneeded columns.
    
  9. Add a foreach loop that, when completed, will render each item in the current user’s task list up to a limit of three.

    foreach (var lightweightTask in lightweightTasksOfUser)
    {
    
    } // end "for each lightweight task of the current user"
    
    For Each lightweightTask In lightweightTasksOfUser
    
    Next lightweightTask ' end "for each lightweight task of the current user"
    
  10. Within the loop, add the following code to render the standard SharePoint Foundation mobile list item bullet. The code uses object initializer syntax, which requires C# 3.0, to initialize the properties. But, of course, you could use separate property assignment statements as well.

    Image taskIcon = new Image() { ImageUrl = this.ItemBulletIconUrl,
                                   BreakAfter = false};
    this.Controls.Add(taskIcon);
    
    Dim taskIcon As New Image() With {.ImageUrl = Me.ItemBulletIconUrl, .BreakAfter = False}
    Me.Controls.Add(taskIcon)
    
  11. Add the following code to render the task name. Note that the Font() property is a read-only property that has writeable subproperties, including Bold and Size. These subproperties cannot be set with object initializer syntax.

    Label taskTitle = new Label { Text = lightweightTask.Title,
                                  BreakAfter = false};
    taskTitle.Font.Bold = BooleanOption.True;
    this.Controls.Add(taskTitle);
    
    Dim taskTitle As Label = New Label With {.Text = lightweightTask.Title, .BreakAfter = False}
    taskTitle.Font.Bold = BooleanOption.True
    Me.Controls.Add(taskTitle)
    
  12. Add the following code so that the task title is followed by the task priority in a small font. Note that the Priority property referred to here was created ad hoc in the anonymous type declared by the LINQ query. In C#, you need to refer to this value with "task["Priority"]", where task is a reference to an SPListItem object from the Tasks list.

    Label priority = new Label() { Text = " " + lightweightTask.Priority };
    priority.Font.Size = FontSize.Small; 
    this.Controls.Add(priority);
    
    Dim priority As New Label() With {.Text = " " & lightweightTask.Priority}
    priority.Font.Size = FontSize.Small
    Me.Controls.Add(priority)
    
  13. Add the following code to put a blank line after each task.

    this.Controls.Add(new LiteralText());
    
    Me.Controls.Add(New LiteralText())
    
  14. Finally, add the following if structure to limit the number of tasks rendered to three, and to render a link to the user’s full list of tasks in the event there are more than three tasks. Note, again, the use of object initializer syntax.

    // Render no more than 3 tasks, but provide link to others.
    if (itemCount++ >= 3)
    {
        Link moreItemLink = new Link
        {
            Text = "All my tasks",
            href = SPMobileUtility.GetViewUrl(taskList, taskList.Views["My Tasks"])
        };
        this.Controls.Add(moreItemLink);
        break;
    } // end "if limit has been reached"
    
    ' Render no more than 3 tasks, but provide link to others.
            If itemCount >= 3 Then
                itemCount += 1
                Dim moreItemLink As Link = New Link With {.Text = "All my tasks", .href = SPMobileUtility.GetViewUrl(taskList, taskList.Views("My Tasks"))}
                Me.Controls.Add(moreItemLink)
                Exit For
            End If ' end "if limit has been reached"
            itemCount += 1
    

    Add the following statement just above the beginning of the foreach loop to initialize the itemCount variable.

    Int16 itemCount = 1;
    
    Dim itemCount As Int16 = 1
    

    The whole declaration should now look like that shown in this code example.

    protected override void CreateControlsForDetailView()
    {
        Image iconImage = this.CreateWebPartIcon(WebPartIconLink.LinkToSummaryView);
        iconImage.BreakAfter = false;
        this.Controls.Add(iconImage);
    
        Label titleLabel = this.CreateWebPartLabel();
        this.Controls.Add(titleLabel);
    
        SPSite siteCollection = SPContext.Current.Site;
        SPUser currentUser = SPContext.Current.Web.CurrentUser;
        SPList taskList = siteCollection.AllWebs["MyGPSite"].Lists["Tasks"];
        SPListItemCollection allTasks = taskList.GetItems(taskList.DefaultView);
    
        // Use LINQ to filter out other users ... 
        var lightweightTasksOfUser = from SPListItem task in allTasks
                                     where task["AssignedTo"].ToString().EndsWith(currentUser.Name)
                                     select new {task.Title, Priority=task["Priority"]}; // ... and unneeded columns.
    
        Int16 itemCount = 1;
        foreach (var lightweightTask in lightweightTasksOfUser)
        {
            Image taskIcon = new Image() { ImageUrl = this.ItemBulletIconUrl,
                                           BreakAfter = false};
            this.Controls.Add(taskIcon);
    
            Label taskTitle = new Label { Text = lightweightTask.Title,
                                          BreakAfter = false};
            taskTitle.Font.Bold = BooleanOption.True;
            this.Controls.Add(taskTitle);
    
            Label priority = new Label() { Text = " " + lightweightTask.Priority };
            priority.Font.Size = FontSize.Small; 
            this.Controls.Add(priority);
    
            this.Controls.Add(new LiteralText());
    
            // Render no more than 3 tasks, but provide link to others.
            if (itemCount++ >= 3)
            {
                Link moreItemLink = new Link
                {
                    Text = "All my tasks",
                    href = SPMobileUtility.GetViewUrl(taskList, taskList.Views["My Tasks"])
                };
                this.Controls.Add(moreItemLink);
                break;
            } // end "if limit has been reached"
    
        } // end "for each lightweight task of the current user"
    } // end CreateControlsForDetailView
    
    Protected Overrides Sub CreateControlsForDetailView()
        Dim iconImage As Image = Me.CreateWebPartIcon(WebPartIconLink.LinkToSummaryView)
        iconImage.BreakAfter = False
        Me.Controls.Add(iconImage)
    
        Dim titleLabel As Label = Me.CreateWebPartLabel()
        Me.Controls.Add(titleLabel)
    
        Dim siteCollection As SPSite = SPContext.Current.Site
        Dim currentUser As SPUser = SPContext.Current.Web.CurrentUser
        Dim taskList As SPList = siteCollection.AllWebs("MyGPSite").Lists("Tasks")
        Dim allTasks As SPListItemCollection = taskList.GetItems(taskList.DefaultView)
    
        ' Use LINQ to filter out other users ... 
        Dim lightweightTasksOfUser = From task As SPListItem In allTasks
                                     Where task("AssignedTo").ToString().EndsWith(currentUser.Name)
                                     Select New With {Key task.Title, Key .Priority = task("Priority")} '... and unneeded columns.
    
        Dim itemCount As Int16 = 1
        For Each lightweightTask In lightweightTasksOfUser
            Dim taskIcon As New Image() With {.ImageUrl = Me.ItemBulletIconUrl, .BreakAfter = False}
            Me.Controls.Add(taskIcon)
    
            Dim taskTitle As Label = New Label With {.Text = lightweightTask.Title, .BreakAfter = False}
            taskTitle.Font.Bold = BooleanOption.True
            Me.Controls.Add(taskTitle)
    
            Dim priority As New Label() With {.Text = " " & lightweightTask.Priority}
            priority.Font.Size = FontSize.Small
            Me.Controls.Add(priority)
    
            Me.Controls.Add(New LiteralText())
    
            ' Render no more than 3 tasks, but provide link to others.
            If itemCount >= 3 Then
                itemCount += 1
                Dim moreItemLink As Link = New Link With {.Text = "All my tasks", .href = SPMobileUtility.GetViewUrl(taskList, taskList.Views("My Tasks"))}
                Me.Controls.Add(moreItemLink)
                Exit For
            End If ' end "if limit has been reached"
            itemCount += 1
        Next lightweightTask ' end "for each lightweight task of the current user"
    End Sub ' end CreateControlsForDetailView
    
  15. Select Build Solution on the Build menu. You are not finished yet, but you need to compile the assembly at this point so that you can generate a public key token.

Register the Mobile Adapter in the compat.browser File

  1. Open the file \\Inetpub\wwwroot\wss\VirtualDirectories\80\App_Browsers\compat.browser in a text editor or Visual Studio and scroll to the <browser> element that has the refID attribute value "default". (If necessary, replace "80" in the URL with the port of the Web application that you are targeting.)

  2. Inside the <controlAdapters> element, add the following as a child element.

    <adapter controlType="Microsoft.SharePoint.WebPartPages.UserTasksWebPart, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
        adapterType="MyCompany.SharePoint.WebPartPages.MobileAdapters.UserTasksWebPartMobileAdapter, MyCompany.SharePoint.WebPartPages.MobileAdapters, Version=1.0.0.0, Culture=neutral, PublicKeyToken=yourAssemblyPublicToken" />
    

    Replace MyCompany in both places with the name of your company. Replace yourAssemblyPublicKeyToken with your assembly’s public key token. Obtain this by clicking Get Assembly Public Key item on Visual Studio’s Tools menu. The key will appear in the Output window. (If the item is not there, see How to: Create a Tool to Get the Public Key of an Assembly.)

    Note

    If your development environment is a multiserver farm, you need to edit the compat.browser file on all servers. For information on how to do this programmatically, see How to: Run Code on All Web Servers.

Register the Adapter as a Safe Control on Development Computer

  1. Right-click the project name in Solution Explorer and select Add and then New Item.

    Note

    If you are developing on a server farm with multiple front-end Web servers, do not use this procedure. Instead, deploy the adapter with a SharePoint Foundation solution and add a <SafeControl> element to the manifest.xml file of the solution. For more information about SharePoint Foundation Solutions, see Using Solutions in SharePoint Foundation and its child topics.

  2. In the Add New Item dialog, add an XML file with the name webconfig.MyCompany.xml, where MyCompany is the name of your company, and click Add.

  3. Add the following markup to the file.

    <action>
      <remove path="configuration/SharePoint/SafeControls/SafeControl[@Assembly='MyCompany.SharePoint.WebPartPages.MobileAdapters3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=yourAssemblyPublicKeyToken']" />
    
      <add path="configuration/SharePoint/SafeControls">
        <SafeControl
          Assembly="MyCompany.SharePoint.WebPartPages.MobileAdapters3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=yourAssemblyPublicKeyToken"
          Namespace="MyCompany.SharePoint.WebPartPages.MobileAdapters3"
          TypeName="*"
          Safe="True"
          AllowRemoteDesigner="True"
        />
      </add>
    </action>
    

    Replace MyCompany in all three places with the name of your company. Replace yourAssemblyPublicKeyToken in both places with the same public key token you used in the compat.browser file markup.

  4. Save the file. A batch file that you will create in the next procedure will execute two actions each time you rebuild. The <add> action will register your adapter as a safe control by adding the specified <SafeControl> element to the all of the web.config files at the root of all the Web applications on your development computer. The <remove> action will do nothing the first time the batch file runs. On all subsequent runs, it will remove the <SafeControl> element that was added the previous time it ran. This is necessary because duplicate <SafeControl> elements can break Web Parts in SharePoint Foundation.

Create a Post-build Batch File

  1. Right-click the project name in Solution Explorer and select Add, and then New Item.

  2. In the Add New Item dialog, select Text File, give it the name MobileAdapterPostBuild.bat, and click Add.

  3. Open the file and add the following lines to it.

    Note

    This code assumes that you have followed the recommendations in How to: Add Tool Locations to the PATH Environment Variable.

    gacutil if bin\debug\MyCompany.SharePoint.WebPartPages.MobileAdapters.dll
    xcopy /y webconfig.MyCompany.xml "C:\Program Files\Common Files\Microsoft Shared\web server extensions\14\CONFIG"
    stsadm –o copyappbincontent
    iisreset
    
  4. On the File menu, click Save MobileAdapterPostBuild.bat As...

  5. On the Save File As dialog, click the down arrow beside the Save button and select Save with encoding.

  6. In the Advanced Save Options dialog, select Western European (Windows) – Codepage 1252 in the Encoding drop-down list, and click OK.

    This file ensures that each time you rebuild the project, the latest versions of your project files are copied to the correct location and that your adapter class is registered as a safe control. It also restarts SharePoint Foundation so it will load the latest version of the assembly.

Rebuild the Solution

  • Select Rebuild Solution on the Build menu of Visual Studio.

Testing the Adapter

The following procedures describe how to test the adapter.

Create a Custom Web Part Page with a User Tasks Web Part

  1. In a computer browser, navigate to the Web site that you want to access from mobile devices. This is the same site whose name you used to replace CustomSite in the code for the override of CreateControlsForDetailView().

  2. In the SharePoint Foundation UI, create a Web Parts page and add the User Tasks Web Part to it. The steps are as follows:

    1. On the Site Actions menu, select More Create Options.

    2. On the Create page, click Web Part Page.

    3. On the New Web Part Page page, name the new page "MyWPpage" and select any layout template that you want.

    4. Choose any document library in the Web site that you want, such as Shared Documents, and then click Create. The new page will open in edit mode.

    5. Select any Web Part zone on the page, for example, Right Column, and click Insert above the edit ribbon.

    6. On the page that opens, click People in the Categories area.

    7. Select User Tasks in the Web Parts area and click Add.

    8. Click Stop Editing to see how the page looks with the User Tasks Web Part. Then continue immediately with the next procedure.

Populate the Web Site’s Task List

  1. From any standard site page on the Web site, click Tasks on the left navigation bar.

  2. On the Tasks page, open the Items tab.

  3. Click New Item on the ribbon to open the New Item form. Fill it out to create a new task and click Save. Repeat this step to create multiple tasks for multiple users. Four or more tasks should be assigned to at least one user.

  4. Open the Browse tab to see how the populated list looks.

Install, Configure, and Use a Mobile Device Emulator

  1. If you haven’t done so already, install and configure a mobile device emulator, as explained in Setting Up Mobile Device Emulators, on any computer with a network connection to your development server or server farm. It can be your development computer.

  2. Log in to the computer as one of the users to which you assigned tasks and launch a browser on the emulator as explained in the Launch an Emulator procedure in Setting Up Mobile Device Emulators.

  3. Navigate to your custom Web Part page. The steps differ depending on the type of Web site. Typically, the home page has a link to View All Site Content. That page, in turn, has a link to the site’s document libraries, such as Shared Documents. The All Documents list for the document library includes a link to your custom Web Parts page.

    The mobile version of the page opens with all the mobile-adapted Web Parts in collapsed (not closed) state. Web Parts for which there is no mobile adapter, or which have been closed on the regular (nonmobile) version of the Web Parts page, are not shown at all. In SharePoint Foundation, closing a Web Part means hiding it. (If no Web Parts on the page meet the conditions required for mobile visibility, then the page does not open. Instead, the mobile version of the All Site Content page opens.) Figure 1 shows a Web Part page on which the User Task Web Part is the only Web Part that is not closed and that has a mobile adapter. This image was made with a Windows Mobile 5.0 Pocket PC emulator.

    Figure 1. The User Tasks Web Part in collapsed state on a mobile device.

    User Tasks Mobile Adapter in collapsed state

  4. Click the arrowhead icon next to User Tasks to expand the Web Part. Figure 2 shows the Web Part, after it has been adapted as explained in this topic, expanded for a user who has fewer than four assigned tasks. Note there is no All my tasks link at the end of the list. Also, note that only the current user’s tasks are listed.

    Figure 2. The User Tasks Web Part in expanded state on a mobile device.

    User Tasks Mobile Adapter in expanded state

    Figure 3 shows the Web Part expanded for a user who has more than three assigned tasks. Note that only three are listed and they are followed by an All my tasks link. This will open the My Tasks view of the Tasks list. Also, again, only the current user’s tasks are listed.

    Figure 3. The User Tasks Web Part in expanded state for a user with more than three assigned tasks.

    User Tasks Web Adapter with more than 3 tasks