views:

1869

answers:

2

Hi

I have extended the standard GridView control in ASP.NET. Basically it is just the same, but paging and sorting is done using callbacks.

I have successfully created my own PagerTemplate, but it only works when PagerSettings.Position is set to Top. I want to have the pager at both the top and the bottom.

I get an HttpException saying: The table must contain row sections in order of header, body, then footer.

Anyone know what this could be?

I already tried calling the MakeAccessible() method, but that doesn't help.

Code:

private int _IdColumnIndex = -1;

public override object DataSource
{
  get
  {
    return HttpContext.Current.Session[this.ClientID + "_DataSource"];
  }
  set
  {
    if (!value.GetType().Equals(typeof(DataView)))
      throw new ArgumentException("Only System.Data.DataView is accepted as a DataSource for DataGridView!");

    HttpContext.Current.Session[this.ClientID + "_DataSource"] = value;
  }
}

/// <summary>
/// Sets the default settings.
/// </summary>
private void SetDefaultSettings()
{
  base.AllowPaging = true;
  base.AllowSorting = true;
  base.ShowHeader = true;
  base.ShowFooter = true;
  base.PagerTemplate = new GridPagerTemplate(PAGER_ID, this);
  base.PagerSettings.Position = PagerPosition.TopAndBottom;
  base.PageSize = 10;

  AddSelectionCheckbox();
}

//private void MakeAccessible()
//{
//  if (this.Rows.Count > 0)
//  {
//    //This replaces <td> with <th> and adds the scope attribute 
//    this.UseAccessibleHeader = true;

//    //This will add the <thead> and <tbody> elements
//    this.HeaderRow.TableSection = TableRowSection.TableHeader;

//    //This adds the <tfoot> element. Remove if you don't have a footer row 
//    this.FooterRow.TableSection = TableRowSection.TableFooter;
//  }
//} 

private void AddSelectionCheckbox()
{
  TemplateField tf = new TemplateField();
  tf.HeaderText = "";
  tf.ItemTemplate = new CheckboxTemplate(ROW_CHECKBOX_ID);

  base.Columns.Add(tf);
}

protected override void OnInit(EventArgs e)
{
  SetDefaultSettings();

  base.DataBound += new EventHandler(DataGridView_DataBound);
  base.RowDataBound += new GridViewRowEventHandler(DataGridView_RowDataBound);

  base.OnInit(e);
}

protected override void OnPreRender(EventArgs e)
{
  base.OnPreRender(e);
  //this.PaintRows();
  //this.FormatCells();

  // Get JavaScript for this DataGridView, and include it on the page.
  string scriptUrl = Page.ClientScript.GetWebResourceUrl(this.GetType(), "Yakinix.UI.Controls.DataGridView.js");
  Page.ClientScript.RegisterClientScriptInclude("DataGridView", scriptUrl);

  string js = "function DGV_MakeCallback(argument)" +
              "{" +
                  "__theFormPostData = '';" +
                  "__theFormPostCollection = new Array();" +
                  "WebForm_InitCallback();" +
                  GetCallbackEventReference("DGV_Callback") +
              ";}";

  Page.ClientScript.RegisterClientScriptBlock(typeof(DataGridView), typeof(DataGridView).FullName + "DGV_MakeCallback", js, true);

  if (this.HeaderRow != null)
    this.HeaderRow.TableSection = TableRowSection.TableHeader;
}

private string GetCallbackEventReference(string func)
{
  string callbackEventReference = Page.ClientScript.GetCallbackEventReference(this, "argument", func, "'" + ClientID + "'", false);

  return callbackEventReference;
}

public override void RenderBeginTag(HtmlTextWriter writer)
{
  writer.RenderBeginTag(HtmlTextWriterTag.Div); // <div>
  base.RenderBeginTag(writer);
}

public override void RenderEndTag(HtmlTextWriter writer)
{
  base.RenderEndTag(writer);
  writer.RenderEndTag(); // </div>
}

