客户要一个有滚动条的asp.net datagrid控件,只好写了:
using system;
using system.web.ui;
using system.web.ui.webcontrols;
using system.componentmodel;
using system.diagnostics;
using system.io;
using system.web.ui.design.webcontrols;
using system.text;
using system.drawing;
[assembly:tagprefix("microsoft.gtec.dsv", "gtecdsv")]
namespace microsoft.gtec.dsv
{
/// <summary>
/// summary description for webcustomcontrol1.
/// </summary>
[toolboxdata("<{0}:scrollablefixedheaderdatagrid runat=server></{0}:scrollablefixedheaderdatagrid>")]
public class scrollablefixedheaderdatagrid: system.web.ui.webcontrols.datagrid
{
protected override void render(htmltextwriter output)
{
//use this flag to determine whether the component is in design-time or runtime.
//the control will be rendered differently in ide.
//dont bother to use datagriddesigner.getdesigntimehtml
bool designmode = ((site != null) && (site.designmode));
//backing up the properties need to change during the render process
string templeft = style["left"];
string temptop = style["top"];
unit tempheight = height;
string temptablestyle = style["table-layout"];
//render a "<div>" container with scrollbars.
output.writebegintag("div");
output.writeattribute("id",id + "_div");
output.writeattribute("style",
"height: " + height + ";" +
//leave 20px for the vertical scroll bar,
//assuming the end-user will not set his scroll bar to more than 20px.
"width: " + (width.value + 20) + "px;" +
"top: " + style["top"] + ";" +
"left: " + style["left"] + ";" +
"position: " + style["position"] + ";" +
"overflow-x: auto;" +
"z-index: " + style["z-index"] + ";" +
//render the scrollbar differently for design-time and runtime.
"overflow-y: " + (designmode?"scroll":"auto")
);
output.write(htmltextwriter.tagrightchar);
//the datagrid is inside the "<div>" element, so place it at (0,0).
style["left"] = "0px";
style["top"] = "0px";
//render the datagrid.
base.render(output);
output.writeendtag("div");
//restore the values
style["left"] = templeft;
style["top"] = temptop;
//the following rendering is only necessary under runtime. it has negative impact during design time.
if (!designmode)
{
//render another copy of the datagrid with only headers.
//render it after the datagrid with contents,
//so that it is on the top. z-index is more complex here.
//set height to 0, so that it will adjust on its own.
height = new unit("0px");
stringwriter sw = new stringwriter();
htmltextwriter htw = new htmltextwriter(sw);
//this style is important for matching column widths later.
style["table-layout"] = "fixed";
base.render(htw);
stringbuilder sbrenderedtable = sw.getstringbuilder();
htw.close();
sw.close();
debug.assert((sbrenderedtable.length > 0),
"rendered html string is empty. check viewstate usage and databinding.");
string temp = sbrenderedtable.tostring();
if (sbrenderedtable.length > 0)
{
//allowpaging at the top?
if ((allowpaging) && ((pagerposition.top == pagerstyle.position || (pagerposition.topandbottom == pagerstyle.position))))
{
trace.writeline(temp);
sbrenderedtable.replace(id,id + "_pager", 0, (temp.indexof(id) + id.length));
temp = sbrenderedtable.tostring();
string pager = temp.substring(0, temp.tolower().indexof(@"</tr>") + 5);
trace.writeline(pager);
output.write(pager);
output.writeendtag("table");
//start of pagers <tr>
int start = temp.tolower().indexof(@"<tr");
//end of pagers </tr>
int end = temp.tolower().indexof(@"</tr>") + 5;
//remove the <tr> for pager from the string. prepare to render the headers.
sbrenderedtable.remove(start,end-start);
trace.writeline(sbrenderedtable.tostring());
sbrenderedtable.replace(id + "_pager",id + "_headers", 0, (temp.indexof(id+"_pager") + (id+"_pager").length));
temp = sbrenderedtable.tostring();
string tableheaders = temp.substring(0, (temp.tolower()).indexof(@"</tr>") + 5);
trace.writeline(tableheaders);
output.write(tableheaders);
output.writeendtag("table");
string headerid = id + "_headers";
string pagerid = id + "_pager";
string divid = id + "_div";
string adjustwidthscript = @"
<script language=javascript>
//debugger;
var headertablerow = " + headerid + @".rows[0];
var originaltablerow = " + id + @".rows[1];"
//adjust header rows height.
+ @"
headertablerow.height = originaltablerow.offsetheight;
" +
//adjust pager rows height, width.
pagerid + @".rows[0].height = " + id + @".rows[0].offsetheight;
" +
pagerid + @".style.width = " + id + @".offsetwidth;
for (var i = 0; i < headertablerow.cells.length; i++) {
headertablerow.cells[i].width = originaltablerow.cells[i].offsetwidth;
}
" +
//also needs to adjust the width of the "<div>" at client side in addition to servier side,
//since the tables actual width can go beyond the width specified at server side under edit mode.
//the server side width manipulation is mainly for design-time appearance.
divid + @".style.width = " + id + @".offsetwidth + 20 + px;
" +
//the following script is for flow-layout. we cannot get the position of the control
//on server side if the the page is with flow-layout.
headerid + @".style.left = " + divid + @".offsetleft;
" +
headerid + @".style.top = " + divid + @".offsettop + " + pagerid + @".offsetheight;
" +
headerid + @".style.position = absolute;
" +
pagerid + @".style.left = " + divid + @".offsetleft;
" +
pagerid + @".style.top = " + divid + @".offsettop;
" +
pagerid + @".style.position = absolute;
</script>";
page.registerstartupscript("dummykey" + this.id, adjustwidthscript);
//output.write(adjustwidthscript);
}
else
{
//replace the tables id with a new id.
//it is tricky that we must only replace the 1st occurence,
//since the rest occurences can be used for postback scripts for sorting.
sbrenderedtable.replace(id,id + "_headers", 0, (temp.indexof(id) + id.length));
trace.writeline(sbrenderedtable.tostring());
//we only need the headers, stripping the rest contents.
temp = sbrenderedtable.tostring();
string tableheaders = temp.substring(0, (temp.tolower()).indexof(@"</tr>") + 5);
trace.writeline(tableheaders);
output.write(tableheaders);
output.writeendtag("table");
//client side script for matching column widths.
//cant find a way to do this on the server side, since the browser can change widths on the client side.
string adjustwidthscript = @"
<script language=javascript>
//debugger;
var headertablerow = " + this.id + @"_headers.rows[0];
var originaltablerow = " + this.id + @".rows[0];
headertablerow.height = originaltablerow.offsetheight;
for (var i = 0; i < headertablerow.cells.length; i++) {
headertablerow.cells[i].width = originaltablerow.cells[i].offsetwidth;
}
" +
//also needs to adjust the width of the "<div>" at client side in addition to servier side,
//since the tables actual width can go beyond the width specified at server side under edit mode.
//the server side width manipulation is mainly for design-time appearance.
this.id + "_div" + @".style.width = " + this.id + @".offsetwidth + 20 + px;
" +
//the following script is for flow-layout. we cannot get the position of the control
//on server side if the the page is with flow-layout.
this.id + "_headers" + @".style.left = " + this.id + @"_div.offsetleft;
" +
this.id + "_headers" + @".style.top = " + this.id + @"_div.offsettop;
" +
this.id + "_headers" + @".style.position = absolute;
</script>";
page.registerstartupscript("dummykey" + this.id, adjustwidthscript);
//output.write(adjustwidthscript);
}
height = tempheight;
style["table-layout"] = temptablestyle;
}
}
}
protected override void oninit(eventargs e)
{
if (0 == width.value) width = new unit("400px");
if (0 == height.value) height = new unit("200px");
//transparent header is not allowed.
if (headerstyle.backcolor.isempty)
{
headerstyle.backcolor = color.white;
}
//transparent pager is not allowed.
if (pagerstyle.backcolor.isempty)
{
pagerstyle.backcolor = color.white;
}
base.oninit (e);
}
[browsable(false)]
public override bool showheader
{
get
{
return true;
}
set
{
if (false == value)
throw new invalidoperationexception("use the original datagrid to set showheaders to false.");
}
}
}
}
