SPGridView

今年一直在为MOSS 2007写自制ASP.NET程序, 为了让程序的样式更通用, 更配合MOSS自带的风格, 我花不了少时间调整样式, 建立公用的CSS和JS.

一个偶然的机会, 我从一大堆"无用的"SharePoint内置控件中找到一个可以用的控件, 就是SPGridView. SPGridView提供了MOSS的列表样式, 给我省了不少功夫. 虽然SPGridView跟MOSS列表页里用的ListViewWebPart长得一模一样, 但是ListViewWebPart根本没用到SPGridView, 所以MOSS列表页里那个漂亮的工具条你就别想了 呵呵.

 

SPGridView是MOSS内置控件中少数能脱离SharePoint List等内置数据源使用的控件, 它继承自System.Web.UI.WebControls.GridView, 但是使用SPGridView时必须手动把AutoGenerateColumns设成false.

 

1. 先创建一个ASP.NET Web Application(ASP.NET Web应用程序)项目.
SPGridView_第1张图片
该项目模板已经包含在VS2005 SP1中, 如果你不打算安装SP1, 就需要先更新KB915364, 再安装 Microsoft Visual Studio Web Application Projects.

 

2. 把引用中无用System.Web.Mobile等Assembly都去掉, 加入Microsoft.SharePoint (找不到的人去找块豆腐撞死好了). 再建个名为 ~masterurl 的目录, 放入一个default.master.
SPGridView_第2张图片

default.master的代码如下

   1: <%@ Master Language="C#" %>
   2: <html>
   3: <head runat="server">
   4:     <asp:contentplaceholder id="PlaceHolderAdditionalPageHead" runat="server"></asp:contentplaceholder>
   5: </head>
   6: <body>
   7:     <form id="form1" runat="server">
   8:         <asp:ContentPlaceHolder ID="PlaceHolderPageTitleInTitleArea" runat="server">
   9:         </asp:ContentPlaceHolder>
  10:         <asp:ContentPlaceHolder ID="PlaceHolderMain" runat="server">
  11:         </asp:ContentPlaceHolder>
  12:     </form>
  13: </body>
  14: </html>

 

项目中的那个instnwnd.sql是取自Microsoft提供的SQL Server 2000 Sample Databases里的Northwind数据库的脚本.使用脚本建立Northwind数据库. 并将连接字符串加入web.config.

 

3. 建立数据访问类NorthwindData.cs. 后面我们将使用ObjectDataSource组件来为SPGridView提供数据.

   1: using System;
   2: using System.Data;
   3: using System.Data.SqlClient;
   4: using System.Configuration;
   5:  
   6: namespace SPGridView_Demo
   7: {
   8:     public class NorthwindData
   9:     {
  10:         private static string connectionString = null;
  11:  
  12:         public NorthwindData()
  13:         {
  14:             if(string.IsNullOrEmpty(connectionString))
  15:             {
  16:                 connectionString = ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
  17:             }
  18:         }
  19:  
  20:         public int GetProductCount()
  21:         {
  22:             int count = 0;
  23:             string commandText = "SELECT COUNT(ProductId) FROM dbo.Products";
  24:             SqlConnection connection = new SqlConnection(connectionString);
  25:             SqlCommand command = new SqlCommand(commandText, connection);
  26:             connection.Open();
  27:             count = Convert.ToInt32(command.ExecuteScalar());
  28:             connection.Close();
  29:             return count;
  30:         }
  31:  
  32:         public DataTable GetProductList(string sortExpression)
  33:         {
  34:             DataTable dt = new DataTable();
  35:             string commandText = "SELECT ProductId, ProductName, p.CategoryId, p.UnitPrice, p.Discontinued, c.CategoryName FROM dbo.Products p INNER JOIN dbo.Categories c ON p.CategoryId = c.CategoryId ORDER BY {SORT}";
  36:  
  37:             if(sortExpression == null || sortExpression.Trim() == string.Empty)
  38:             {
  39:                 sortExpression = "ProductId";
  40:             }
  41:  
  42:             commandText = commandText.Replace("{SORT}", sortExpression);
  43:             SqlConnection connection = new SqlConnection(connectionString);
  44:             SqlDataAdapter adapter = new SqlDataAdapter(commandText, connection);
  45:             connection.Open();
  46:             adapter.Fill(dt);
  47:             connection.Close();
  48:             return dt;
  49:         }
  50:  
  51:         public DataTable GetProductList(int startRowIndex, int maximumRows, string sortExpression)
  52:         {
  53:             DataTable dt = new DataTable();
  54:             string commandText = "SELECT * FROM (SELECT ProductId, ProductName, p.CategoryId, UnitPrice, p.Discontinued, c.CategoryName, ROW_NUMBER() OVER (ORDER BY {SORT}) AS RowNumber FROM dbo.Products p INNER JOIN dbo.Categories c ON p.CategoryId = c.CategoryId) a WHERE RowNumber BETWEEN @StartRowIndex + 1 AND @StartRowIndex + @MaximumRows";
  55:  
  56:             if(sortExpression == null || sortExpression.Trim() == string.Empty)
  57:             {
  58:                 sortExpression = "ProductId";
  59:             }
  60:  
  61:             commandText = commandText.Replace("{SORT}", sortExpression);
  62:             SqlConnection connection = new SqlConnection(connectionString);
  63:             SqlDataAdapter adapter = new SqlDataAdapter(commandText, connection);
  64:             adapter.SelectCommand.Parameters.Add(new SqlParameter("@StartRowIndex", startRowIndex));
  65:             adapter.SelectCommand.Parameters.Add(new SqlParameter("@MaximumRows", maximumRows));
  66:             connection.Open();
  67:             adapter.Fill(dt);
  68:             connection.Close();
  69:             return dt;
  70:         }
  71:     }
  72: }

 