/// <summary>
/// Renders the gridview control and strips whitespace. It also
/// applies the right formatting.
/// </summary>
public string RenderHtml()
{
  using (StringWriter sw = new StringWriter(CultureInfo.InvariantCulture))
  {
    //this.PaintRows();
    //this.FormatCells();

    if (this.HeaderRow != null)
      this.HeaderRow.TableSection = TableRowSection.TableHeader;

    this.UseAccessibleHeader = true;
    this.RenderControl(new HtmlTextWriter(sw));
    string html = sw.ToString();

    return html;
  }
}

// TODO: Style all rows for the column matching current SortExpression.
protected void DataGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
  //DataGridView gridView = (DataGridView)sender;

  //if (gridView.SortExpression.Length > 0)
  //{
  //  int cellIndex = -1;

  //  //  find the column index for the corresponding sort expression
  //  foreach (DataControlField field in gridView.Columns)
  //  {
  //    if (field.SortExpression == gridView.SortExpression)
  //    {
  //      cellIndex = gridView.Columns.IndexOf(field);
  //      break;
  //    }
  //  }

  //  if (cellIndex > -1)
  //  {
  //    if (e.Row.RowType == DataControlRowType.Header)
  //    {
  //      //  this is a header row,
  //      //  set the sort style
  //      e.Row.Cells[cellIndex].CssClass += (gridView.SortDirection == SortDirection.Ascending ? " sortascheader" : " sortdescheader");
  //    }
  //    else if (e.Row.RowType == DataControlRowType.DataRow)
  //    {
  //      //  this is a data row
  //      e.Row.Cells[cellIndex].CssClass += (e.Row.RowIndex % 2 == 0 ? " sortaltrow" : "sortrow");
  //    }
  //  }
  //}

  RenderDetailsRow(e);
}

/// <summary>
/// Renders the details row, which is hidden when rendered. Clicking a details link, displays the row and more detailed data is shown, and is editable.
/// </summary>
/// <param name="e">The <see cref="System.Web.UI.WebControls.GridViewRowEventArgs"/> instance containing the event data.</param>
private void RenderDetailsRow(GridViewRowEventArgs e)
{
  // Only add a details row to DataRows
  if (false && e.Row.RowType == DataControlRowType.DataRow)
  {
    Table table = e.Row.Parent as Table;

    if (table != null)
    {
      GridViewRow row = new GridViewRow(-1, -1, DataControlRowType.DataRow, DataControlRowState.Normal);
      row.Style[HtmlTextWriterStyle.Display] = "none";

      TableCell rowCell = new TableCell();
      rowCell.ColumnSpan = this.Columns.Count;
      rowCell.Height = new Unit(100);

      row.Cells.Add(rowCell);
      table.Rows.Add(row);

      HtmlAnchor htmlAhref = new HtmlAnchor();
      htmlAhref.HRef = "javascript:void(0);";
      htmlAhref.Attributes.Add("onclick", "ToggleElement('" + row.ClientID + "');");
      htmlAhref.InnerText = "Test";

      e.Row.Cells[e.Row.Cells.Count - 1].Controls.Add(htmlAhref);
    }
  }
}

protected void DataGridView_DataBound(object sender, EventArgs e)
{
  FillPagerDetails(base.TopPagerRow);
  FillPagerDetails(base.BottomPagerRow);
}

private void FillPagerDetails(GridViewRow pagerRow)
{
  if (pagerRow != null)
  {
    // Retrieve the DropDownList and Label controls from the row.
    DropDownList pageList = pagerRow.Cells[0].FindControl(PAGER_ID) as DropDownList;

    if (pageList != null)
    {
      pageList.Attributes.Add("onchange", "DataGridViewPage(this.value, '" + ClientID + "');");

      // Add the number of pages to the ListBox
      for (int i = 0; i < base.PageCount; i++)
      {
        int pageNumber = i + 1;

        // Create a ListItem that represents a page
        ListItem item = new ListItem(String.Format("{0}", pageNumber), i.ToString());

        // If the page is already selected, make sure the 
        // ListBox select the selected page
        if (i == base.PageIndex)
          item.Selected = true;

        // Add the ListItem object to the Items collection of the DropDownList.
        pageList.Items.Add(item);
      }
    }
  }
}

protected override void InitializeRow(GridViewRow row, DataControlField[] fields)
{
  base.InitializeRow(row, fields);

  if (row.RowType == DataControlRowType.Header)
  {
    for (int i = 0; i < row.Cells.Count; i++)
    {
      row.Cells[i].Controls.Clear();

      if (row.Cells[i].Text.Equals("CHKCOL", StringComparison.Ordinal))
      {
        CheckBox chkSelectAll = new CheckBox();
        chkSelectAll.ID = "chkSelectAllRows";
        row.Cells[i].Controls.Add(chkSelectAll);
      }
      else
      {
        row.Cells[i].Text = String.Format(CultureInfo.InvariantCulture, "<a href=\"javaScript:DataGridViewSort('{0}', '{2}');void(0)\" title=\"{1}\">{1}</a>", fields[i].SortExpression, fields[i].HeaderText, this.ClientID);
      }
    }
    //if (ViewState["SortExpression"] != null)
    //{
    //  int index;
    //  index = 0;// GetHeaderCellIndex(ViewState["SortExpression"].ToString());
    //  if (index != -1)
    //  {
    //    //Literal literal;
    //    //if ((SortDirection)ViewState["SortDirection"] == SortDirection.Ascending)
    //    //{
    //    //  if (string.IsNullOrEmpty(SortAscendingImageUrl))
    //    //  {
    //    //    literal = new Literal();
    //    //    literal.Text = "&#8593;";
    //    //    row.Cells[index].Controls.Add(literal);
    //    //  }
    //    //  else
    //    //  {
    //    //    sortImage.ImageUrl = SortAscendingImageUrl;
    //    //    row.Cells[index].Controls.Add(sortImage);
    //    //  }
    //    //}
    //    //else
    //    //{
    //    //  if (string.IsNullOrEmpty(SortDescendingImageUrl))
    //    //  {
    //    //    literal = new Literal();
    //    //    literal.Text = "&#8595;";
    //    //    row.Cells[index].Controls.Add(literal);
    //    //  }
    //    //  else
    //    //  {
    //    //    sortImage.ImageUrl = SortDescendingImageUrl;
    //    //    row.Cells[index].Controls.Add(sortImage);
    //    //  }
    //    //}
    //  }
    //}
  }
  else if (row.RowType == DataControlRowType.DataRow)
  {
    for (int i = 0; i < row.Cells.Count; i++)
    {
      if (fields[i].HeaderText.Equals("id", StringComparison.InvariantCultureIgnoreCase))
      {
        _IdColumnIndex = i;
        break;
      }
    }
  }
}

protected override void OnRowCreated(GridViewRowEventArgs e)
{
  base.OnRowCreated(e);

  if (e.Row.RowType == DataControlRowType.Header)
  {
    //HtmlImage img = new HtmlImage();
    //img.ID = "lbCheckAll";
    //img.Src = "/img/btn_slct_all.gif";
    //img.Alt = "Select all";
    //img.Attributes.Add("onclick", "SelectAllRows('" + this.ClientID + "',true);return false;");
    //e.Row.Cells[0].Controls.Add(img);
  }
  else if (e.Row.RowType == DataControlRowType.DataRow)
  {
    //AddCheckbox(e);
  }
  else if (e.Row.RowType == DataControlRowType.Footer)
  {
    //e.Row.Cells[0].Text = "sdsds";
  }
}

