帮酷LOGO
0 0 评论
文章标签:FIX  Enhancement  GRID  Gridview  

介绍

GridView 中,你是否需要控制列的宽度,特别是当 GridView的宽度为 relative 时( 例如: width=" 100%") 和列一直得到它们之间的GridView的总宽度? 所有你想要的只是为每一列带来足够的宽度,以显示它的内容,但它的余的宽度可以分配。 你有没有尝试过 wrap="false""GridViewRowStyle 或者 HeaderStyle 中,在所有浏览器中都无法正常工作? 这篇文章是我在解决这两个问题时所做的,它既适用于 IE,也适用于。

此外,我还提供了在 GridView 中使用自动生成的列来解决问题的方法。 众所周知,有 GridView的属性: AutoGenerateColumns,当设置为 true 时,将自动从数据源生成列。 问题是我们在生成的列上有有限的控制( 如果有的话)。 例如如果 DataSource 包含日期列,显示的数据将不会显示为格式化的,例如 (1/1/2008 12: 00: 00 AM,而不是 (1/1/2008) 或者( 01/01/2008 ),即 等等。 这是个问题,因为我们不能改变格式以满足我们的需求。 解决这个问题的最好方法是从 GridView 中选择 inherit 并定制 GridView。 对于那些不希望在它的项目中处理额外引用的( 我像),我有另一种方法。

背景

我的解决方案取决于JavaScript和 DOM。 我正在使用元素注入到现有的HTML中。 代码并不是那么复杂,而且你不需要被这些令人害怕的术语吓到。 你需要了解一些JavaScript和 DOM ( 文档对象模型) 实际上意味着什么。 如果你发现你缺少这样的知识,几分钟后就会节省你的时间。

对于自动生成的列问题,解决方案取决于对不具有 public 辅助功能的成员设置值的反射。 反射具有性能损失,在某些情况下可能会。 如果你发现我的解决方案具有弱性能( 我也没注意到我也没有测试),请尝试替代方案: 继承。

插图

下面是一些图片,说明了我的解决方案:

这就是示例代码在没有解决方案的情况下的样子:

NoneFixed.jpg

这就是如何使用格式化的列和包装未更改的方式看起来如这里:

WrappingNotFixed.jpg

这是对标题进行格式化和换行的列的外观:

WrappingFixedHeader.jpg

在启用所有功能时,( 对 header 和数据行进行固定的包装):

AllFixed.jpg

这是日期字段没有格式化的方式:

AutoGenColsNoFormatting.jpg

这是应用格式化后显示日期字段的方式:

AutoGenColsWithFormatting.jpg

列宽和换行

ColumnWidthsAndWrapping.aspx

解决方案有两个部分: 驻留在ASPX页面中的JavaScript,以及注册对这些函数的调用的代码,这些函数驻留在该页面后面的代码中。

让我们从JavaScript函数开始:

function formatGrid(el)
{
 el = document.getElementById(el); 
 var numOfCols = 
 el.getElementsByTagName("TR")[1].getElementsByTagName("TD").length; 
 var colGrp = document.createElement("colgroup"); 
 var col = document.createElement("col");
 col.setAttribute("span", numOfCols-1);
 col.setAttribute("width", "1");
 col.setAttribute("white-space", "nowrap");
 col.setAttribute("padding-left", "2");
 col.setAttribute("padding-right", "2");
 col.setAttribute("border-width", "2");
 colGrp.appendChild(col); 
 el.insertBefore(colGrp, el.firstChild); 
}

函数 formatGrid 接收文档中元素的ID,在我们的例子中,它通常是 GridView。 这个函数的作用是格式化 GridView,这样每列的宽度就足以显示它的内容,最后的列将获得剩余的宽度。 记住,我们通常在这里处理一个宽度为 100%的GridView。 代码比较清晰,我将指出以下内容:

  • 我们正在获取 GridViewnumOfCols 中的列数,并稍后使用 numOfCols - 1
  • ColGroup 元素负责分组列并为它们分配公共属性。
  • Col 元素是 ColGroup 元素的子级,将重写 ColGroup 元素上设置的内容这就是为什么要在 Col 元素上设置属性( 详细信息) 检查 http://www.w3.org/TR/html401/struct/tables.html#h- 11.2.4.1
  • span<code> 属性表示 Col 元素将会影响多少列;在我们的例子中,我们需要限制除最后一个列之外的所有列的宽度。
  • 唯一需要的属性是 spanwidth 属性,其他属性是可选的。

以下函数解决了问题的包装部分,我们需要在 GridView的头中像"名字"这样的内容出现在同一行,而不是在两行中拆分和拆分。 这里外,这适用于数据可以长的数据行,因这里被强制换行到两行或者多行中。 我们可能需要它们在一行中显示为一个单位。 现在,使用以下函数更容易:

function addNoWrapSpan(el, noWrap)
{
 var span = document.createElement("span");
 while(el.childNodes.length> 0)
 {
 var child = el.firstChild;
 el.removeChild(child);
 span.appendChild(child);
 } 
 if(noWrap)
 span.style.whiteSpace = "nowrap";
 else span.style.whiteSpace = "inherit";
 el.appendChild(span);
}function setNoWrap(el, noWrapTH, noWrapTD)
{
 el = document.getElementById(el);
 if(noWrapTH)
 {
 var allTHs = el.getElementsByTagName("TH"); 
 for(var i = 0; i <allTHs.length; i++)
 {
 addNoWrapSpan(allTHs[i], noWrapTH);
 }
 }
 if(noWrapTD)
 {
 var allTDs = el.getElementsByTagName("TD"); 
 for(var i = 0; i <allTDs.length; i++)
 {
 addNoWrapSpan(allTDs[i], noWrapTD);
 }
 }
}

函数 addNoWrapSpan 所有元素 inside 为提供的元素 el ( 是 td 还是 th) 移动到动态生成的span 元素that根据 noWrap 是否是 true 或者 false 来生成适当的样式。 所使用的样式为:

span.style.whiteSpace = "nowrap"; 

如果 noWrap 是 true,并且是 false,则设置以下样式:

span.style.whiteSpace = "inherit";

setNoWrap 接收3 个参数:我们正在配置的元素,noWrapTH 表示在子 th 上是否允许包装,以及 noWrapTD 是否允许在 td的inside 上包装包装。 如你所见,setNoWrap 在必要时调用 addNoWrapSpan

附加的示例项目包含一个示例 GridView:

<asp:GridViewID="GridView1"runat="server"AutoGenerateColumns="False"Width="100%"CssClass="GridClass"><Columns><asp:BoundFieldDataField="FirstName"HeaderText="First Name"/><asp:BoundFieldDataField="LastName"HeaderText="Last Name"/> 
 <asp:BoundFieldDataField="Age"HeaderText="Age"/><asp:CommandFieldShowSelectButton="True"/></Columns></asp:GridView>

简单地说,它显示代码中的数据集,也有一个 CommandField,它有一个选择 LinkButton。 现在,代码:

private DataTable BuildDataSource()
{
 DataTable dt = new DataTable();
 dt.Columns.Add("FirstName", typeof(string));
 dt.Columns.Add("LastName", typeof(string));
 dt.Columns.Add("Age", typeof(int));
 DataRow dr;
 dr = dt.NewRow();
 dr["FirstName"] = "John the first";
 dr["LastName"] = "Doe";
 dr["Age"] = 23;
 dt.Rows.Add(dr);
 dr = dt.NewRow();
 dr["FirstName"] = "Clark";
 dr["LastName"] = "Kent";
 dr["Age"] = 28;
 dt.Rows.Add(dr);
 return dt;
}

这是为测试目的显示数据的简单方法;这将被代码替换为在实际情况下从数据库中检索数据:

protectedoverridevoid OnPreRender(EventArgs e)
{
 base.OnPreRender(e);
 string formatScript = "";
 formatScript += string.Format("formatGrid('{0}');" + 
 "setNoWrap('{0}', false, true);", 
 GridView1.ClientID); 
 ClientScript.RegisterStartupScript
 (this.GetType(), "format", formatScript, true);
}

这就是我们的JavaScript使用的地方。 请注意,我正在调用 setNoWrap 并指定 false 对于 noWrapTH 参数,这意味着如果它的内容很长,那么 header 仍然会包装,你可以能需要更改它。 true

自动生成的列

AutoGeneratedColumns.aspx

这里我描述了一个方法,它允许我们格式化日期 inside 自动生成的列( AutoGenerateColumns =true )。

privatevoid FormatDatesInGridView(GridView gv, GridViewRow gvr)
{
 if (gv.DataSource!= null)
 {
 DataTable dt = null;
 if (gv.DataSource is DataView)
 {
 dt = ((DataView)gv.DataSource).Table;
 }
 elseif (gv.DataSource is DataSet)
 {
 dt = ((DataSet)gv.DataSource).Tables[0];
 }
 elseif (gv.DataSource is DataTable)
 {
 dt = (DataTable)gv.DataSource;
 }
 if (gvr.RowType == DataControlRowType.DataRow)
 {
 foreach (TableCell tc in gvr.Cells)
 {
 DataControlFieldCell dcfc = (DataControlFieldCell)tc;
 if (dcfc.ContainingField is AutoGeneratedField)
 {
 AutoGeneratedField agf = (AutoGeneratedField)dcfc.ContainingField;
 if (agf.DataType == typeof(DateTime))
 {
 System.Reflection.FieldInfo fi;
 fi = typeof(BoundField).GetField("_dataFormatString", 
 System.Reflection.BindingFlags.Instance | 
 System.Reflection.BindingFlags.NonPublic);
 fi.SetValue(agf, "{0:dd/MM/yyyy}");
 fi = typeof(DataControlField).GetField("_statebag", 
 System.Reflection.BindingFlags.Instance | 
 System.Reflection.BindingFlags.NonPublic);
 ((StateBag)fi.GetValue(agf))["DataFormatString"] = 
 "{0:dd/MM/yyyy}";
 System.Reflection.MethodInfo mi;
 mi = typeof(BoundField).GetMethod("OnFieldChanged", 
 System.Reflection.BindingFlags.NonPublic | 
 System.Reflection.BindingFlags.Instance);
 mi.Invoke(agf, null);
 ((BoundField)agf).HtmlEncode = false; //Fix for Dates Formatting }
 }
 }
 }
 }
}

调用 FormatDatesInGridView的方法将从 inside 调用要格式化的GridViewRowCreated 事件处理程序。 这里方法接收两个参数: GridView 和当前行。 调用事件处理程序的外观如下:

protectedvoid GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
 if(e.Row.RowType == DataControlRowType.DataRow)
 FormatDatesInGridView(GridView1, e.Row);
}

方法首先确定 GridView ( 如果你有其他类型的DataSource,则可以修改)的DataSource。 然后,检查当前 GridViewRow的所有单元格,如果 AutoGeneratedFieldContainingField的类型为 DateTime,则使用反射执行以下操作:

  • _dataFormatString 设置为 "{0:dd/MM/yyyy}" ( 可以根据需求进行更改)。
  • Viewstate["DataFormatString"] 设置为相同的值 "{0:dd/MM/yyyy}",方法是设置 _statebag["DataFormatString"]的值。
  • 调用 OnFieldChanged 方法将这些更改应用到( 这里方法最终导致绑定发生)。
  • 在某些环境中,HtmlEncode 有时是 true,这会阻止我们控制日期格式。 这就是我添加这一行的原因 false

就是这样,日期字段已经格式化。 我如何使用反射器和检查类内部工作方式是如何实现这些步骤的。

更新历史记录

  • 12 2009年02月: 日期格式的HtmlEncode 修复。
  • 03 2008年08月: 自动生成的列。
  • 22 2008年06月: 添加了演示部分。


文章标签:GRID  Gridview  FIX  Enhancement  

Copyright © 2011 HelpLib All rights reserved.    知识分享协议 京ICP备05059198号-3  |  如果智培  |  酷兔英语