4. 创建 SPGVP1.aspx

SPGridView_第3张图片 SPGridView_第4张图片

我们先试着绑些数据给SPGridView, 再发布到MOSS里看看效果吧.
代码:

   1: <%@ Page Language="C#" MasterPageFile="~masterurl/default.master" %>
   2:  
   3: <%@ Register Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Namespace="Microsoft.SharePoint.WebControls" TagPrefix="cc1" %>
   4: <asp:Content ID="Content1" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
   5: </asp:Content>
   6: <asp:Content ID="Content2" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server">
   7: </asp:Content>
   8: <asp:Content ID="Content3" ContentPlaceHolderID="PlaceHolderMain" runat="server">
   9:     <cc1:SPGridView ID="SPGridView1" runat="server" AutoGenerateColumns="False" DataSourceID="ObjectDataSource1">
  10:         <Columns>
  11:             <cc1:SPBoundField DataField="ProductId" HeaderText="Product ID" SortExpression="ProductId" />
  12:             <asp:HyperLinkField DataTextField="ProductName" DataNavigateUrlFields="ProductId" DataNavigateUrlFormatString="#{0}" HeaderText="Product Name" SortExpression="ProductName" />
  13:             <cc1:SPBoundField DataField="ProductName" HeaderText="Product Name" SortExpression="ProductName" />
  14:             <cc1:SPBoundField DataField="CategoryName" HeaderText="Category" SortExpression="CategoryName" />
  15:             <asp:BoundField DataField="UnitPrice" DataFormatString="${0:F2}" HeaderText="Unit Price" SortExpression="UnitPrice" />
  16:             <asp:TemplateField HeaderText="Orderable" SortExpression="Discontinued">
  17:                 <itemtemplate>
  18:                 <asp:Label id="lblDiscontinued" runat="server" text='<%# Convert.ToBoolean(Eval("Discontinued")) ? "Yes" : "No" %>'></asp:Label></itemtemplate>
  19:             </asp:TemplateField>
  20:         </Columns>
  21:     </cc1:SPGridView>
  22:     <asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="GetProductList" TypeName="SPGridView_Demo.NorthwindData" SortParameterName="sortExpression"></asp:ObjectDataSource>
  23: </asp:Content>

效果:
SPGridView_第5张图片

然后我们给Product Name列加入项菜单吧, 我们计划给它加入2个菜单项, 1个是链接型View Deltail, 另1个是回发型Order Now. 觉得不过瘾再加个带!号的Order Now, 用于区别Unit Price高于$40的产品好了.

我们把Product Name列那行代码 

<asp:HyperLinkField DataTextField="ProductName" DataNavigateUrlFields="ProductId" DataNavigateUrlFormatString="#ProductDetail-{0}" HeaderText="Product Name" SortExpression="ProductName" />
改成
<cc1:SPMenuField HeaderText="Product Name" MenuTemplateId="mtProduct" SortExpression="ProductName" TextFields="ProductName" TokenNameAndValueFields="PID=ProductId,PNAME=ProductName" NavigateUrlFields="ProductId" NavigateUrlFormat="#ProductDetail-{0}" />
基中MenuTemplateId属性指定了一个菜单模板(MenuTemplate), 菜单模板也是一个SharePoint控件类.将下面的代码加到SPGridView代码之外.
<cc1:MenuTemplate ID="mtProduct" runat="server">
    <cc1:MenuItemTemplate ID="mitView" runat="server" Text="View Detail" ClientOnClickNavigateUrl="#ProductDetail-%PID%" />
    <cc1:MenuItemTemplate ID="mitOrder" runat="server" Text="Order Now" />
    <cc1:MenuItemTemplate ID="mitOrderWarn" runat="server" Text="Order Now" ImageUrl="/_layouts/images/exclaim.gif" />
