How to: Create a Mobile Adapter

Applies to: SharePoint Foundation 2010

This topic explains how to create a mobile adapter class that makes a Web Part on a Microsoft SharePoint Foundation page accessible to mobile devices.

For a concrete example of adapting a Web Part, see Walkthrough: Creating a Mobile Adapter.

Setup Your Development Environment

Prerequisite Development Tasks

  1. If the Web Parts pages that you want to make accessible to mobile devices do not already exist, you must create them. For more information, see Web Part Infrastructure in SharePoint Foundation and WebPartPage.

    Note

    You do not create pages of the WebPartMobilePage class. These are created by the runtime when a mobile device requests a Web Parts page (WebPartPage).

  2. If the Web Parts that you want to adapt for mobile access do not already exist, you must create them. For more information, see Web Part Infrastructure in SharePoint Foundation, and Web Parts Overview.

Create and Deploy a Mobile Adapter Class

  1. In a Visual Studio class library project, add a reference to System.Web and Microsoft.SharePoint.dll, and then add a using (Imports in Visual Basic) statement for the namespace Microsoft.SharePoint.WebPartPages. You may need to add using statements for other namespaces depending on the details of you adapter implementation. Commonly, mobile adapters make calls to types in System.Web.UI.MobileControls, Microsoft.SharePoint, and Microsoft.SharePoint.MobileControls.

  2. Add a class named WebPartClassMobileAdapter, where WebPartClass is the name of the Web Part that you are adapting. For example, if the Web Part is StockListWebPart, name the adapter class StockListWebPartMobileAdapter. Set it to inherit from WebPartMobileAdapter.

  3. Set the namespace to conform to the guidelines in Namespace Naming Guidelines. For a mobile adapter, use something like MyCompany.SharePoint.WebPartPages.MobileAdapters.

  4. Although the WebPartMobileAdapter.Control property is not marked virtual or override (Overridable and Overrides in Visual Basic), you may need to effectively override it by hiding it and replacing it. You do this by declaring a new Control property in your derived class by using the new keyword (Shadows in Visual Basic). For more information about why you might want to do this and about how to do it, see the reference topic for WebPartMobileAdapter.Control.

  5. Override the CreateControlsForSummaryView() method if the default implementation is not appropriate for mobile access to the Web Part. Basically, an override should create any needed child controls and add them to the Controls collection in the order in which they should appear on a mobile device. Typically, you will want at least a small icon and a title for the Web Part to appear in the summary view on a mobile device. (If that is all that you want to appear in summary view, then you do not need to override CreateControlsForSummaryView().) Two helper methods are provided to make this easier: CreateWebPartIcon(WebPartMobileAdapter.WebPartIconLink) and CreateWebPartLabel().

    But sometimes more information is needed, even in the summary view. For example, suppose the Web Part that is being adapted has multiple child items that are all of the same type. You can add a count of the total number of children to the summary view with a Label control that follows the icon and title. The following code shows how to do this.

    Important

    This example makes two assumptions: first, that the Web Part that is being adapted has a Count property, of type String, which returns the total number of child items; and, second, that this property is accessible, because the Control property has been hidden and replaced as alluded to in the preceding step.

    protected override void CreateControlsForSummaryView()
    {
        Image iconImage = this.CreateWebPartIcon(WebPartIconLink.LinkToDetailView);
        iconImage.BreakAfter = false;
        this.Controls.Add(iconImage);
    
        Label titleLabel = this.CreateWebPartLabel();
        titleLabel.BreakAfter = false;
        this.Controls.Add(titleLabel);
    
        Label count = new Label() { Text=this.Control.Count.ToString() }
        this.Controls.Add(count);
    }
    
    Protected Overrides Sub CreateControlsForSummaryView()
            Dim iconImage As Image = Me.CreateWebPartIcon(WebPartIconLink.LinkToDetailView)
            iconImage.BreakAfter = False
            Me.Controls.Add(iconImage)
    
            Dim titleLabel As Label = Me.CreateWebPartLabel()
            titleLabel.BreakAfter = False
            Me.Controls.Add(titleLabel)
    
            Dim count As New Label() With {.Text=Me.Control.Count.ToString()} 
            Me.Controls.Add(count)
    End Sub
    

    Note that when the CreateWebPartIcon(WebPartMobileAdapter.WebPartIconLink) method is passed LinkToDetailView as it is in this example, then, provided that the browser on the device supports CSS and ECMAScript 1.0 or greater, it renders an arrowhead icon that functions as a clickable control that executes the expand script of the SharePoint Foundation Mobile Adapter Framework. When a user clicks it, the Web Part expands into the detailed view and the icon becomes a downward pointing arrowhead (which itself is a control that executes the collapse script). If the browser does not support both CSS and ECMAScript 1.0 or greater, then a different icon is rendered and clicking it navigates the user to a page (mblwpdetail.aspx) that consists of a detailed view of the Web Part. If you do not want the Web Part to have a detailed view on mobile pages, pass NoLink to CreateWebPartIcon(WebPartMobileAdapter.WebPartIconLink) instead.

    Some other things you might want to do in an override of CreateControlsForSummaryView():

    • If your rendering logic assumes that certain entities are not null or assumes that other conditions are true, consider designing your override to test the assumptions and either doing nothing, or providing alternate rendering, when the conditions are not true. For example, if the Web Part that you are adapting renders a list view, you can check to see if the view is designated as a mobile view by testing this.Control.View.MobileView. For more information, see MobileView.

    • If there are visible aspects of the Web Part that you are adapting that are not conveyed by just an icon and title, consider constructing small versions of them for the summary view. For example, if a pie chart is part of the Web Part, your override of CreateControlsForSummaryView() could include a small stock image of a pie chart; or if the Web Part included a photograph, your override could include a very small scaled version of the image.

  6. Override the CreateControlsForDetailView() method if the default implementation is not appropriate for mobile access to the Web Part. The default implementation renders an icon and title for a Web Part followed by a message saying that there is no detailed view for the Web Part. If you have overridden CreateControlsForSummaryView() and do not want to provide a detailed view, override CreateControlsForDetailView() and have it do nothing as shown in the following example.

    protected override void CreateControlsForDetailView(){}
    
    Protected Overrides Sub CreateControlsForDetailView()
    End Sub
    

    If you do want a detailed view, the process of overriding CreateControlsForDetailView() is the same as for overriding CreateControlsForSummaryView(), except that you show more details of the Web Part. For example, if the Web Part has multiple child items of the same type, consider showing the first few of them in detail view. In the following sample code, the CreateControlsForDetailView() method is overridden to display up to three items of the current list as children under the Web Part icon and title. A more detailed discussion of this code is in Walkthrough: Creating a Mobile Adapter.

    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  ' end "for each lightweight task of the current user"
    End Sub ' end CreateControlsForDetailView
    
  7. If you need custom logic during the Init, Load, PreRender, or Unload events, override the OnLoadForMobile(EventArgs), OnInitForMobile(EventArgs), OnPreRenderForMobile(EventArgs), or OnUnloadForMobile(EventArgs) methods, respectively.

  8. If you want mobile users to be redirected to a custom summary view page or detailed view page instead of the pages provided by the built-in default pages, override the SummaryViewPageUrl or DetailViewPageUrl properties.

  9. To change the icon that appears next to the Web Part title, override one or more of the following properties:

    • SummaryViewTitleIconUrl – The icon that appears next to the title when the Web Part is collapsed.

    • DetailViewTitleIconUrl – The icon that appears next to the title when the Web Part is expanded.

    • TitleIconUrl– The icon that appears next to the title when the mobile device does not support expand/collapse scripting.

    The following code shows an example of how to override one of these properties. In this override of TitleIconUrl, if the Web Part displays a list and the list has an icon of its own in the ImageUrl property, that icon will be displayed.

    protected override string TitleIconUrl
    {
        get
        { 
            SPContext context = SPContext.GetContext(HttpContext.Current);
    
            if (String.IsNullOrEmpty(context.List.ImageUrl))
            {
                return base.TitleIconUrl;
            }
            return context.List.ImageUrl;
        }
    }
    
    Protected Overrides ReadOnly Property TitleIconUrl() As String
        Get
            Dim context As SPContext = SPContext.GetContext(HttpContext.Current)
    
            If String.IsNullOrEmpty(context.List.ImageUrl) Then
                Return MyBase.TitleIconUrl
            End If
            Return context.List.ImageUrl
        End Get
    End Property
    
  10. Compile the assembly, give it a strong name, and deploy it to the Global Assembly Cache (GAC), or to the \BIN folder of the Web application, on every front-end Web server in the farm. We recommend that you accomplish this by deploying the adapter with a SharePoint Foundation solution. For simplicity, this topic assumes that you are deploying to the GAC. For more information about the choice, see Place your Assembly in the Bin or Global Assembly Cache. To copy the assembly to only the GAC of your development computer, use the gacutil.exe utility. The easiest way to ensure that each new version of your adapter is installed in the GAC is to run the utility from a batch file that is launched with a Post-build event in Visual Studio. The batch file should have the following line, where AssemblyName is replaced by the name of your assembly.

    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\AssemblyName.dll
    

