海洋工作室——网站建设专家:How To: Create an ASP.NET AJAX Style Folder Explorer

Here is the original link,
http://mattberseth.com/blog/2007/07/hwo_to_create_an_aspnet_ajax_s.html
I recently created a folder explorer using a combination of the ASP.NET TreeView and GridView controls as well as the new ASP.NET AJAX UpdatePanel.  You can view a live demo of the folder explorer here. The code for this sample is at the bottom of the page.

[Update 7/24/2007]: Here is another post regarding this, except with a few more enhancements

[Update 7/17/2007]: Per Ruckus' comments, I would like to make the following clarifications:

  • The sample provides read-only access to the folders and files, making it more of a 'Folder Browser' instead of an Explorer
  • A full-postback is done when a folder is clicked in the right hand pane.  If you have a suggestion to a nice way to have the Folder image buttons cause async-postbacks instead of complete post-backs, let me know.  I thought about possibly handling the RowDataBound event and explicitly calling RegisterAsyncPostBackControl ...
  • Offically the TreeView control is not supposed to work inside an UpdatePanel.  I didn't run into any problem, but this is why I classified this post under 'prototype'.  It needs a little more work/investigation. 

The design of the page is pretty simple.  The left panel is a regular ASP.NET TreeView that displays the folders I am allowing the users to browse.  The right panel contains the selected folders contents - both files and folders.  Both of these controls are contained within an UpdatePanel so traversing the folder structure does not cause the complete page to refresh.

Here is the markup for the TreeView (left panel):   

<asp:TreeView ID="tvFolders" runat="server" OnSelectedNodeChanged="TvFolders_SelectedNodeChanged">
    <NodeStyle ImageUrl="Img/folder.gif" HorizontalPadding="3px" Font-Underline="false" ForeColor="black" />
    <SelectedNodeStyle Font-Underline="true" Font-Bold="true" />
</asp:TreeView> 

And here is the GridView (right panel).  The only interesting part about the GridView is that because the GridView does not display Headers when the DataSource is empty, I am using the work a round outlined here.   

<mb:GridView 
    ID="gvFolderItems" runat="server" 
    EmptyDataText="This folder is empty." 
    GridLines="None" Width="100%" BorderStyle="None" 
    OnRowDataBound="GvFolderItems_RowDataBound" 
    OnRowCommand="GvFolderItems_RowCommand" 
    AutoGenerateColumns="false" ShowHeaderWhenEmpty="true">
    <HeaderStyle HorizontalAlign="Left" BackColor="BurlyWood" />
    <Columns>
        <asp:TemplateField HeaderText="Name">
            <ItemTemplate>
                <%--The image is set when row is databound--%>
                <asp:ImageButton 
                    ID="btnItemIcon" runat="server" 
                    ImageAlign="AbsMiddle" 
                    CommandArgument='<%# Eval("Name") %>' 
                    CommandName="ItemClick" />
                <asp:LinkButton 
                    ID="btnItemName" runat="server" 
                    Text='<%# Eval("Name") %>' 
                    CommandArgument='<%# Eval("Name") %>' 
                    CommandName="ItemClick" 
                    Font-Underline="false" 
                    ForeColor="black" /> 
            </ItemTemplate>     
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Size">
            <ItemTemplate>
                <%--The image is set when row is databound--%>
                <asp:Label ID="lblSize" runat="server" />                                                
            </ItemTemplate>
        </asp:TemplateField>
   </Columns>
</mb:GridView>

Finally, because I need use the Response object to write the PDF contents to the output stream, I have to notify the UpdatePanel that any postback that originates from the GridView needs to be a full postback (instead of an partial one - you can not write to the Response object during a partial-postback.  If you do it will generate an error).  This is done by adding the PostBackTrigger item to the UpdatePanels Triggers collection and point it to our GridView.

<Triggers>
    <asp:AsyncPostBackTrigger ControlID="tvFolders" EventName="SelectedNodeChanged" />
    <asp:PostBackTrigger ControlID="gvFolderItems" />
</Triggers>  