</cc1:MenuTemplate>

注意: ID为mitView的菜单项模板使用了ClientOnClickNavigateUrl属性来指定此菜单项的连接, 它类似与HyperLinkField的DataNavigateUrlFormatString属性, 但是变量标识是%Alias%, 而不是{index}或列名. 这里的数据别名(Alias)是在调用此菜单模板的SPMenuField的TokenNameAndValueFields指定的, 格式为 "别名1=列名1,别名2=列名2,...".
特别注意: 我们使用了PNAME 代表ProductName的数据, 如果数据里包含单/双引号等字符, 会导致菜单项失灵. 因为最终控件生成的HTML代码将是 location.href='XXXXX' 或 ''__doPostBack('YYYYYY')". 哈! 像JS注入吧~.

然后我们在SPGridVIew的OnRowDataBound事件中写些判断代码来控制不同情况下菜单模板的显示. 我们不打算让用户订购Discontinued的产品, 并在订购菜单项中使用!号图标提示该产品单价超过了$40.

protected void SPGridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if(e.Row.RowType == DataControlRowType.DataRow)
{
Microsoft.SharePoint.WebControls.Menu menu = e.Row.Cells[1].Controls[0] as Microsoft.SharePoint.WebControls.Menu;
if(menu != null)
{
bool discontinued = Convert.ToBoolean(DataBinder.Eval(e.Row.DataItem, "Discontinued"));
decimal unitPrice = Convert.ToDecimal(DataBinder.Eval(e.Row.DataItem, "UnitPrice"));
if(discontinued)
{
menu.HiddenMenuItems.Add(this.mitOrder);
menu.HiddenMenuItems.Add(this.mitOrderWarn);
}
if(unitPrice >= 40m)
{
menu.HiddenMenuItems.Add(this.mitOrder);
}
else
{
menu.HiddenMenuItems.Add(this.mitOrderWarn);
}
}
}
}

让我们来看看现在实现了的效果.
P1-2
SPGridView_第6张图片
P1-4

我们还需要给Order Now菜单项添加回发行为.

protected override void OnInit(EventArgs e)
{
base.OnInit(e);
this.mitOrder.ClientOnClickUsingPostBackEventFromControl(this.SPGridView1, "Order:%PID%");
this.mitOrderWarn.ClientOnClickUsingPostBackEventFromControl(this.SPGridView1, "Order:%PID%");
}
这样我们的Order Now菜单项的点击行为会变成 __doPostBack('ctl00$PlaceHolderMain$SPGridView1','Order:22') 的样子. 现在我们加入代码来响应这些回发操作.
private void Order(string pid)
{
Response.Write(pid + " is ordered.");
}
protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl, string eventArgument)
{
base.RaisePostBackEvent(sourceControl, eventArgument);
if(eventArgument == null || eventArgument.Trim() == string.Empty)
{
return;
}
if(eventArgument.Contains(":"))
{
int posIndex = eventArgument.IndexOf(":");
string commandName = eventArgument.Substring(0, posIndex);
string argument = eventArgument.Remove(0, posIndex + 1);
switch(commandName)
{
case "Order":
this.Order(argument);
break;
}
this.SPGridView1.DataBind();
}
}
 
Isn't it amazing?
 
