Exercise 7-2: Connectable Web Parts


Exercise 7-2: Connectable Web Parts

Connectable web parts are good for allowing end users to create more personalized views of data without additional programming assistance. Of course, some-one still has to create the original web parts. In this exercise, you will create a web part that supports two different interfaces. These interfaces will allow you to connect the web part to custom lists you create in SPS.

Creating the Project

You will be building your web part in C#. Therefore, you should start a new web part library project in Visual Studio and name it SPSMultiFace . The web part itself will be named Detail . Once the project is created, take care to modify the web part description file and template code with the new names .

In order to code the project, you will also need to set several references. Your web part will need access to the appropriate namespaces for Single Sign-On (SSO), SQL Server database access, and web part connections. Listing 7-13 shows what your project should look like before you begin to add code.

Listing 7-13: Starting the Web Part
start example
 using System; using System.Web.UI; using System.Web.UI.WebControls; using System.ComponentModel; using System.Xml.Serialization; using System.Security; using System.Security.Permissions; using Microsoft.SharePoint; using Microsoft.SharePoint.Utilities; using Microsoft.SharePoint.WebPartPages; using Microsoft.SharePoint.WebPartPages.Communication; using Microsoft.SharePoint.Portal; using Microsoft.SharePoint.Portal.SingleSignon; using System.Data; using System.Data.SqlClient; namespace SPSMultiFace {     [DefaultProperty(""),     ToolboxData("<{0}:Detail runat=server></{0}:Detail>"),     XmlRoot(Namespace="SPSMultiFace")]     public class Detail : Microsoft.SharePoint.WebPartPages.WebPart     {     } } 
end example
 

Implementing the Interfaces

Your web part is designed to consume a cell as well as provide a row. Therefore your Detail class will have to implement ICellConsumer and IRowProvider . You can implement these interfaces by adding them to the class declaration. When you add the interfaces, Visual Studio should automatically offer to add the interface stubs. These stubs will define the events that the interface supports. Listing 7-14 shows how the code should appear with the stubs defined.

Listing 7-14: Adding the Event Stubs
start example
 //Inherits WebPart and implements ICellConsumer public class Detail : Microsoft.SharePoint.WebPartPages.WebPart, ICellConsumer, IRowProvider { public event CellConsumerInitEventHandler CellConsumerInit; public event RowReadyEventHandler RowReady; public event RowProviderInitEventHandler RowProviderInit; } 
end example
 

Defining the Properties

Your web part will only use four properties. These properties will not be visible in the property pane and are used only to hold the connection information for the database. Because you have implemented properties many times in many web parts, the detailed code will not be repeated here. Instead, create the properties using the information in Table 7-7.

Table 7-7: Property Information

PROPERTY

TYPE

BROWSABLE

DESCRIPTION

sqlServer

string

false

The server name where SQL Server is located

database

string

false

The name of the database (e.g., pubs )

userName

string

false

The user name to access the database

password

string

false

The password for the database

Creating the Child Controls

Your web part uses a DataGrid to display book records from the pubs database. The records you display will be based on a search parameter received through the ICellConsumer interface. Once the records are displayed, the grid may in turn provide a row to another web part. Therefore, you have to construct our DataGrid to support row-level selection. Listing 7-15 shows how to code the CreateChildControls method to create the DataGrid and a Label for messages.

