本教程介绍如何使用以下工具构建一组主从 portlet:
大约 1 个小时
本教程包括下列主题:
| 概述 | ||
| 情景 | ||
| 前提条件 | ||
| 编写触发事件的主 Portlet 的代码 | ||
| 修改 GridView 以触发事件而无回传 | ||
| 检测到事件时刷新 .NET 控件 Portlet | ||
| 使用 Scripting Framework 刷新 .NET 控件 Portlet | ||
| 总结 | ||
| 相关信息 | ||
将光标置于此图标上以加载和查看本教程的所有屏幕截图。(警告:因为此操作会同时加载所有屏幕截图,所以网速较慢时,响应时间可能会比较长。)
注:此外,您还可以在下列步骤中将光标放在每个单独的图标上,从而仅加载和查看与该步骤相关的屏幕截图。
本教程讲述如何在 WebCenter Interation 门户中使用 .NET 控件。在一个页面中使用两个 portlet 的常用方法是使用主从模式来显示数据。理想情况下,这可使用就地刷新功能来实现,从而只刷新需要重新显示数据的 portlet。为了对主从 portlet 使用 .NET 控件和就地刷新功能,您需要:
这里用于演示各种方法的 portlet 应用程序包含三个 portlet。Select Date portlet 是一个日历控件,用户可以通过它选择一个特定日期。选择一个日期后,Orders portlet 会显示该天的订单(购买的书籍)。在 Orders portlet 中选择一个订单后,Order Details portlet 会显示该订单的详细信息(购买的书籍)。这三个 portlet 均使用 .NET 控件,Select Date portlet 使用 Calendar 控件,Order 和 Order Details portlet 使用 GridView 控件。该应用程序的数据存储在 xml 文件中,因为这样做可以将示例数据存储在一个平面文件中,从而易于修改和扩展。
完成的三个 portlet 应如下所示。用户可进行以下操作:
这三个 portlet 均使用 .NET 控件,portlet 间的通信通过 Scripting Framework 的事件机制来完成。
开始本教程之前,您应该:
| 1. | 可以访问或已经安装了以下应用程序:
|
| 2. | 创建了 Visual Studio 网站以创建示例代码。 为本 OBE 建立一个 Visual Studio 示例网站:
|
| 3. | 安装了 .NET Web Control Consumer(.NET Oracle WebCenter Portlet Toolkit 的一部分) 安装 Web Control Consumer (WCC):
|
| 4. | 对 Web Center Interaction 进行了设置使其支持此 OBE 针对此 OBE 设置 Web Center Interaction:
|
在本主题中,您将首次尝试编写 Orders portlet 的代码。为此,您将添加一个 GridView 来显示订单列表。您将向这个 GridView 添加一个 Select 列,单击此列时会产生相应的事件,从而触发 Order Details portlet。
已经将该 Order Details portlet 注册到(即监听)这个事件。
| 添加并配置一个 XmlDataSource | ||
| 添加一个 GridView 以显示订单 | ||
| 当选中一行时触发一个事件 | ||
1. |
在 Visual Studio Solution Explorer 窗格中,双击 Orders.aspx 在编辑器中打开。
|
2. |
如果未处于 Design 模式中,则单击编辑器右下方的 Design 以选择 Design 模式。
|
| 3. | 单击 Ctrl + Alt + X 打开 Toolbox,然后将 XmlDataSource 拖到 Design 编辑器中。
|
| 4. | 单击 XmlDataSource 上方的小箭头,然后选择 ConfigureDataSource。
|
| 5. | 在 Data file 域中输入“~/orders.xml”,然后单击 OK(无需输入转换文件,因为已经通过数据作为属性设置了该 xml 文件的格式。一般情况下,xml 文件不会用这种方式进行格式化,而是需要您使用 xsl 转换文件。)
|
| 1. | 从 Toolbox 中,将 GridView 拖到 Design 编辑器中。
|
| 2. | 在 GridView Tasks 下拉控件中,选择数据源 XmlDataSource1。
|
| 3. | 在 Design 编辑器中选择该 GridView(您会看到编辑器中 GridView 的外围被虚线所环绕),然后在 Properties 面板中将 Width 属性设置为 100%。
|
| 4. | 在门户中,打开名为 WCC Samples 的 My Page。您会看到,Orders Portlet 中列出了所有的订单。
|
| 1. | 将编辑器切换到 Source 模式,在 <pt:namespace pt:token="$$PORTLET_ID$$" xmlns:pt='http://www.plumtree.com/xmlschemas/ptui/' /> |
| 2. | 在表单 form1 后添加以下 Javascript 代码块: <script>
thisPortlet$$PORTLET_ID$$ =
PTPortlet.getPortletByID($$PORTLET_ID$$);
function performRaiseEvent$$PORTLET_ID$$(invoiceId) {
alert(invoiceId);
thisPortlet$$PORTLET_ID$$.
raiseEvent('showOrderDetail', invoiceId );
}
</script>
该代码将触发件包含 invoiceId 参数的事件。Order Details portlet 将检测到该事件并显示相关的详细信息。 |
| 3. | 将一个 Select 列添加到 GridView 中。如前,从 GridView Tasks 中选择 Add New Column。在出现的对话框中,选择 CommandField 字段类型,单击 Select 作为命令按钮的类型,Button type 域中会自动为您选择 Link。单击 OK。
|
| 4. | 在 GridView 的 Properties 面板中,双击 SelectedIndexChanged。该操作将用要调用的方法名 GridView1_SelectedIndexChanged 填充该属性,并将您带入 C# 代码编辑器中。
|
| 5. | 在 ClientScript.RegisterClientScriptBlock(typeof(Page), "raiseEventShowOrderDetail", "<script>performRaiseEvent" + portlet_id + "( " + GridView1.SelectedRow.Cells[1].Text + " );</script>"); 当 GridView 的所选索引更改后,此代码将调用 注:此处使用 您可能还注意到, |
| 6. | 声明一个 int 类型的类变量(记住,作为类变量,其声明不得在方法内部进行。因此,将其放在 Page_Load 方法之上。) protected int portlet_id; |
| 7. | 在 portlet_id = Plumtree.Remote.Portlet. PortletContextFactory. CreatePortletContext(Request, Response). GetRequest().GetPortletID(); 此代码将从 HTML 请求头中检索 portlet id。 |
| 8. | 现在转到 WCC Samples My Page,试着单击 Select 列中的各个链接。每个链接都会在 Order Details portlet 中产生对应订单的详细信息。
您现在已实现了这个目标:用一个 .NET 控件来调用一个 Javascript 函数。但目前为止仅实现了功能,因为 Orders portlet 尚不能指出您选中了哪个订单。 |
| 9. | 我们对该 GridView 的属性稍加修改。将所选行的背景色设置为黄色。
|
| 10. | 现在,在浏览器中重新加载 WCC Sample 页面,然后在 Orders portlet 中进行一些选择。最近选中的订单会高亮显示。
|
在本主题中,您将对 Orders portlet 进行修改,使其只需单击某订单行即可得到该订单的详细信息,而无需单击 Select 链接。为此,您需要重写 Render() 方法来修改 GridView 的工作方式。(可以使用其他方法来修改 GridView,如与 OnRowDataBound 事件相关联的方法,但这样的话您必须禁用 EventValidation。而使用 Render 方法,您可以调用 ClientScriptManager 的 RegisterForEventValidation() 方法来监听您添加到控件中的事件。只能在 Render 中做此事的原因是,Render() 方法是唯一一个您可在其中进行这种调用 (RegisterForEventValidation()) 的方法。
| 允许用户单击 GridView 中的任意位置 | ||
| 1. | 转到页面 (Orders.aspx.cs) 的源代码,添加以下方法及其代码: 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);
}
注意,此代码将添加一个对 __doPostBack() 的 Javascript 调用,还将添加一个样式,从而当光标移到 GridView 上时会变为一个指针(手形)。另外,您可能注意到没有对 RegisterForEventValidation() 的调用。这是因为,通过将“true”添加到 GetPostBackEventReference 中作为最后一个参数,系统将自动为您监听该回传。
|
| 2. | 转到源代码页面,通过添加 该行代码现在如下所示。 <asp:CommandField ShowSelectButton="True" Visible="False"/> (您也可以仅删除此行 — 如果这么做,请确保相应调整
|
| 3. | 现在,转到 WCC Samples My Page,试着单击某行中的任意位置。您会看到,当光标移到可单击的任意位置时会变成一个小手,而 Order Details portlet 会正确地响应。
|
在本主题中,您将修改 GridView,使其只在客户端上工作而不回传到服务器。
在本主题中,您将了解如何修改 GridView 从而使您既能触发 Scripting Framework 事件而又不调用 __doPostBack()。在前一主题中,您成功地使 GridView 调用了一个 Javascript 函数,从而实现了与 Scripting Framework 的通信。为此,您通过使 Render 方法过载,从而为 GridView 的每行添加了一个 onclick 方法。onclick 方法调用 __doPostBack(),后者回传到服务器端并调用 GridView1_SelectedIndexChanged,此函数进而写回一些 Javascript 并引发对 performRaiseEvent<portlet_id> 的调用。
这是一个很好的解决方案,但在某些情况下使用另一种方法可能更合适。既然可以向 GridView 的一行添加一个 onclick 方法,您也可以让此 onclick 方法直接调用 performRaiseEvent 方法。这样,将不会回传到服务器,也不需要。(一般而言,这是件好事,但您将看到这个方法也有缺点)。您可再次使用 Render() 方法(用于编写 onclick 方法),但既然没有回传,也就无需验证(即,无需调用 RegisterForEventValidation()),您可以使用 OnRowDataBound 事件的方法。这可稍许简化代码。
经过本主题的学习后,您会了解此方法的优缺点。
| 编写 OnRowDataBound 事件的代码以创建客户端的函数调用 | ||
| 编写代码以实现所选行的高亮显示 | ||
| 将 GridView 标题设置为门户样式 | ||
| 1. | 在 Design 编辑器中选择 GridView。 |
| 2. | 单击 Properties 面板中的 Event 图标,然后双击 RowDataBound。这将为该事件创建一个带默认名称的方法,并显示代码编辑器。
|
| 3. | 在 GridView1_RowDataBound 方法中添加以下代码(注意,索引是 id 列,因此如果 Select 列存在但只是不可见,则索引为“1”;如果 Select 列已删除,则索引为“0”)。 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. | 将整个 |
| 5. | 现在转到 WCC Samples My Page,试着单击某列的 Select 链接。除了以下两点不同之处,其功能同前面一样:
这指出了一个优点(无回传)和一个缺点(GridView 的一些特性需要回传)。在此例中,如果只需要行高亮显示功能,可以添加以下代码。 |
| 1. | 在 Orders.aspx 中添加以下 Javascript 函数: 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. | 在 GridView1_RowDataBound 方法中添加下面这行代码,由于此代码行现在既设置颜色又调用 e.Row.Attributes.Add("onclick",
"javascript:hilite('GridView1'," +
e.Row.RowIndex + "); performRaiseEvent" +
portlet_id + "('" +
e.Row.Cells[1].Text + "');");
|
| 3. | 现在,我们来检查 Orders portlet 的功能。其效果应与其回传并使用 GridView1_SelectedIndexChanged 方法调用 Javascript 函数时完全一样。
如果您只想添加所选行高亮显示功能,这样是可行的。直接调用 Javascript 非常高效,但 .NET 控件的设计并非以这种方式工作,因此您可能会发现只能拥有有限的特性和功能。 |
| 1. | 在编辑器中以 Source 模式打开 Orders.aspx,将以下属性添加到 AutoGenerateColumns="False" 现在,GridView 的代码将如下所示。您可能注意到了,现在有一些不再需要的无关的属性。现在先保留这些属性,可以稍后再删除它们。
(您可以在 Design 模式下完成此工作,但通常与直接修改源代码一样容易。在下面几步中,您将继续直接处理源代码)。 |
| 2. | 在 <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. | 在 </Columns> 结束标记之后、</asp:GridView> 结束标记之前添加以下内容: <HeaderStyle Wrap="False" BackColor="Silver" CssClass="platportletHeaderBg customappText" /> 注意,这只是设置背景色并向 GridView 的标题行添加一个门户 CSS 类。 |
| 4. | 现在,转到 WCC Samples My Page。Orders portlet 中表的标题现在应与其他 portlet 中的相匹配。
|
至此,您已学习了如何利用 .NET 控件以使其使用 Scripting Framework,进而触发事件。如果您的 .NET 控件不触发事件,而是注册到某个事件并需要在检测到事件时就地刷新,则还有一些问题需要考虑。
首先,您需要决定使用哪种机制。WCC(如果已启用)会在某 portlet 中的一个 .NET 控件刷新时自动提供就地刷新,但 Scripting Framework 提供了一个 Refresh() 方法,甚至还提供了一个 RefreshOnEvent 方法。
在本主题中,您将使用 WCC 方式,但稍后您会看到在 Order Details portlet 中使用了 Scripting Framework 的 formRefresh() 方法。
为了触发 Orders portlet 进行自我刷新,您首先将创建一个 Select Date portlet,它使用日历触发事件。然后,您将编写 Orders portlet 的代码,使其检测该事件并用当日创建的订单进行自我刷新。
| 创建一个日历 portlet | ||
| 编写日历 portlet 代码以触发一个事件 | ||
| 在 Orders portlet 中监听该事件 | ||
| 编写 Orders portlet 代码以显示所选日期的订单 | ||
| 1. | 右键单击 http://localhost/WCC_Sample 并选择 Add New Item。
|
| 2. | 选择 WebForm 作为模板,键入 SelectDate.aspx 作为该 Web 表单的名称。然后单击 Add。(一定要使用名称 SelectDate.aspx,因为您已设置了一个使用该名称的 Web 服务和 portlet)。
|
| 3. | 使用 Toolbox 将一个 Calendar 添加到 SelectDate Design 编辑器。
|
| 4. | 将此 SelectDate portlet 添加到 WCC Samples 页面。 当您重新加载 WCC Sample My Page 后,会看到该日历,您可以单击该日历,但是目前不会产生任何效果。您将需要添加事件框架,使其与 Orders portlet 进行交互。
|
| 1. | 在 Design 编辑器中双击日历中某日。
Visual Studio 将为该日历配置 SelectionChanged 事件,并为处理此事件的方法添加一个方法存根。将在代码编辑器中显示此方法存根以备您添加需要的代码。
|
| 2. | 正如您在 Orders.aspx.cs 中所为,添加必要的代码以获取 portlet id,并将其保存在一个 protected int 类型的变量 portlet_id 中。 然后在 Calendar1_SelectionChanged 方法中添加以下代码: String javascriptCodeToRaiseEvent =
"<script> searchByDate" +
portlet_id +
"('" + Calendar1.SelectedDate.ToShortDateString() +
"'); </script>";
ClientScript.RegisterClientScriptBlock(typeof(Page),
"raiseSelectDateEvent",
javascriptCodeToRaiseEvent);
|
| 3. | 同样,如您在 Orders portlet 中所为,在 SelectDate.aspx 中的 <body> 开始标记下面添加命名空间标记。 然后添加以下 Javascript 函数,您添加到 Calendar1_SelectionChanged 方法中的代码将调用此函数。 <script>
thisPortlet$$PORTLET_ID$$ = PTPortlet.getPortletByID($$PORTLET_ID$$);
function searchByDate$$PORTLET_ID$$(orderDate) {
thisPortlet$$PORTLET_ID$$.raiseEvent('showOrders', orderDate );
}
</script>
注意此事件名称为 该日历有一个 DayRender 事件,但要对它进行重写使之不执行回传并且只在客户端上使用 Javascript,要比 GridView 的情形需要更高的技巧,因此,最好还是使用回传和 |
| 4. | 最后,由于您可能不会在 2009 年 5 月完成此教程,请将以下代码添加到 SelectDate.aspx.cs 的 Page_Load 方法中: Calendar1.VisibleDate = DateTime.Parse("5, 6, 2009");
这将确保该日历开始于 2009 年 5 月 — 这是有用的,因为示例数据是该月 6、7、8 和 9 日的数据。 如果您愿意,可以看看 WCC Samples My Page。由于尚未将 Orders portlet 修改为监听该日历事件,在该日历中选择一天不会有任何作用,但也不会有错误,无论是编译错误还是 Javascript 错误。 |
| 1. | 在 Orders portlet 的代码中添加一行 JavaScript 代码,用以监听日历触发的事件。 thisPortlet$$PORTLET_ID$$.registerForEvent
('showOrders',
getByDate$$PORTLET_ID$$);
|
| 2. | 添加上述代码中作为回调函数引用的 Javascript 函数: function getByDate$$PORTLET_ID$$(theDate) {
__doPostBack('showOrders',theDate);
}
此代码使用 __doPostBack() 函数,这意味着它将使用 WCC 来确保就地刷新。在本 OBE 的稍后部分,您将看到使用 Scripting Framework 代之可达到相同目的的示例。__doPostBack() 将日期参数传回服务器端,服务器端可使用该参数过滤 XmlDataSource,从而只返回当日的订单。 然而,为了能够使用 在 <asp:LinkButton ID="LinkButton1" runat="server"></asp:LinkButton>
|
| 3. | 在编写服务器端代码之前,首先向 XmlDataSource 添加一个 XPath 查询使其不返回订单。下面是这个 XPath 查询(过滤所依据的字符串由单引号引起 — 这里是一个空字符串,因此不返回任何订单): //Invoice[@date=''] 因此,asp:XmlDataSource 声明现在应如下所示: <asp:XmlDataSource ID="XmlDataSource1" runat="server" DataFile="~/orders.xml" XPath = "//Invoice[@date='']"> </asp:XmlDataSource> |
| 4. | 试着重新加载该页面。现在您会看到 Orders portlet 不包含任何数据。
|
| 1. | 在 Page_Load 方法中添加以下代码: string selectDate = "";
if (Request["__EVENTTARGET"] == "showOrders") {
selectDate = Request["__EVENTARGUMENT"].ToString();
Response.Write("Got Request - selectDate is: " +
print("<br>");
}
试着重新加载页面,然后在 Select Date portlet 中选择一个日期。您会看到该日期写到了 Orders portlet 的顶部,如下所示:
|
| 2. | 现在您将添加代码来设置 XPath,从而正确地显示所选日期的订单。您将使用 IsPostBack 布尔值检查这是否是一个回传。用这种方式,您可在初次页面加载时提供一个不同的消息。 然后在 Page_Load 方法中,在您前面添加的代码下面添加以下代码来获取该日期的相关值。 if (IsPostBack) {
XmlDataSource1.XPath = "//Invoice[@date='" + selectDate + "']";
GridView1.DataBind();
if (GridView1.Rows.Count == 0) {
Response.Write("No invoices for that date.
|
| 3. | 尝试再次重新加载该页面并选择一个日期。记住只有 6、7、8 和 9 日有订单。在 Orders portlet 中您会看到您所选日期的订单。
|
| 4. | 现在添加一条 添加以下 else{
Response.Write
("Select a date from the Date Select portlet.<br>");
}
最后,删除所有用于调试的 Response.Write 语句。(您还可将获取日期的代码移至 if(IsPostBack) 块中。
|
| 5. | 至此,您已完成了 SelectDate portlet 和 Order portlet 的代码编写工作。转到 WCC Samples My Page,试着使用这些 portlet。您可获得 2009 年 5 月 6、7、8 和 9 日的订单。
|
作为本 OBE 的最后一部分,我们来研究 OrderDetails portlet。这个 portlet 使用的技术与前面的 portlet不同,它使用 Scripting Framework 进行刷新。我们已写好代码,但在下面的讨论中将给出更进一步的解释。
OrderDetails 用于说明如何在门户中通过使用 Scripting Framework 来使用 .NET 控件。因此,该 portlet 不使用 WCC。您将在下面的代码示例中看到 WCC 被禁用了。(通常,WCC 在 Web.Config 中定义,用于网站中所有 Web 表单。因此,如果对某网站启用了 WCC,而您希望 WCC 对该网站中的一个特定 Web 表单不起作用时,就必须显式地禁用它)。
| 1. | 使用 Scripting Framework 时为了将数据传回服务器端,OrderDetails portlet 将一个隐藏的输入添加到表单。(另一个好办法是使用 Session 首选项保存这些值)。
|
| 2. | 和 Orders portlet 一样,OrderDetails 也使用一个 XmlDataSource。但对于 OrderDetails portlet,XPath 根据 id 属性进行过滤。
|
| 3. | 该 GridView 使用上面定义的 XmlDataSource。要记住的一点是,由于 OrderDetails 不使用 WCC,将不会重写 GridView ID (GridView1)(因此,它可能与该页面中另一 portlet 中的另一 GridView 冲突)。然而,您将在稍后的代码段中看到,有一种方法可以在页面的源代码中显式地解决此问题。
|
| 4. | Javascript 代码块包含监听事件的命令以及回调函数。该函数有两条语句:
|
| 1. | 此代码断(在 Page_Load 方法中)禁用了 WCC,其目的是说明通过使用 Scripting Framework 而非 WCC 来使用 .NET 控件 portlet。
|
| 2. | 与 Orders portlet 中一样,这里的
最后,尽管 OrderDetails portlet 不使用 WCC,但可以在 OnPreRender 方法中用以下代码消除 .NET 控件名称的歧义。
|
在本教程中,您学习了如何:
| 编写一个 portlet,使其使用一个 .NET 控件来触发一个 Scripting Framework 事件 | ||
| 检测到事件时使用 WCC (Web Control Consumer) 刷新 .NET 控件 portlet | ||
| 检测到事件时使用 Scripting Framework 刷新 .NET 控件 portlet | ||
要了解更多有关 WCI (Web Center Interaction) 开发的信息,可以参考:
| OTN 网站上的文档 | ||
| OTN 网站上的教程 | ||
将光标置于该图标上可以隐藏所有的屏幕截图。