在使用Grid的时候,会用到链接跳转。如果只是普通的链接跳转,那只要使用a标签的href就可以实现。但是有时,我们希望在链接跳转的时候,能够引发回发事件,在后台作出一定的处理,然后再跳转。这样要如何实现呢?我们可以定义一个LinkButtonField来实现。代码如下
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.UI;
namespace AspNetServerControl
{
/// <summary>
/// 表格链接按钮列
/// </summary>
[ToolboxItem(false)]
[ParseChildren(true)]
[PersistChildren(false)]
public class LinkButtonField : BaseField
{
}
}
注:LinkButtonField也是继承自BaseField。添加了一个列字段后,我们就需要在列编辑器中,增加相应的类型,代码如下:
[Designer("AspNetServerControl.Design.GridDesigner, AspNetServerControl.Design")]
[ToolboxData("<{0}:Grid Title=\"Grid\" runat=\"server\"><Columns></Columns></{0}:Grid>")]
[ToolboxBitmap(typeof(Grid), "toolbox.Grid.bmp")]
[Description("表格控件")]
[ParseChildren(true)]
[PersistChildren(false)]
[ControlBuilder(typeof(NotAllowWhitespaceLiteralsBuilder))]
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
public class Grid : ControlBase, IPostBackEventHandler
{
///属性代码请参照《 Asp.Net服务器控件开发的Grid实现(二)Html标记渲染》
#region 事件
#region OnRowCommand
Defines the Click event.
//public event EventHandler<GridCommandEventArgs> RowCommand;
Invoke delegates registered with the Click event.
//protected virtual void OnRowCommand(GridCommandEventArgs e)
//{
// if (RowCommand != null)
// {
// RowCommand(this, e);
// }
//}
private static readonly object _rowCommandHandlerKey = new object();
/// <summary>
/// 行内事件
/// </summary>
[Category(CategoryName.ACTION)]
[Description("行内事件")]
public event EventHandler<GridCommandEventArgs> RowCommand
{
add
{
Events.AddHandler(_rowCommandHandlerKey, value);
}
remove
{
Events.RemoveHandler(_rowCommandHandlerKey, value);
}
}
/// <summary>
/// 触发行内事件
/// </summary>
/// <param name="e">事件参数</param>
protected virtual void OnRowCommand(GridCommandEventArgs e)
{
EventHandler<GridCommandEventArgs> handler = Events[_rowCommandHandlerKey] as EventHandler<GridCommandEventArgs>;
if (handler != null)
{
handler(this, e);
}
}
#endregion
#endregion
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
if (_columns == null)
{
return;
}
writer.Write(
String.Format("<table id=\"{0}\" name=\"{1}\" rules=\"all\" border=\"1\" cellspacing=\"0\" style=\"width:100%;border-collapse:collapse;\">", UniqueID, UniqueID));
//RenderHeader(writer);
RenderBody(writer);
writer.Write("</table>");
}
private void RenderBody(HtmlTextWriter writer)
{
DataTable dt = DataSource as DataTable;
if (dt == null || dt.Rows.Count <= 0)
{
return;
}
writer.Write("<tbody>");
int rowIndex = 0;
int columnIndex = 0;
foreach (DataRow row in dt.Rows)
{
writer.Write(String.Format("<tr {0}>", GetRowStyle()));
columnIndex = 0;
foreach (GridColumn column in Columns)
{
if (column is LinkButtonField)
{
writer.Write(String.Format("<td {0}><a {1} name=\"{2}\">{3}</a></td>",
GetItemStyle(column),
GetLinkButtonPostBack(column as LinkButtonField, rowIndex, columnIndex),
column.UniqueID,
row[column.DataField]));
}
else
{
writer.Write(String.Format("<td {0}>{1}</td>", GetItemStyle(column), row[column.DataField]));
}
columnIndex++;
}
writer.Write("</tr>");
rowIndex++;
}
writer.Write("</tbody>");
}
private String GetLinkButtonPostBack(LinkButtonField linkButton, int rowIndex, int columnIndex)
{
if (linkButton == null)
{
return "";
}
String arg = String.Format("Command${0}${1}${2}${3}", rowIndex, columnIndex, linkButton.CommandName, linkButton.CommandArgument);
String clientScript = Page.ClientScript.GetPostBackClientHyperlink(this, arg);
String href = String.Format("href=\"{0}\"", clientScript);
return href;
}
///其他代码请参照《 Asp.Net服务器控件开发的Grid实现(二)Html标记渲染》
public void RaisePostBackEvent(string eventArgument)
{
if (eventArgument.StartsWith("Command$"))
{
string[] commandArgs = eventArgument.Split('$');
if (commandArgs.Length == 5)
{
GridCommandEventArgs gridCommandEventArgs =
new GridCommandEventArgs(Convert.ToInt32(commandArgs[1]),
Convert.ToInt32(commandArgs[2]),
commandArgs[3],
commandArgs[4]);
OnRowCommand(gridCommandEventArgs);
}
}
}
注:
1.IPostBackEventHandler接口需要实现RaisePostBackEvent方法。
2.在页面回发时,是通过控件的name来索引到对应的事件,然后回发到目标的。所以对于Grid控件,我们必须要在Render的时候将name赋值,然后在生成回发脚本时将其对应上。
(1)所以在Render函数的table标记的name要赋值,这里使用UniqueID以确保唯一性。
(2)同时在RenderBody时,如果是LinkButtonField的列,就增加回发脚本。即将LinkButtonField渲染到表格的单元格中时,使用a标记渲染,同时将其href赋值相应的脚本。在GetLinkButtonPostBack中生成相应的回发脚本。
(3)GetLinkButtonPostBack函数中,将单元格的列索引和行索引以及LinkButtonField的CommandName和CommandArgument一同作为回发参数。同时,为了便于区分,使用Command作为前缀,并以$作为参数间的分隔符。使用Page.ClientScript.GetPostBackClientHyperlink来生成回发的js脚本,使用该系统方法相应简单,当然也可以直接写出脚本,代码如下。
javascript:__doPostBack('Grid_Edit','Command$0$3$LINK$cmdarg'
3.在RaisePostBackEvent收到回发事件后,将相应的参数分离出来,并作相应的处理。为了参够让使用Grid自定义回发后调用的事件,我们自定义了一个OnRowCommand事件。
4.OnRowCommand事件由三个部分主成。
(1)唯一KEY:使用静态只读的object,即_rowCommandHandlerKey。
(2)将事件添加到委托事件的处理列表中,即RowCommand中。也有直接使用委托来定义的,但性能上不及这种定义。委托直接定义的,原则上是线程安全些。为了能够更好的自定义事件,我们定义了一个事件参数GridCommandEventArgs。代码见后文。
(3)使用Grid的开发者实现的OnRowCommand事件,相当于是按钮的OnClick事件。注意,此事件的命名必须与前面的委托相对应,即只在前面增加一个On。
GridCommandEventArgs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AspNetServerControl
{
/// <summary>
/// 表格行命令事件参数
/// </summary>
public class GridCommandEventArgs : EventArgs
{
private int _rowIndex;
/// <summary>
/// 行索引
/// </summary>
public int RowIndex
{
get { return _rowIndex; }
set { _rowIndex = value; }
}
private int _columnIndex;
/// <summary>
/// 列索引
/// </summary>
public int ColumnIndex
{
get { return _columnIndex; }
set { _columnIndex = value; }
}
private string _commandName;
/// <summary>
/// 命令名称
/// </summary>
public string CommandName
{
get { return _commandName; }
set { _commandName = value; }
}
private string _commandArgument;
/// <summary>
/// 命令参数
/// </summary>
public string CommandArgument
{
get { return _commandArgument; }
set { _commandArgument = value; }
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="rowIndex">行索引</param>
/// <param name="columnIndex">列索引</param>
/// <param name="commandName">命令名称</param>
/// <param name="commandArgument">命令参数</param>
public GridCommandEventArgs(int rowIndex, int columnIndex, string commandName, string commandArgument)
{
_rowIndex = rowIndex;
_columnIndex = columnIndex;
_commandName = commandName;
_commandArgument = commandArgument;
}
}
}
在完成这些后,就可以使用了。
UI代码如下:
<div>
<S:Grid runat="server" ID="Grid_Edit" OnRowCommand="Grid_Edit_RowCommand">
<RowStyle Height="50px;" />
<Columns>
<S:BoundField runat="server" ID="BoundField1" HeaderText="货号" DataField="NO">
<ItemStyle HorizontalAlign="Center" />
</S:BoundField>
<S:BoundField runat="server" ID="BoundField2" HeaderText="类型" DataField="Type">
<ItemStyle HorizontalAlign="Center" />
</S:BoundField>
<S:BoundField runat="server" ID="BoundField3" HeaderText="状态" DataField="Status">
<ItemStyle HorizontalAlign="Center" />
</S:BoundField>
<S:LinkButtonField runat="server" ID="LinkButtonField1" HeaderText="链接"
DataField="Link"
CommandName="LINK">
</S:LinkButtonField>
</Columns>
</S:Grid>
</div>
对应的后台代码如下:
private void InitLoad()
{
Grid_Edit.DataSource = GenerateData();
}
private DataTable GenerateData()
{
DataTable dt = new DataTable();
dt.Columns.Add("NO");
dt.Columns.Add("Type");
dt.Columns.Add("Status");
dt.Columns.Add("Link");
dt.Rows.Add(new String[] { "H10001", "食品", "已售完", "http://www.baidu.com" });
dt.Rows.Add(new String[] { "H10002", "蔬菜", "待销售", "http://www.baidu.com" });
dt.Rows.Add(new String[] { "H10003", "水果", "待销售", "http://www.baidu.com" });
dt.Rows.Add(new String[] { "H10004", "器具", "销售中", "http://www.baidu.com" });
return dt;
}
protected void Grid_Edit_RowCommand(object sender, AspNetServerControl.GridCommandEventArgs e)
{
//需要处理的代码
}
这样Grid的回发事件就是实现了。其他的回发处理,可以仿此。