Practical .NET

Implementing the Default SharePoint Interfaces with Lists

Peter returns to creating a provider WebPart, but this time passes SharePoint list data from one WebPart to another.

In my previous column, I showed how to implement the IWebPartParameters interface to provide data to other WebParts. That WebPart used a lot of non-native SharePoint technology: it accessed a relational database, stored the data in a DataSet, and displayed the data in an ASP.NET GridView (not even an SPGridView).

This case study shows how to create a provider WebPart but uses native SharePoint technology: it pulls data from a SharePoint List, stores the data in a collection of SPListItems, and displays the data in a SharePoint list.

For my case study, I created a Visual WebPart that displays, from SharePoint's Tasks List, only the Task's Title and only lists the Tasks that are more than 49 percent complete. While I used an ASP.NET GridView for displaying my data in my last column, this Visual WebPart will use the native SharePoint ListViewByQuery.

I could add the ListViewByQuery to my Visual WebPart's UserControl in the .ascx file's markup, but just to show another option, I'll add the control in code in the UserControl's CreateChildControls method. That code first creates a ListViewByQuery and attaches the Tasks list to it:

Protected Overrides Sub CreateChildControls()
  MyBase.CreateChildControls()
  Dim lvq As New Microsoft.SharePoint.WebControls.ListViewByQuery
  lvq.List = SPContext.Current.Web.Lists("Tasks")

The next step is to create a query that specifies that the Title field is to be displayed, sets the criteria for selecting the Tasks, and specifies the order in which the Tasks are to be displayed. To make the query run a little faster, I limit the query to retrieving only the fields normally displayed for this List:

Dim qry As New SPQuery(lvq.List.DefaultView)
Dim camlXML As XElement  
camlXML = <FieldRef Name='Title'/>      
qry.ViewFields = camlXML.ToString
qry.ViewFieldsOnly = True
camlXML = <Where>
             <Gt>
               <FieldRef Name='PercentComplete'/>
               <Value Type='Number'>.49</Value>
             </Gt>
           </Where>
qry.Query = camlXML.ToString
camlXML = <OrderBy>
             <FieldRef Name='Title' Ascending='True' />
           </OrderBy>
qry.Query += camlXML.ToString

I then attach the query to the ListViewByQuery and add the control to my UserControl's collection of controls to display my Tasks as a SharePoint list in my Visual WebPart:

lvq.Query = qry
Me.Controls.Add(lvq)

However, for this WebPart to act as a provider to other WebParts, I also need the List contained within the ListViewByQuery. I get that with the following code and pass it to a property on the code-only part of my Visual WebPart:

CType(Me.Parent, ListQParms).lst = lvq.List.GetItems(lvq.Query)
Implementing IWebParttable
As I've made pretty clear, I think that any WebPart that retrieves data should implement at least some of the default interfaces that SharePoint defines, so that other WebParts can access that data. So in the code-only portion of my WebPart, I'll have it implement the IWebPartTable interface (since it's difficult to determine which ListItem a user has selected in the ListViewByQuery, this is the easiest interface to implement):

Public Class ListQParms
    Inherits WebPart
    Implements IWebPartTable

I need to set up a connection methods for the interface. As you add more interfaces, the only thing that has to change from one interface's connection method to another is the name of the interface (I have this set up as code snippet):

<ConnectionProvider("Task data", "ConnectionPointIWebpartTable", _
        AllowsMultipleConnections:=True)> _
Public Function MyTableConnection() As IWebPartTable
  Return Me
End Function

Before looking at the members required by the interfaces, let's look at the property that the UserControl uses to pass the retrieved collection of ListItems to this part of the Visual WebPart. All the interesting code for this property is in the property's Setter. That code first converts the collection into a DataTable, making the rest of the code easier. The code then uses the TypeDescriptor's GetProperties method to generate a collection of PropertyDescriptors for each column in the DataTable, which is stored to be used later:

Private _lst As SPListItemCollection
Private _pds As PropertyDescriptorCollection
Dim _dt As DataTable
Public Property lst() As SPListItemCollection
  Get
    Return _lst
  End Get
  Set(value As SPListItemCollection)
    _lst = value
    _dt = _lst.GetDataTable
    _pds = TypeDescriptor.GetProperties(_dt.DefaultView)
  End Set
End Property

Now I can implement the Schema property that Visual Studio will have added to my WebPart as part of supporting the interface. All I have to do in the property interface is return the PropertyDescriptionCollection:

Public ReadOnly Property Schema As PropertyDescriptorCollection _
        Implements WebParts.IWebPartTable.Schema
  Get
    Return _pds
  End Get
End Property

I also need to implement the GetTableData method that accepts the function in the connecting WebPart that I'm to call when it's time to pass the data. Here's that method:

Private tcb As TableCallback
Public Sub GetTableData(callback As WebParts.TableCallback) _
    Implements WebParts.IWebPartRow.GetTableData
  rcb = callback
End Sub

Again, this code is identical from one WebPart to another.

Finally, in the PreRender event, I use the callback to pass data to the connecting WebPart (after determining that I have some data and that there is a part connected). I check whether the _lst is Nothing separately from checking the callback method so that I can add more callbacks to support other interfaces:

Private Sub ListQParms_PreRender( _
 sender As Object, e As System.EventArgs) Handles Me.PreRender
  If _lst IsNot Nothing Then
    If tcb IsNot Nothing Then
      tcb(_dt.Rows)
    End If
  End If
End Sub

More sophisticated UIs could be created with more sophisticated SharePoint controls (the SPGridView, for instance). But those wouldn't make much difference to the code required to implement the IWebPartTable interface: While your UI code might change, this code to deliver data to other WebParts would hold still -- always a nice feature.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube