This tutorial shows you how to build a set of master-detail portlets using:
Approximately 1 hour
This tutorial covers the following topics:
Place the cursor over this icon to load and view all the screenshots for this tutorial.
(Caution: This action loads all screenshots simultaneously, so response
time may be slow depending on your Internet connection.)
Note: Alternatively, you can place the cursor over each individual icon in the following steps to load and view only the screenshot associated with that step.
This tutorial covers using .NET controls in the WebCenter Interation portal. A common use of two portlets on a page is the use of the master-detail pattern to display data. Ideally, this is implemented using in place refresh so that only the portlet that needs to redisplay data refreshes. To use .NET controls with in place refresh for the master-detail portlets you need the following:
The portlet application used to illustrate the various techniques comprises three portlets. The Select Date portlet is a calendar control that allows the user to select a particular date. On selecting a date, the Orders portlet displays the invoices (for books purchased) for that day. On selecting an invoice in the Orders portlet, the Order Details portlet displays the details of that invoice (the books purchased). All three portlets use .NET controls; the Select Date portlet uses a Calendar control, and the Order and Order Details portlets use GridView controls. The application's data is stored in xml as by doing so the sample data can be stored in a flat file and easily modified or extended.
Here's what the completed portlets will look like. In operation the user will:
All three portlets use .NET controls and communication between portlets is via the Scripting Framework's event mechanism.
Before starting this tutorial, you should:
| 1. | Have access to or have installed the following applications:
|
| 2. | Have created a Visual Studio Web Site to create the sample code. To set up a Visual Studio Sample Web Site for this OBE:
|
| 3. | Have installed the .NET Web Control Consumer (part of the Oracle WebCenter Portlet Toolkit for .NET) To set up the Web Control Consumer (WCC)
|
| 4. | Have Web Center Interaction set up to support the OBE To set up the Web Center Interaction for the OBE
|
In this topic you will complete a first pass of coding the Orders portlet. To do so, you will add a GridView to display a list of invoices. You will add a Select column to the GridView that when clicked raises the appropriate event to trigger the Order Details portlet.
The Order Details portlet is already set up to register (i.e. listen) for this event.
| Add and configure an XmlDataSource | ||
| Add a GridView to display the invoices | ||
| Raise an event when a row is selected | ||
1. |
In the Visual Studio Solution Explorer pane, double-click Orders.aspx
to open it in the editor.
|
2. |
If you're not already in Design mode, select Design mode by clicking on Design at the bottom right of the editor.
|
| 3. | Click Ctrl + Alt + X to open the Toolbox, then drag an XmlDataSource over to the Design editor.
|
| 4. | Click the small arrow at the top of the XmlDataSource, then select ConfigureDataSource.
|
| 5. | Enter "~/orders.xml" for the Data file and click OK (You don't need to enter a transform file as the xml file has been formatted with the data as attributes. Normally the xml file would not be formatted in this way; instead you'd need to use an xsl transform file).
|
| 1. | From the Toolbox, drag a GridView on to the Design editor.
|
| 2. | In the GridView tasks drop-down control, choose the Data Source XmlDataSource1
|
| 3. | Select the GridView in the design editor (you'll see a dotted line around the outside of the GridView in the editor), then in the properties panel, set the Width to 100%.
|
| 4. | In the portal open the My Page called WCC Samples. You should see that for the Orders Portlet, all the orders are listed.
|
| 1. | Switch the editor to Source mode and add this line of code just under the opening
<pt:namespace pt:token="$$PORTLET_ID$$" xmlns:pt='http://www.plumtree.com/xmlschemas/ptui/' />
|
| 2. | Add this Javascript block after the form form1 <script>
thisPortlet$$PORTLET_ID$$ =
PTPortlet.getPortletByID($$PORTLET_ID$$);
function performRaiseEvent$$PORTLET_ID$$(invoiceId) {
alert(invoiceId);
thisPortlet$$PORTLET_ID$$.
raiseEvent('showOrderDetail', invoiceId );
}
</script>
This will raise the event that includes the parameter invoiceId. The event will be detected by the Order Details portlet and it will display the relevant detail.
|
| 3. | Add a Select column to the GridView. As before select Add New Column from GridView tasks. In the dialog, choose the field type CommandField, click Select as the type of command button and take the Button type of Link which will be automatically selected for you. Click OK.
|
| 4. | In the GridView properties panel, double-click on SelectedIndexChanged. This will populate the property with the method name to be called, GridView1_SelectedIndexChanged, and take you to the C# code editor.
|
| 5. | In ClientScript.RegisterClientScriptBlock(typeof(Page), "raiseEventShowOrderDetail", "<script>performRaiseEvent" + portlet_id + "( " + GridView1.SelectedRow.Cells[1].Text + " );</script>"); This will call the Note: The You may also notice that the
|
| 6. | Declare a class variable of type int (remember as a class variable the declaration must not be within a method. So put it just above the Page_Load method. protected int portlet_id;
|
| 7. | In portlet_id =
Plumtree.Remote.Portlet.
PortletContextFactory.
CreatePortletContext(Request, Response).
GetRequest().GetPortletID();
This code will retreive the portlet id from the HTML request header.
|
| 8. | Now go to the WCC Samples My Page and try clicking on the Select links for a column. Each link should produce a detail for that invoice in the Order Details portlet.
You have now acheived the objective of getting a .NET control to call a Javascript function. But as yet it's barely functional as the Orders portlet doesn't in any way indicate which invoice has been selected.
|
| 9. | Make a small change to the properties of the GridView. Set the background color to yellow for the selected row.
|
| 10. | Now reload the WCC Sample page in the browser, then make some selections in the Orders portlet. The most recently selected invoice will be highlighted.
|
In this topic, you will modify the Orders portlet so that instead of clicking on the Select link, you will simply click on the row of the invoice you want more detail for. In order to achieve this you need to modify the way the GridView works by re-writing the Render() method. (It is possible to use other methods to modify the GridView, such as the method attached to the OnRowDataBound event, but you would then have to disable EventValidation. By using the Render method instead you can call the RegisterForEventValidation() method of the ClientScriptManager to register the events you're adding to the control. The reason you have to do this in Render is that Render() is the only method where you can make the call (RegisterForEventValidation()).
| Allow user to click anywhere on GridView | ||
| 1. |
Go to the code behind page (Orders.aspx.cs) and add the following method and its code: protected override void Render
(System.Web.UI.HtmlTextWriter writer) {
foreach (GridViewRow row in GridView1.Rows) {
if (row.RowType == DataControlRowType.DataRow) {
row.Attributes["onclick"] =
Page.ClientScript.
GetPostBackEventReference(GridView,
"Select$" + row.RowIndex,
true);
row.Attributes.Add("style", "cursor:pointer;");
}
}
base.Render(writer);
}
Note that this will add a Javascript call to __doPostBack()
and also add a style so that the cursor will change to a pointer (a hand)
when the mouse is moved over the GridView Also, you may notice that there
is no call to RegisterForEventValidation(). That's because
by adding "true" as the last parameter to GetPostBackEventReference,
the system will automatically register the postback for you.
|
| 2. |
Go to the source page and make the Select row invisible by adding the
attribute The line of code will now look like this. <asp:CommandField ShowSelectButton="True" Visible="False"/> (You could also just remove this line - if you do, make sure to adjust
the code in the
|
| 3. |
Now go to the WCC Samples My Page and try clicking anywhere on a row. You should notice that the cursor changes into a hand anywhere that you can click and that the Order Details portlet responds correctly.
|
In this topic you modify the GridView so that it only works on the client-side and does not post back to the server.
In this topic, you will see how it is possible to modify the GridView
so that you can raise the Scripting Framework event without calling __doPostBack().
In the previous topic you succeeded in having the GridView call a Javascript
function and thus communicate with the Scripting framework. You did this by
overloading the Render method to add an onclick method to each row of the
GridView. The onclick method called __doPostBack() which posted
back to the server side and called GridView1_SelectedIndexChanged which in
turn wrote back some Javascript and caused the call to performRaiseEvent<portlet_id>
to be called.
This is a good solution, but there is a another approach that may
be appropriate in some cases. Since it's possible to add an onclick method
to a row of the GridView, you can have this onclick method call the performRaiseEvent
method directly. That way there will be no post back to the server and no
need for one. (This is generally a good thing, but you will see that there
are drawbacks to this approach). You could use the Render() method
again (to write the onclick method), but since there's no postback, and therefore
no need for validation (i.e. no need to call RegisterForEventValidation()),
you can use the method of the OnRowDataBound event. This simplifies the code
slightly.
You'll see the advantages and disadvantages of this approach as you work through this topic.
| Code the OnRowDataBound event to create client-side function call | ||
| Write code to implement highlighting for selected row | ||
| Set the GridView header to a portal style | ||
To code OnRowDataBound, perform the following steps:
| 1. |
Select the GridView in the Design editor |
| 2. |
Click the Event icon in the properties panel, then double-click on RowDataBound. This will create a method with the default name for this event and display the code editor.
|
| 3. |
Add the following code to the GridView1_RowDataBound method (Note that the index is for the id column so it will be "1" if the Select column exists but is invisible, or "0" if the Select column has been removed) if (e.Row.RowType == DataControlRowType.DataRow) {
e.Row.Attributes.Add("onclick",
"javascript:performRaiseEvent" +
portlet_id +
"('" +
e.Row.Cells[1].Text +
"');");
e.Row.Style["cursor"] = "pointer";
}
|
| 4. |
Comment out the entire |
| 5. |
Now go to the WCC Samples My Page and try clicking on the Select links for a column. The functionality will be the same as before with two exceptions:
This shows both an advantage (no post back) and a disadvantage (some features of the GridView require post back). In this case, if the only functionality required is highlighting of a row, you could add this.
|
| 1. |
Add this Javascript function to Orders.aspx function hilite(control, rowId){
if(document.getElementsByTagName){
var table = document.getElementById(control);
var rows = table.getElementsByTagName("tr");
for(i = 1; i < rows.length; i++){
rows[i].style.backgroundColor = "white";
}
rows[rowId + 1].style.backgroundColor = "yellow";
}
}
|
| 2. |
Add this line to the GridView1_RowDataBound method and since this line
now sets the color and calls e.Row.Attributes.Add("onclick",
"javascript:hilite('GridView1'," +
e.Row.RowIndex + "); performRaiseEvent" +
portlet_id + "('" +
e.Row.Cells[1].Text + "');");
|
| 3. |
Check the functionality of the Orders portlet now. It should behave exactly as it did when it posted back and used the GridView1_SelectedIndexChanged method to call the Javascript function.
If all you want to do is add a selected highlight, this may be feasible. Calling the Javascript directly is very efficient, but .NET Controls are not designed to work this way and you may find that you have limited features and functionality. |
| 1. |
Open Orders.aspx in the editor in source mode and add the following
attribute to the AutoGenerateColumns="False" The code for the GridView will now look like this. You may notice that there are now some extraneous attributes that are no longer needed. For now, leave them as they are; you can remove them later.
(You could do this in design mode, but often it's just as easy to make modifications directly to the source. You will continue to work directly with the source in the next few steps).
|
| 2. |
Add the following within the <asp:BoundField DataField="id"
HeaderText="Invoice Id"
SortExpression="id" />
<asp:BoundField DataField="customer"
HeaderText="Customer Name"
SortExpression="customer" />
<asp:BoundField DataField="date"
HeaderText="Date"
SortExpression="date" />
|
| 3. |
After the </Columns> close tag and before the </asp:GridView> close tag add the following: <HeaderStyle Wrap="False" BackColor="Silver"
CssClass="platportletHeaderBg customappText" />
Notice that this is simply setting the background color and adding a portal CSS class to the header row of the GridView.
|
| 4. |
Now go to the WCC Samples My Page. The Orders portlet table headings should now match those in the other portlet.
|
Up until now you've looked at how to work with a .NET control to get it to use the Scripting Framework, and thus raise an event. There are another set of issues to think about if instead of raising an event, your .NET control portlet registers for an event and therefore needs to refresh in place when that event is detected.
First, you need to decide which mechanism to use. The WCC, when enabled, automatically gives you in place refresh when a .NET control in a portlet refreshes, but the Scripting Framework gives you a Refresh() method, and even a RefreshOnEvent method.
In this topic you will use the WCC approach,
but later you will look at the Order Details portlet and its use of the ScriptingFramework
formRefresh() method.
In order to trigger the Orders portlet to refresh itself, you will first create a Select Date portlet that uses a calendar to raise an event. You will then code the Orders portlet to detect this event and refresh itself with the orders that were made on the day in question.
| Create a portlet with a calendar | ||
| Code the calendar portlet to raise an event | ||
| Register for the event in the Orders portlet | ||
| Code the Orders portlet to display invoices for date selected | ||
| 1. |
Right-click on http://localhost/WCC_Sample and select Add New Item.
|
| 2. |
Select WebForm as the template, and type the name SelectDate.aspx for the name of the web form. Then click Add. (Be sure to use the name SelectDate.aspx as you have already set up a Web Service and portlet to use this name.
|
| 3. |
Using the Toolbox, add a Calendar to the SelectDate Design editor.
|
| 4. |
Add the SelectDate portlet to the WCC Samples page. When you reload the WCC Sample My Page you will see the calendar and can click on it, but it won't have any effect as yet. You will need to add the event framework to have it interact with the Orders portlet.
|
| 1. |
Double-click on one of the days of the calendar in the design editor.
Visual Studio will configure the SelectionChanged event for the calendar, and add a method stub for the method that will handle this event. This method stub will be displayed in the code editor ready for you to add the necessary code.
|
| 2. |
Just as you did in Orders.aspx.cs, add the necessary code to get the portlet id and store it in a protected int variable, portlet_id. Then add the following code to the Calendar1_SelectionChanged method: String javascriptCodeToRaiseEvent =
"<script> searchByDate" +
portlet_id +
"('" + Calendar1.SelectedDate.ToShortDateString() +
"'); </script>";
ClientScript.RegisterClientScriptBlock(typeof(Page),
"raiseSelectDateEvent",
javascriptCodeToRaiseEvent);
|
| 3. |
Again, as you did in the Orders portlet, add the namespace tag just below the opening <body> tag in SelectDate.aspx. Then add the following Javascript function that will be called by the code you added to the Calendar1_SelectionChanged method . <script>
thisPortlet$$PORTLET_ID$$ = PTPortlet.getPortletByID($$PORTLET_ID$$);
function searchByDate$$PORTLET_ID$$(orderDate) {
thisPortlet$$PORTLET_ID$$.raiseEvent('showOrders', orderDate );
}
</script>
Note that the event name is There is a DayRender event for the calendar but to rewrite this so
that it doesn't postback and just uses Javascript on the client is trickier
than with a GridView so it's best to use the post back approach and
the
|
| 4. |
Finally, since you may not be completing this tutorial in May 2009, add the following code to the Page_Load method of SelectDate.aspx.cs Calendar1.VisibleDate = DateTime.Parse("5, 6, 2009");
This will ensure that the Calendar opens on May 2009 - useful because the sample data is for the 6th, 7th, 8th, and 9th of that month. You can take a look at the WCC Samples My Page if you like. Since the Orders portlet has not been modified yet to listen for the calendar event, there will be no functionality on selecting a day in the calendar, but there should also be no errors, either compile errors or Javascript errors.
|
| 1. |
Add a JavaScript line to the code of the Orders portlet to register for the event raised by the calendar. thisPortlet$$PORTLET_ID$$.registerForEvent
('showOrders',
getByDate$$PORTLET_ID$$);
|
| 2. |
Add the Javascript function registered as the callback function in the code above function getByDate$$PORTLET_ID$$(theDate) {
__doPostBack('showOrders',theDate);
}
This code uses the __doPostBack() function which means that it will use the WCC to ensure that it refreshes in place. Later in this OBE, you'll see an example of using the Scripting Framework instead for this purpose. __doPostBack() passes the date parameter back to the server side where it can be used to filter the XmlDataSource to return only the invoices for the day in question. However, in order to be able to use the Add the following code inside the <asp:LinkButton ID="LinkButton1" runat="server"></asp:LinkButton>
|
| 3. |
Before coding the server side, first add an XPath query to the XmlDataSource so that it will return no invoices. Here is the XPath query to do this (the string to filter on goes in the single quotes - here it's an empty string to return no invoices): //Invoice[@date=''] So the asp:XmlDataSource declaration should now look like this: <asp:XmlDataSource ID="XmlDataSource1" runat="server"
DataFile="~/orders.xml"
XPath = "//Invoice[@date='']">
</asp:XmlDataSource>
|
| 4. |
Try reloading the page. You should now see that the Orders portlet now has no data.
|
| 1. |
Add the following code to the Page_Load method string selectDate = "";
if (Request["__EVENTTARGET"] == "showOrders") {
selectDate = Request["__EVENTARGUMENT"].ToString();
Response.Write("Got Request - selectDate is: " +
selectDate + "<br>");
}
Try reloading the page, then select a date in the Select Date portlet. You should see the date written to the top of the Orders portlet as shown below.
|
| 2. |
Now you will add the code that sets XPath so that the correct invoices are shown for the date selected. You will use the IsPostBack boolean to check if this is a post back or not. In this way you can give a different message on the initial page load. Then add the following code to the Page_Load method, just below the code you previously added to get the value of the date. if (IsPostBack) {
XmlDataSource1.XPath = "//Invoice[@date='" + selectDate + "']";
GridView1.DataBind();
if (GridView1.Rows.Count == 0) {
Response.Write("No invoices for that date.<br>");
}
}
|
| 3. |
Try reloading the page again and selecting a date. Remember that only the 6th, 7th, 8th, and 9th have any invoices. You should see the invoices for the date you select displayed in the Orders portlets.
|
| 4. |
Now add an Add this else {
Response.Write
("Select a date from the Date Select portlet.<br>");
}
Finally remove any Response.Write statements that were used for debugging.
(You could also move the code that gets the date inside the if(IsPostBack)
block.
|
| 5. |
You have now completed the coding for the SelectDate portlet and the Order portlet. Go to the WCC Samples My Page and try out the portlets. You should get invoices for the 6th, 7th, 8th, and 9th of May 2009.
|
As the final part of this OBE, examine the OrderDetails portlet. Unlike the technique you used in this portlet, that portlet uses the Scripting Framework for refresh. The code is documented but some further explanations are given in the following discussion.
OrderDetails is designed to illustrate how .NET controls can be used in the portal by using the Scripting Framework. As such, it doesn't use the WCC. As you'll see in the code samples below, the WCC is disabled. (Normally the WCC is defined in Web.Config for all the Web Forms in the Web Site, so if enabled for a Web Site, it must be explicitly disabled if you do not want it to operate on a particular Web Form in that Web Site).
| 1. |
In order to pass data back to the server-side when using the Scripting Framework, the OrderDetails portlet adds a hidden input to the form. (Another good approach would be to use Session preferences to store the values).
|
| 2. |
OrderDetails uses an XmlDataSource, just like the Orders portlet. But in the case of the OrderDetails portlet, XPath is filtering on the id attribute.
|
| 3. |
The GridView uses the XmlDataSource defined above. One thing to remember is that since OrderDetails does not use the WCC, the GridView ID (GridView1) will not be rewritten (therefore it could clash with another GridView in another portlet on the page). However, as you'll see in a later piece of code, there is a way to do this explicitly in the Code Behind page.
|
| 4. |
The Javascript block contains the command to register for the event, and also the callback function. The function has two statements:
|
| 1. |
This piece of code (in the Page_Load method) disables the WCC in order to illustrate using .NET control portlets with the Scripting Framework, without the use of the WCC.
|
| 2. |
Just as in the Orders portlet, the code to use XPath with the XmlDataSource
is contained within an
Finally, although the OrderDetails portlet does not use the WCC, the names of .NET controls can be disambiguated using the following code in the OnPreRender method.
|
In this lesson, you learned how to:
| Code a portlet that uses a .NET Control to raise an Scripting Framework event | ||
| Refresh a .NET Control portlet on detecting an event by using the WCC (Web Control Consumer) | ||
| Refresh a .NET Control portlet on detecting an event by using the Scripting Framework | ||
To learn more about Developing for WCI (Web Center Interaction) you can refer to:
| Documentation on the OTN Web site. | ||
| Tutorials on the OTN Web Site | ||
Place the cursor over this icon
to hide all screenshots.