SPGridView

http://www.cnblogs.com/enjoyment的文章,自己整理后:

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

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

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

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

CreateP1-1 CreateP1-2

我们先试着绑些数据给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>

效果:
P1-1

然后我们给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
P1-3
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>
Isn't it amazing?
 
个人修改后的完整代码(无回发部分):
 

Code

x后台部分:

Code


Have you ever wanted to create a custom SharePoint menu using a MenuItemTemplate in a delegate control and handle a post back, for example in the welcome menu delegate control? Using the ClientOnClickScript and ClientOnClickScript properties is fairly easy, but wiring up the ClientOnClickUsingPostBackEvent can be a little tricky. To get around this I subclassed the MenuItemTemplate with an event to which you can subscribe. The menu item will target itself for postback, unless you specify otherwise – or it can be used as a normal menuitem by not subscribing to the event. Here is the code for the class:

Code
Then simply instantiate an ObjectDataSource object that points to the example data class in the CreateChildControl() method of the web part:


Code

如何添加回发



最近做的项目需要使用SPGridView,并在SPGridView使用下拉菜单。

参考了一下这篇博客:

http://www.cnblogs.com/enjoyment/archive/2008/12/28/1364142.html

 

关于如何通过MenuItemTemplate的ClientOnClickNavigateUrl属性来指定此菜单项的连接,这篇文章中写得很清楚,我就不多说了。

至于如何处理菜单项的回发行为,我就看得有点晕了。后来又参考了一下这篇文章:

http://www.thesug.org/blogs/patrickr/Lists/Posts/Post.aspx?List=8afc69af%2Df9fc%2D4786%2D816f%2D6419264c42da&ID=18

总算是做出来了,下面总结一下:

1.首先需要定义一个类,该类继承自MenuItemTemplate,并实现 IPostBackEventHandler接口
代码如下:


class PostBackMenuItemTemplate : MenuItemTemplate, IPostBackEventHandler
{
     
protected override void
 EnsureChildControls()
    {
        
if (!this
.ChildControlsCreated)
        {
            
base
.EnsureChildControls();
            
if (string.IsNullOrEmpty(this
.ClientOnClickUsingPostBackEvent))
            {
                
this.ClientOnClickUsingPostBackEventFromControl(this"%ItemID%");//此处的"%ItemID%" 要与后面的  menuCol.TokenNameAndValueFields = "ItemID=ID"; 相对应

            }
        }
    }

    
public void RaisePostBackEvent(string
 eventArgument)
    {
        MenuItemEventHandler handler 
= this
.OnPostBack;
        
if (handler != null
)
        {
            handler(eventArgument);
        }
    }

    
public event
 MenuItemEventHandler OnPostBack;
}

public delegate void MenuItemEventHandler(string
 id);
2.下面是一个例子演示了如何使用

 

在下面的代码中,为SPGridView添加了一个菜单列,菜单中包含一个菜单项,点击后由 mit_OnPostBack 方法处理回发

 


protected void initSPGridView()
{

  SPMenuField menuCol 
= new
 SPMenuField();
        menuCol.TextFields 
= "用品名称"
;
        menuCol.HeaderText 
= "用品名称"
;
        menuCol.MenuTemplateId 
= "menu"
;
        menuCol.TokenNameAndValueFields 
= "ItemID=ID"//此处要与前面的 this.ClientOnClickUsingPostBackEventFromControl(this, "%ItemID%"); 相对应

        MenuTemplate mt = new MenuTemplate();
        mt.ID 
= "menu"
;
        PostBackMenuItemTemplate mit 
= new
 PostBackMenuItemTemplate();
        mit.ClientOnClickPostBackConfirmation 
= "你确定要删除吗?"
;
        mit.Text 
= "删除"
;
        mit.ID 
= "menu1";//必须要给ID赋值,否则会出错

        mit.OnPostBack+=new MenuItemEventHandler(mit_OnPostBack);
        mt.Controls.Add(mit);
        
this
.Controls.Add(mt);
        SPGridView1.Columns.Add(menuCol);

}
处理MenuItemTemplate的回发

 


 void mit_OnPostBack(string id)
    {
        
//此处的id就是菜单对应的那个列表项的ID了,可以通过ID找到列表项并进行操作。在这个例子中,我只是用一个label来显示该ID

        Label1.Text = id;
    }

 


posted @ 2009-08-01 14:11  邑尘  阅读(1219)  评论(2编辑  收藏  举报