Listing 7-15: Creating Child Controls
start example
 //Child Controls protected DataGrid grdBooks; protected Label lblMessage; protected override void CreateChildControls() {     //Purpose: draw the user interface     grdBooks = new DataGrid();     grdBooks.AutoGenerateColumns=false;     grdBooks.Width=Unit.Percentage(100);     grdBooks.HeaderStyle.Font.Name = "arial";     grdBooks.HeaderStyle.Font.Name = "arial";     grdBooks.HeaderStyle.Font.Bold = true;     grdBooks.HeaderStyle.ForeColor = System.Drawing.Color.Wheat;     grdBooks.HeaderStyle.BackColor = System.Drawing.Color.DarkBlue;     grdBooks.AlternatingItemStyle.BackColor = System.Drawing.Color.LightCyan;     grdBooks.SelectedItemStyle.BackColor=System.Drawing.Color.Blue;     //Add a button to the grid for selection     ButtonColumn objButtonColumn = new ButtonColumn();     objButtonColumn.Text="Select";     objButtonColumn.CommandName="Select";     grdBooks.Columns.Add(objButtonColumn);     //Add data columns     BoundColumn objColumn = new BoundColumn();     objColumn.DataField="title_id";     objColumn.HeaderText="Title ID";     grdBooks.Columns.Add(objColumn);     objColumn = new BoundColumn();     objColumn.DataField="title";     objColumn.HeaderText="Title";     grdBooks.Columns.Add(objColumn);     objColumn = new BoundColumn();     objColumn.DataField="price";     objColumn.HeaderText="Price";     grdBooks.Columns.Add(objColumn);     objColumn = new BoundColumn();     objColumn.DataField="ytd_sales";     objColumn.HeaderText="2003 Sales";     grdBooks.Columns.Add(objColumn);     objColumn = new BoundColumn();     objColumn.DataField="pubdate";     objColumn.HeaderText="Published";     grdBooks.Columns.Add(objColumn);     Controls.Add(grdBooks);     lblMessage = new Label();     lblMessage.Width = Unit.Percentage(100);     lblMessage.Font.Name = "arial";     lblMessage.Text = "";     Controls.Add(lblMessage); } 
end example
 

Coding the Web Part Life Cycle

Once the child controls are defined, you are ready to code each step in the life cycle of the web part. Remember that a connectable web part not only goes through the normal steps such as OnLoad and RenderWebPart , but it must also properly register its interfaces and respond to connection events. In this section, you will code the life cycle methods in roughly the order they are called by the web part infrastructure.

Note

You may want to refer back to Figure 7-2 during this exercise as an aid in understanding the life cycle.

Registering Interfaces

The first thing your web part will do is register its interfaces with the web part infrastructure when the EnsureInterfaces method is called. Because you implement two interfaces, you will have to make two separate calls to the RegisterInterface method. Listing 7-16 shows how to code the EnsureInterfaces method.

Listing 7-16: Registering Interfaces
start example
 //Private member variables private int m_rowConsumers = 0; private int m_cellProviders=0; public override void EnsureInterfaces() {     //Tell the connection infrastructure what interfaces the web part supports     try     {         RegisterInterface("PublisherConsumer_WPQ_",             "ICellConsumer",             WebPart.LimitOneConnection,             ConnectionRunAt.Server,             this,             "",             "Get a publisher name from...",             "Receives a publisher name.");         RegisterInterface("BookProvider_WPQ_",             "IRowProvider",             WebPart.UnlimitedConnections,             ConnectionRunAt.Server,             this,             "",             "Provide a row to...",             "Provides book information as a row of data.");     }     catch(SecurityException e)     {         //Use "WSS_Minimal" or "WSS_Medium" to utilize connections         lblMessage.Text+=e.Message+ "<br>";     } } 
end example
 

Run Location

The web part infrastructure will query your web part to find out where it runs. All of your code executes on the server. Therefore, add the following simple code to tell the web part infrastructure that your part only runs on the server.

 public override ConnectionRunAt CanRunAt() {     //This Web Part runs on the server     return ConnectionRunAt.Server; } 

Connection Notification

An end user will connect your web part with another using the drop-down menu in SPS. When your part is connected, it will receive notification when the PartCommunicationConnect method is called. In this method, you will track the number of web parts that are connected based on the interface that is connecting. Add the code from Listing 7-17 to the web part.

Listing 7-17: The PartCommunicationConnect Method
start example
 public override void PartCommunicationConnect(string interfaceName, WebPart connectedPart,string connectedInterfaceName,ConnectionRunAt runAt) {     //Make sure this is a server-side connection     if (runAt==ConnectionRunAt.Server)     {         //Draw the controls for the web part         EnsureChildControls();         //Check if this is my particular cell interface         if (interfaceName == "PublisherConsumer_WPQ_")         {             //Keep a count of the connections             m_cellProviders++;         }         if (interfaceName == "BookProvider_WPQ_")         {             //Keep a count of the connections             m_rowConsumers++;         }     } } 
end example
 

Implementing the Transformers

Both ICellConsumer and IRowProvider are compatible with several different interfaces. Because of this, the web part infrastructure needs to implement a transformer that allows the end user to map fields in the connected web parts. Therefore, your web part must tell the web part infrastructure what fields are available for mapping. Add the code from Listing 7-18 for the GetInitEventArgs method, which is called by the web part infrastructure.

Listing 7-18: Providing Transformer Metadata
start example
 public override InitEventArgs GetInitEventArgs(string interfaceName) {     //Purpose: provide data for a transformer     if (interfaceName == "PublisherConsumer_WPQ_")     {         EnsureChildControls();        CellConsumerInitEventArgs initCellArgs = new CellConsumerInitEventArgs();         //Field name metadata         initCellArgs.FieldName = "pub_name";         initCellArgs.FieldDisplayName = "Publisher name";         //return the metadata         return(initCellArgs);     }     else if (interfaceName == "BookProvider_WPQ_")     {         EnsureChildControls();         RowProviderInitEventArgs initRowArgs = new RowProviderInitEventArgs();          //Field names metadata          char [] splitter =";".ToCharArray();          string [] fieldNames = "title_id;title;price;ytd_sales;pubdate".Split(splitter);          string [] fieldTitles = "Title ID;Title;Price;Sales;Date".Split(splitter);          initRowArgs.FieldList = fieldNames;          initRowArgs.FieldDisplayList=fieldTitles;          //return the metadata          return(initRowArgs);     }     else     {         return null;     } } 
end example
 

Communicating Schema Information

Before any data can be transferred between web parts, the connected parts must exchange schema information so that they will know what to expect. This schema exchange is accomplished when the PartCommunicationInit method is called by the web part infrastructure. In this method, your web part fires its init methods to transfer the schema information to the connected parts. Add the code from Listing 7-19 to transfer the schema information.

Listing 7-19: Sending Schema Information
start example
 public override void PartCommunicationInit() {     if(m_cellProviders > 0)     {        CellConsumerInitEventArgs initCellArgs = new CellConsumerInitEventArgs();         //Field name metadata         initCellArgs.FieldName = "pub_name";         initCellArgs.FieldDisplayName = "Publisher name";         //Fire the event to broadcast what field the web part can consume         CellConsumerInit(this, initCellArgs);     }     if(m_rowConsumers>0)     {         RowProviderInitEventArgs initRowArgs = new RowProviderInitEventArgs();         //Field names metadata         char [] splitter =";".ToCharArray();         string [] fieldNames = "title_id;title;price;ytd_sales;pubdate".Split(splitter);         string [] fieldTitles = "Title ID;Title;Price;Sales;Date".Split(splitter);         initRowArgs.FieldList = fieldNames;         initRowArgs.FieldDisplayList=fieldTitles;         //Fire event to broadcast what fields the web part can provide         RowProviderInit(this,initRowArgs);     } } 
end example
 

Sending Data

The actual data is transferred between connected parts when the web part infrastructure calls the PartCommunicationMain method. We only have to code this method for the IRowProvider interface because it must initiate the transfer by firing the RowReady event. This event expects to send a row of information to a connected part based on a selection made in the DataGrid . If no selection is made, then a null row is transferred. Add the code from Listing 7-20 to fire the RowReady event.

Listing 7-20: Sending a Row of Data
start example
 public override void PartCommunicationMain() {     if (m_rowConsumers>0)     {         string status = string.Empty;         DataRow[] dataRows = new DataRow[1];         if(grdBooks.SelectedIndex > -1)         {             //Send selected row             DataSet dataSet = (DataSet)grdBooks.DataSource;             DataTable objTable = dataSet.Tables["books"];             dataRows[0] = objTable.Rows[grdBooks.SelectedIndex];             status = "Standard";         }         else         {             //Send a null row             dataRows[0] = null;             status = "None";         }         //Fire the event         RowReadyEventArgs rowReadyArgs = new RowReadyEventArgs();         rowReadyArgs.Rows=dataRows;         rowReadyArgs.SelectionStatus=status;         RowReady(this,rowReadyArgs);     } } 
end example
 

Receiving Data

When your web part receives data from a cell provider, the CellReady event will fire. Your web part is designed to only run a query when a valid cell is provided by a connected web part. Therefore, all of the database code will be run in this event. Also note that you must implement the CellProviderInit method because it is part of the ICellConsumer interface, but your web part has nothing to do for this event. Add the code from Listing 7-21 to receive the cell data, run the query, and populate the grid.

Listing 7-21: Returning Book Information
start example
 public void CellProviderInit(object sender, CellProviderInitEventArgs cellProviderInitArgs) { //Since we can only consume one kind of cell, nothing can be done here } public void CellReady(object sender, CellReadyEventArgs cellReadyArgs) {     //Purpose: Run the query whenever a new cell value is provided     EnsureChildControls();     //Get Credentials (from SSO in production environment)     userName="sa";     password="";     database="pubs";     sqlServer="(local)";     string strConn = "Password=" + password + ";Persist Security Info=True " +     ";User ID=" + userName + ";Initial Catalog=" + database + ";Data Source=" +     sqlServer;     //Build SQL statement     string strSQL;     DataSet dataSet = new DataSet("books");     try     {         strSQL = "SELECT title_id, title, price, ytd_sales, pubdate ";         strSQL += "FROM publishers INNER JOIN titles ";         strSQL += "ON publishers.pub_id = titles.pub_id ";         strSQL += "WHERE (pub_name = '" + cellReadyArgs.Cell.ToString() + " ')";     }     catch     {         lblMessage.Text="Select a value from a connected web part.";         return;     }     //Run the query     try     {         SqlConnection conn = new SqlConnection(strConn);         SqlDataAdapter adapter = new SqlDataAdapter(strSQL,conn);         adapter.Fill(dataSet,"books");     }     catch(Exception x)     {         lblMessage.Text += x.Message + "<br>";     }     //Bind to grid     try     {         grdBooks.DataSource=dataSet;         grdBooks.DataMember="books";         grdBooks.DataBind();     }     catch(Exception ex)     {         lblMessage.Text += ex.Message + "<br>";     } } 
end example
 
Note

For simplicity, I have hard-coded some database credentials into the code. Be sure to change these credentials to suit your environment. Also, you can get some "extra credit" by modifying the code to use the Microsoft SSO service.

Rendering the Web Part

Once all of the data is retrieved from the database, you can render the grid. Because you added a ButtonColumn to the DataGrid , the rows in the grid will be selectable through a hyperlink. If you connect another web part as a row consumer, selecting a row will result in a data transfer. Add the code from Listing 7-22 to render the web part.

Listing 7-22: Rendering the Web Part
start example
 protected override void RenderWebPart(HtmlTextWriter output) {     //Draw the control     grdBooks.RenderControl(output);     output.Write("<br>");     lblMessage.RenderControl(output); } 
end example
 

Using the Web Part

Once the web part is coded, you may compile it and use it in SPS. Before compiling, be sure that you have provided a strong name for the web part and marked it as safe in the web.config file. Also note that connecting web parts is affected by the current code access security policy. In order to connect web parts, you should be sure that you have implemented WSS_Minimal or WSS_Medium as the policy. Also, don't forget to restart IIS if you make any changes to web.config .

Creating the Publishers List

Before you import the web part into SPS, you will create a list of publishers that you can use to provide the search parameter to your web part. You will create this list from scratch inside the portal. Once the list is created, you will add some items for the search.

To create the publishers list, follow these steps:

  1. Log in to SPSPortal as a member of the Administrators site group .

  2. Navigate to any site you created earlier.

  3. In the site, click the Create link to open the Create page.

  4. On the Create page, select Custom Lists Custom List.

  5. Name the new list Publishers and click Create.

  6. Once the list is created, click "Modify settings and columns" in the Actions list.

  7. On the Customize Publishers page, select Columns Title to modify the one existing column.

  8. On the Change Column page, change the column name to Publisher .

  9. Click OK.

  10. Click the Home link to return to the site home page.

  11. On the home page, select Modify Shared Page Add Web Parts Browse.

  12. In the Add Web Parts pane, locate the Publishers list and drag it to a zone on the page.

  13. Close the Add Web Parts pane.

  14. Add the following Publisher items to the Publishers list:

    • New Moon Books

    • Binnet & Hardley

    • Algodata Infosystems

    • Five Lakes Publishing

    • Ramona Publishers

    • GGG&G

    • Scootney Books

    • Lucerne Publishing

Importing the Web Part

Once the Publishers list is set up, you can import the new web part and connect it. The web part will fill the grid based on selections made in the Publishers list.

To import the new web part, follow these steps:

  1. On the home page, select Modify Shared Page Add Web Parts Import.

  2. In the Add Web Parts pane, click Browse.

  3. Locate the web part description file for SPSMultiFace and click Open.

  4. Click Upload.

  5. Drag the new web part from the Add Web Parts pane onto any zone.

  6. Using the drop-down list for SPSMultiFace , select Connections Get a Publisher Name From Publishers.

  7. In the Edit Connection dialog, select Publisher as the column name to provide.

  8. Click Finish.

  9. Close the Add Web Parts pane.

  10. Select a publisher name from the list and verify that the grid fills with book information. Figure 7-3 shows the final site.

    click to expand
    Figure 7-3: Connecting the new web part

Creating the Authors List

The Publishers list uses the ICellConsumer interface. In order to test out the IRowProvider interface, you will need to create a second list. This list will contain authors associated with titles.

To create the Authors list, take these steps:

  1. In the site, click the Create link to open the Create page.

  2. On the Create page, select Custom Lists Custom List.

  3. Name the new list Authors and then click Create.

  4. Once the list is created, click "Modify settings and columns" in the Actions list.

  5. On the Customize Authors page, select Columns Add a New Column.

  6. Name the new column Author and then click OK.

  7. Click the Home link to return to the site home page.

  8. On the home page, select Modify Shared Page Add Web Parts Browse.

  9. In the Add Web Parts pane, locate the Authors list and drag it to a zone on the page.

  10. Close the Add Web Parts pane.

  11. Add the following items to the Authors list:

    • Title: The Busy Executive's Database Guide ; Author: Bennet

    • Title: Fifty Years in Buckingham Palace Kitchens ; Author: Blotchet-Halls

    • Title: But Is It User Friendly? ; Author: Carson

    • Title: The Gourmet Microwave ; Author: Deface

    • Title: Silicon Valley Gastronomic Treats ; Author: del Castillo

    • Title: Secrets of Silicon Valley , Author; Dull

    • Title: The Busy Executive's Database Guide ; Author: Green

    • Title: You Can Combat Computer Stress! ; Author: Green

  12. Using the drop-down menu associated with the Authors list, select Modify Shared Web Part.

  13. Using the drop-down menu associated with the Authors list, select Connections Get Sort/Filter From SPSMultiFace.

  14. In the Edit Connection dialog, select Title as the SPSMultiFace column to use when connecting.

  15. Click Next.

  16. In the Edit Connection dialog, select Title as the Authors list column to use when connecting.

  17. Click Finish.

  18. Close the Authors properties pane.

  19. In the Publishers list, click Algodata Infosystems and verify the grid data appears.

  20. In the grid of books, click the Select link associated with the title The Busy Executive's Database Guide . Verify that the Authors list is filtered. Figure 7-4 shows the final project.

    click to expand
    Figure 7-4: The completed project




Microsoft SharePoint[c] Building Office 2003 Solutions
Microsoft SharePoint[c] Building Office 2003 Solutions
ISBN: 1590593383
EAN: N/A
Year: 2006
Pages: 92

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net