Edit the compat.browser File

  1. Open the file \\Inetpub\wwwroot\wss\VirtualDirectories\port_number\App_Browsers\compat.browser, where port_number is the port of the Web application, in a text editor and scroll to the <browser> element that has the refID attribute value "default". It will have a child <controlAdapters> element that looks something like the following example:

    <controlAdapters>
      <adapter controlType="Microsoft.SharePoint.WebPartPages.XsltListViewWebPart, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
        adapterType="Microsoft.SharePoint.WebPartPages.XsltListViewWebPartMobileAdapter, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      <adapter controlType="Microsoft.SharePoint.WebPartPages.ListViewWebPart, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
        adapterType="Microsoft.SharePoint.WebPartPages.ListViewWebPartMobileAdapter, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      <adapter controlType="Microsoft.SharePoint.Applications.GroupBoard.WebPartPages.WhereaboutsWebPart, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
        adapterType="Microsoft.SharePoint.WebPartPages.WhereaboutsWebPartMobileAdapter, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
      <adapter controlType="Microsoft.SharePoint.WebPartPages.ImageWebPart, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
        adapterType="Microsoft.SharePoint.WebPartPages.ImageWebPartMobileAdapter, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
    </controlAdapters>
    
  2. Add an <adapter> element as a child of the <controlAdapters> element to map your adapter class to the Web Part that it adapts following the pattern of the example above. Note that the controlType and adapterType attributes are required. Both are the fully qualified name of the class and the four-part name of the assembly. To obtain your adapter assembly’s public key token, use the Get Assembly Public Key item on Visual Studio’s Tools menu. (If the item is not there, see How to: Create a Tool to Get the Public Key of an Assembly.) For more information about this XML markup, see Browser Definition File Schema (browsers Element).

    The following are examples of new <adapter> elements. The first is for a Contoso control that adapts the UserTasksWebPart that ships with SharePoint Foundation. The second is for a Northwinds control that adapts a Northwinds Web Part in the same Northwinds solution.

      <adapter controlType="Microsoft.SharePoint.WebPartPages.UserTasksWebPart, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c "
        adapterType="Contoso.SharePoint.WebPartPages.UserTasksWebPartMobileAdapter, Contoso, Version=1.0.0.0, Culture=neutral, PublicKeyToken=eedb3d3ba3b4c675" />
    
      <adapter controlType="NorthWinds.SharePoint.WebPartPages.VotingWebPart, Northwinds, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ffec2e2af2b4c675"
        adapterType="NorthWinds.SharePoint.WebPartPages.VotingWebPartMobileAdapter, NorthWinds, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ffec2e2af2b4c675" />
    

    Note

    To deploy your adapter class to a server farm, you must change the compat.browser file as described above on every front-end Web server. You should not overwrite the existing compat.browser with a compat.browser file of your own or you may cancel adapter mappings that are made by other SharePoint Foundation solution providers. Consider deploying the adapter as part of a SharePoint Foundation Feature. In the FeatureActivated(SPFeatureReceiverProperties) event handler, add code to create a timer job (SPJobDefinition) that adds the needed <adapter> element to the compat.browser file on every front-end Web server. For detailed information about programmatically editing the compat.brower file on all servers by using a timer job, see How to: Run Code on All Web Servers.

Register Your Adapter as a Safe Control

  1. You must register your adapter class as a safe control.

    Tip

    We recommend that register your adapter by deploying the adapter with a SharePoint Foundation solution. The steps in this section are required only if your development computer is a single front-end Web server. Using a SharePoint Foundation solution enables you to register controls as safe on all front-end Web servers automatically when your solution is deployed. For more information about using solution deployment to register controls as safe, see Solutions Overview, Manually Creating Solutions in SharePoint Foundation, and Solution Schema.

  2. In your Visual Studio project, add an XML file with a name of the form webconfig.*.xml, where you replace the * with the name of your company or some other name that is not likely to be used by any other SharePoint Foundation solution providers.

  3. Add an <action> element to the file like the one shown in the markup below. The TypeName attribute of the <SafeControl> element can be the name of your adapter class, such as UserTasksWebPartMobileAdapter. If you have multiple adapter classes in the same namespace, you can use "*" as the value of TypeName. The following is an example.

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

    Warning

    The use of "*" as the value of TypeName makes every class in the namespace a safe control. If you have some classes in the assembly that should not be designated as safe, move them to a different assembly or avoid using "*".

    For more information about the <SafeControl> element and webconfig.*.xml files, see How to: Create a Supplemental .config File and Working with Web.config Files.

  4. Save the file. It must now be copied to the folder %ProgramFiles%\Common Files\Microsoft Shared\web server extensions\14\CONFIG on your development computer. The simplest way to do this on your development computer is to add the following lines to a Post-build event command line or a batch file that is executed in a Post-build event command line.

    Note

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

    xcopy /y webconfig.MyCompany.xml "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\CONFIG"
    stsadm –o copyappbincontent
    
  5. The stsadmn.exe command copyappbincontent performs the "<action>" defined in your webconfig.*.xml. In this case, it inserts the new <SafeControl> element for your adapter into the web.config file at the root of the Web application. (It first removes any existing <SafeControl> elements for the adapter. This lets you rerun the stsadm operation with every build without duplicating the <SafeControl> element.) For more information about copyappbincontent and stsadm.exe, see Stsadm command-line tool and Operation name: Copyappbincontent