protected override void OnRowDataBound(GridViewRowEventArgs e)
{
  base.OnRowDataBound(e);

  if (e.Row.RowType == DataControlRowType.DataRow)
  {
    //AddCheckbox(e);

    if (_IdColumnIndex != -1)
      e.Row.Cells[_IdColumnIndex].Text = "<a href=\"Details.aspx?id=" + e.Row.Cells[_IdColumnIndex].Text + "\">Edit</a>";
  }
}

public override void Sort(string sortExpression, SortDirection sortDirection)
{
  DataView dv = this.DataSource as DataView;

  if (dv != null)
  {
    dv.Sort = sortExpression + " " + (sortDirection == SortDirection.Ascending ? "ASC" : "DESC");
  }
}

#region ICallbackEventHandler Members

private string callbackArg = String.Empty;

string ICallbackEventHandler.GetCallbackResult()
{
  // Save state with PageStatePersister and place it to Page.ClientState
  System.Reflection.MethodInfo mi = typeof(Page).GetMethod("SaveAllState", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
  mi.Invoke(this.Page, null);

  // Get serialized viewstate from Page's ClientState
  System.Reflection.PropertyInfo stateProp = typeof(Page).GetProperty("ClientState", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
  string state = stateProp.GetValue(this.Page, null).ToString();

  return callbackArg + "¤#¤" + state;
}

void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument)
{
  try
  {
    // Load XML from client
    XmlDocument doc = new XmlDocument();
    doc.LoadXml(eventArgument.Replace("&", "&amp;"));
    string pagerId = String.Empty;

    if (doc.DocumentElement.Name == "callback")
    {
      string gridId = doc.DocumentElement["arguments"]["gridId"].InnerText;

      switch (doc.DocumentElement["action"].InnerText)
      {
        case "sort":
          string sortExpression = doc.DocumentElement["arguments"]["sortExpression"].InnerText;
          Sort(sortExpression.Split(' ')[0].Trim(), sortExpression.Split(' ')[1].Trim().Equals("ASC") ? SortDirection.Ascending : SortDirection.Descending);
          break;

        case "page":
          string pageIndex = doc.DocumentElement["arguments"]["pageIndex"].InnerText;
          this.PageIndex = Convert.ToInt32(pageIndex);
          pagerId = base.BottomPagerRow.Cells[0].FindControl(PAGER_ID).ClientID;
          break;

        case "search":
          break;
      }

      this.DataBind();
      callbackArg = String.Format("{0}¤|¤{1}¤|¤{2}", gridId, this.RenderHtml(), pagerId);
    }

    doc = null;
  }
  catch (Exception ex)
  {
    callbackArg = String.Format(AJAX_ERROR_FORMAT, ex.Message + "\n\n" + ex.StackTrace);
  }
  finally
  {
    if (callbackArg.Equals(string.Empty))
    {
      callbackArg = String.Format(AJAX_ERROR_FORMAT, "An AJAX Error occurred!");
    }
  }
}

#endregion
A: 

Giving it a quick look over, you want to make sure that your header and footer pagers are correctly set to TableRowSection.TableHeader or TableRowSection.TableFooter.

You might be trying to draw your Bottom Pager as a thead and not a tfoot.

The error i believe is being thrown by the WebControls.Table

Glennular
A: 

This is what works for me.. the PagerRows must be set to their proper sections.

I got this tip from http://www.codeproject.com/Members/iguigova?msg=3153516 (search for MakeAccessible)

protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);

    if (Rows.Count > 0)
    {
        UseAccessibleHeader = true;
        HeaderRow.TableSection = TableRowSection.TableHeader;
        FooterRow.TableSection = TableRowSection.TableFooter;
    }

    if (TopPagerRow != null)
    {
        TopPagerRow.TableSection = TableRowSection.TableHeader;
    }
    if (BottomPagerRow != null)
    {
        BottomPagerRow.TableSection = TableRowSection.TableFooter;
    }
}
Dave Anderson