读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应用程序)项目.
该项目模板已经包含在VS2005 SP1中, 如果你不打算安装SP1, 就需要先更新KB915364, 再安装 Microsoft Visual Studio Web Application Projects.
2. 把引用中无用System.Web.Mobile等Assembly都去掉, 加入Microsoft.SharePoint (找不到的人去找块豆腐撞死好了). 再建个名为 ~masterurl 的目录, 放入一个default.master.
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, 再发布到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>
效果:
然后我们给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}" />
<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); } } } }
我们还需要给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%"); }
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(); } }
<%@ 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: else63: {
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>
x后台部分:
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
如何添加回发
最近做的项目需要使用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);
在下面的代码中,为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);
…
}
void mit_OnPostBack(string id)
{
//此处的id就是菜单对应的那个列表项的ID了,可以通过ID找到列表项并进行操作。在这个例子中,我只是用一个label来显示该ID
Label1.Text = id;
}