导航

Fixed Header Scrollable DataGrid control

Posted on 2004-08-28 00:51  Felix Wang (Intl Vendor)  阅读(2506)  评论(4)    收藏  举报

客户要一个有滚动条的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.
            
//Don't 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 pager's <tr>
                        int start = temp.ToLower().IndexOf(@"<tr ");
                        
//End of pager's </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 row's height.
                            + @"
                            headerTableRow.height = originalTableRow.offsetHeight;
                            
" +
                            //Adjust pager row's 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 Table's 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>";
                        output.Write(adjustWidthScript);
                    }
 
                    
else 
                    
{
                        
//Replace the table's 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.
                        
//Can't 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 Table's 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>";
                        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) 
            
{
                HeaderStyle.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.");
            }

        }

    }

}