Asp.Net服务器控件开发的Grid实现(四)回发事件

版权所有,禁止匿名转载;禁止商业使用。

在使用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。

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的回发事件就是实现了。其他的回发处理,可以仿此。


0 0