The path to the physical folder structure I am allowing the user to browse is located at the 'rootfolder' appsetting key.  Additionally, these folders only contain PDF files so when the directory item is a file I am always displaying the PDF icon.  This logic is contained in the RowDataBound event handler and you will need to update it for each type of file your application supports.  I am binding directly to the FileSystemInfo object and displaying the Name property value for both Folders and Files and the Size property for just the Files.  You should be able to include other attributes in a similar manner.

That's It.  Enjoy! 

* Disclaimer: Offically the TreeView control is not supposed to work inside an UpdatePanel.  I didn't run into any problem, but this is why I classified this posting under 'prototype'.  It needs a little more work/investigation. 

<%@ Page Language="C#" %>
<%@ Register Assembly="MattBerseth.WebControls" Namespace="MattBerseth.WebControls" TagPrefix="mb" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
    <script runat="server">
    /// <summary>
    ///
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!this.IsPostBack)
        {
            string rootFolder = this.Server.MapPath(ConfigurationManager.AppSettings["rootfolder"]);

            // load the treeview based on the folder strucutre
            TreeNode rootNode = new TreeNode("Root", rootFolder);
            rootNode.Expanded = true;
            rootNode.Select();
            // add the root node
            this.tvFolders.Nodes.Add(rootNode);

            // bind sub directories to the treeview
            BindDirs(rootFolder, rootNode);

            // bind the gridview to the datasource using the root node
            BindDirsContents(rootNode, this.gvFolderItems);
        }
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    protected void GvFolderItems_RowDataBound(object sender, GridViewRowEventArgs args)
    {
        if (args.Row.RowType == DataControlRowType.DataRow)
        {
            System.IO.FileSystemInfo item = (System.IO.FileSystemInfo)args.Row.DataItem;
            ImageButton imageButton = (ImageButton)args.Row.FindControl("btnItemIcon");

            if (item is System.IO.DirectoryInfo)
            {
                imageButton.ImageUrl = @"Img/folder.gif";
            }
            else
            {
                imageButton.ImageUrl = @"Img/pdf.gif";

                System.IO.FileInfo fileInfo = (System.IO.FileInfo)item;
                Label lblSize = (Label)args.Row.FindControl("lblSize");
                lblSize.Text = string.Format("{0:N0} KB", fileInfo.Length / 1000);
            }
        }
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    protected void GvFolderItems_RowCommand(object sender, GridViewCommandEventArgs args)
    {
        // handle either opening the item or rebinding the grid
        if (args.CommandName == "ItemClick")
        {
            string name = (string)args.CommandArgument;
            System.IO.DirectoryInfo dinfo = new System.IO.DirectoryInfo(this.tvFolders.SelectedNode.Value);

            if (System.IO.File.Exists(System.IO.Path.Combine(dinfo.FullName, name)))
            {
                System.IO.FileInfo fileInfo = new System.IO.FileInfo(System.IO.Path.Combine(dinfo.FullName, name));
                //  they clicked on a file, download it
                //  to there PC
                this.Response.Clear();
                this.Response.AddHeader("Content-Disposition", "attachment; filename=" + fileInfo.Name);
                this.Response.AddHeader("Content-Length", fileInfo.Length.ToString());  
                this.Response.ContentType = "application/octet-stream";
                this.Response.WriteFile(fileInfo.FullName);  
                this.Response.End();
            }
            else
            {
                foreach (TreeNode node in this.tvFolders.SelectedNode.ChildNodes)
                {
                    if (node.Text == name)
                    {
                        node.Selected = true;
                        node.Expanded = true;

                        // expand the parents
                        TreeNode parentNode = node.Parent;
                        while (parentNode != null)
                        {
                            parentNode.Expanded = true;
                            parentNode = parentNode.Parent;
                        }

                        // bind the gridview to the datasource
                        BindDirsContents(node, this.gvFolderItems);
                        break;
                    }
                }
            }
        }
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    protected void TvFolders_SelectedNodeChanged(object sender, EventArgs args)
    {
        BindDirsContents(this.tvFolders.SelectedNode, this.gvFolderItems);
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="node"></param>
    private static void BindDirsContents(TreeNode node, System.Web.UI.WebControls.GridView gridView)
    {
        // bind the gridview to the datasource
        gridView.DataSource = new System.IO.DirectoryInfo(node.Value).GetFileSystemInfos();
        gridView.DataBind();
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="path"></param>
    /// <param name="treeNode"></param>
    private static void BindDirs(string path, TreeNode treeNode)
    {
        if (!string.IsNullOrEmpty(path))
        {
            foreach (string directoryPath in System.IO.Directory.GetDirectories(path))
            {
                System.IO.DirectoryInfo directory = new System.IO.DirectoryInfo(directoryPath);
                TreeNode subNode = new TreeNode(directory.Name, directory.FullName);
                treeNode.ChildNodes.Add(subNode);

                // bind sub directories
                BindDirs(directoryPath, subNode);
            }
        }
    }    
    </script>
</head>
<body>
    <form id="form" runat="server">
        <asp:ScriptManager ID="scriptManager" runat="server" />
        <div>
            <p style="background-color:AliceBlue; width:700px">
            This is an example of how to combine a TreeView a GridView and an UpdatePanel to create a<br />
            nice AJAX folder browser<br />
            </p>
            <br />
            <asp:UpdatePanel ID="updPanel" runat="server">
                <Triggers>
                    <asp:AsyncPostBackTrigger ControlID="tvFolders" EventName="SelectedNodeChanged" />
                    <asp:PostBackTrigger ControlID="gvFolderItems" />
                </Triggers>                            
                <ContentTemplate>        
                    <table id="tbl" cellpadding="0px" cellspacing="0px">            
                        <tr>
                            <td style="border:solid 1px black" valign="top">
                                <div style="overflow:auto;width:300px;height:450px;">
                                    <asp:TreeView 
                                        ID="tvFolders" runat="server" 
                                        OnSelectedNodeChanged="TvFolders_SelectedNodeChanged">
                                        <NodeStyle 
                                            ImageUrl="Img/folder.gif" HorizontalPadding="3px" 
                                            Font-Underline="false" ForeColor="black" />
                                        <SelectedNodeStyle 
                                            Font-Underline="true" Font-Bold="true" />
                                    </asp:TreeView> 
                                </div>
                            </td>
                            <td style="border:solid 1px black" valign="top">
                                <div style="overflow:auto;width:400px;height:450px;">
                                    <mb:GridView 
                                        ID="gvFolderItems" runat="server" 
                                        EmptyDataText="This folder is empty." 
                                        GridLines="None" Width="100%" BorderStyle="None" 
                                        OnRowDataBound="GvFolderItems_RowDataBound" 
                                        OnRowCommand="GvFolderItems_RowCommand" 
                                        AutoGenerateColumns="false" ShowHeaderWhenEmpty="true">
                                        <HeaderStyle HorizontalAlign="Left" BackColor="BurlyWood" />
                                        <Columns>
                                            <asp:TemplateField HeaderText="Name">
                                                <ItemTemplate>
                                                    <%--The image is set when row is databound--%>
                                                    <asp:ImageButton 
                                                        ID="btnItemIcon" runat="server" 
                                                        ImageAlign="AbsMiddle" 
                                                        CommandArgument='<%# Eval("Name") %>' 
                                                        CommandName="ItemClick" />
                                                    <asp:LinkButton 
                                                        ID="btnItemName" runat="server" 
                                                        Text='<%# Eval("Name") %>' 
                                                        CommandArgument='<%# Eval("Name") %>' 
                                                        CommandName="ItemClick" 
                                                        Font-Underline="false" 
                                                        ForeColor="black" /> 
                                                </ItemTemplate>     
                                            </asp:TemplateField>
                                            <asp:TemplateField HeaderText="Size">
                                                <ItemTemplate>
                                                    <%--The image is set when row is databound--%>
                                                    <asp:Label ID="lblSize" runat="server" />                                                
                                                </ItemTemplate>
                                            </asp:TemplateField>
                                       </Columns>
                                    </mb:GridView>
                                </div>
                            </td>
                        </tr>
                    </table>
                </ContentTemplate>
            </asp:UpdatePanel>             
            <br /> 
        </div>
    </form>    
</body>
</html>
posted @ 2009-09-09 11:14  海洋——海纳百川,有容乃大.  阅读(499)  评论(0编辑  收藏  举报