完整代码:
<%@ Page Language="C#" MasterPageFile="~masterurl/default.master" %>
<%@ Register Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Namespace="Microsoft.SharePoint.WebControls" TagPrefix="cc1" %>
<script runat="server">
   1:  
   2:     private void Order(string pid)
   3:     {
   4:         Response.Write(pid + " is ordered.");
   5:     }
   6:  
   7:     protected override void RaisePostBackEvent(IPostBackEventHandler sourceControl, string eventArgument)
   8:     {
   9:         base.RaisePostBackEvent(sourceControl, eventArgument);
  10:  
  11:         if(eventArgument == null || eventArgument.Trim() == string.Empty)
  12:         {
  13:             return;
  14:         }
  15:  
  16:         if(eventArgument.Contains(":"))
  17:         {
  18:             int posIndex = eventArgument.IndexOf(":");
  19:             string commandName = eventArgument.Substring(0, posIndex);
  20:             string argument = eventArgument.Remove(0, posIndex + 1);
  21:  
  22:             switch(commandName)
  23:             {
  24:                 case "Order":
  25:                     this.Order(argument);
  26:                     break;
  27:             }
  28:  
  29:             this.SPGridView1.DataBind();
  30:         }
  31:     }
  32:     
  33:     protected override void OnInit(EventArgs e)
  34:     {
  35:         base.OnInit(e);
  36:  
  37:         this.mitOrder.ClientOnClickUsingPostBackEventFromControl(this.SPGridView1, "Order:%PID%");
  38:         this.mitOrderWarn.ClientOnClickUsingPostBackEventFromControl(this.SPGridView1, "Order:%PID%");
  39:     }
  40:     
  41:     protected void SPGridView1_RowDataBound(object sender, GridViewRowEventArgs e)
  42:     {
  43:         if(e.Row.RowType == DataControlRowType.DataRow)
  44:         {
  45:             Microsoft.SharePoint.WebControls.Menu menu = e.Row.Cells[1].Controls[0] as Microsoft.SharePoint.WebControls.Menu;
  46:  
  47:             if(menu != null)
  48:             {
  49:                 bool discontinued = Convert.ToBoolean(DataBinder.Eval(e.Row.DataItem, "Discontinued"));
  50:                 decimal unitPrice = Convert.ToDecimal(DataBinder.Eval(e.Row.DataItem, "UnitPrice"));
  51:  
  52:                 if(discontinued)
  53:                 {
  54:                     menu.HiddenMenuItems.Add(this.mitOrder);
  55:                     menu.HiddenMenuItems.Add(this.mitOrderWarn);
  56:                 }
  57:  
  58:                 if(unitPrice >= 40m)
  59:                 {
  60:                     menu.HiddenMenuItems.Add(this.mitOrder);
  61:                 }
  62:                 else
  63:                 {
  64:                     menu.HiddenMenuItems.Add(this.mitOrderWarn);
  65:                 }
  66:             }
  67:         }
  68:     }
</script> <asp:Content ID="Content1" ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server"> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server"> </asp:Content> <asp:Content ID="Content3" ContentPlaceHolderID="PlaceHolderMain" runat="server"> <cc1:SPGridView ID="SPGridView1" runat="server" AutoGenerateColumns="False" DataSourceID="ObjectDataSource1" OnRowDataBound="SPGridView1_RowDataBound"> <Columns> <cc1:SPBoundField DataField="ProductId" HeaderText="Product ID" SortExpression="ProductId" /> <cc1:SPMenuField HeaderText="Product Name" MenuTemplateId="mtProduct" SortExpression="ProductName" TextFields="ProductName" TokenNameAndValueFields="PID=ProductId,PNAME=ProductName" NavigateUrlFields="ProductId" NavigateUrlFormat="#ProductDetail-{0}" /> <cc1:SPBoundField DataField="ProductName" HeaderText="Product Name" SortExpression="ProductName" /> <cc1:SPBoundField DataField="CategoryName" HeaderText="Category" SortExpression="CategoryName" /> <asp:BoundField DataField="UnitPrice" DataFormatString="${0:F2}" HeaderText="Unit Price" SortExpression="UnitPrice" /> <asp:TemplateField HeaderText="Orderable" SortExpression="Discontinued"> <itemtemplate> <asp:Label id="lblDiscontinued" runat="server" text='<%# Convert.ToBoolean(Eval("Discontinued")) ? "Yes" : "No" %>'></asp:Label> </itemtemplate> </asp:TemplateField> </Columns> </cc1:SPGridView> <cc1:MenuTemplate ID="mtProduct" runat="server"> <cc1:MenuItemTemplate ID="mitView" runat="server" Text="View Detail" ClientOnClickNavigateUrl="#ProductDetail-%PID%" /> <cc1:MenuItemTemplate ID="mitOrder" runat="server" Text="Order Now" /> <cc1:MenuItemTemplate ID="mitOrderWarn" runat="server" Text="Order Now" ImageUrl="/_layouts/images/exclaim.gif" /> </cc1:MenuTemplate> <asp:ObjectDataSource ID="ObjectDataSource1" runat="server" SelectMethod="GetProductList" TypeName="SPGridView_Demo.NorthwindData" SortParameterName="sortExpression"></asp:ObjectDataSource> </asp:Content>

你可能感兴趣的:(SPGridView)