Master page: UpdatePanel and saving TreeView's state

Last post 01-28-2008 12:31 PM by bishpop999. 4 replies.

Sort Posts:

  • Master page: UpdatePanel and saving TreeView's state

    10-11-2006, 2:04 PM
    • Loading...
    • Matt M
    • Joined on 08-25-2006, 5:25 PM
    • Eastern WA, USA
    • Posts 128

    I have a master page with an updatepanel around a treeview, used for navigation.  The issue is the treeview's state is not maintained across pages.  In other words, if you're on page A and collapse all nodes, then click a node to get to page B, the treeview reverts to its default state, possibly re-opening nodes you just closed.

    I understand these are cross-page postings, so there isn't a blindingly simple solution.  I did find some information about surrounding the TreeView in an UpdatePanel that magically fixes the issue.  The article, which I cannot seem to find now, was using the January CTP of ATLAS, so it was a bit dated.  There are other solutions, such as one detailed here: http://forums.asp.net/thread/1387445.aspx .

    That seems like a bit of work to just keep the state.  Is there a better way?

     

    My sample code follows.

    Master page:

     

    1    <%@ Master Language="VB" CodeFile="Site.master.vb" Inherits="Site" %>
    2    
    3    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    4 5 <html xmlns="http://www.w3.org/1999/xhtml" >
    6 <head runat="server">
    7 <title>Test Page</title>
    8 <link rel="stylesheet" type="text/css" href="stylesheet.css" />
    9 </head>
    10 <body>
    11 <atlas:ScriptManager runat="server" ID="SM1" EnablePartialRendering="true" />
    12 <div id="wrapper" >
    13 <form id="Form1" runat="server" style="margin-top: .2em;">
    14 <div id="content">
    15 <asp:ContentPlaceHolder ID="MainContent" runat="server">
    16 </asp:ContentPlaceHolder>
    17 </div>
    18 <div id="navigation">
    19 <atlas:UpdatePanel runat="server" ID="treeviewUP">
    20 <Triggers>
    21 <atlas:ControlEventTrigger ControlID="NavigationTree" EventName="TreeNodeCollapsed" />
    22 <atlas:ControlEventTrigger ControlID="NavigationTree" EventName="TreeNodeExpanded" />
    23 </Triggers>
    24 <ContentTemplate>
    25 <asp:TreeView ID="NavigationTree" runat="server" ImageSet="simple" ShowLines="true"
    26 PopulateNodesFromClient="false">
    27 <Nodes>
    28 <asp:TreeNode Text="Node 1" Value="Forms">
    29 <asp:TreeNode Text="Node 1.1" />
    30 <asp:TreeNode Text="Node 1.2" />
    31 <asp:TreeNode Text="Node 1.3" />
    32 </asp:TreeNode>
    33 <asp:TreeNode Text="Node 2" Value="CMS_Root" />
    34 <asp:TreeNode Text="Node 3" Value="83"/>
    35 </Nodes>
    36 </asp:TreeView>
    37 </ContentTemplate>
    38 </atlas:UpdatePanel>
    39 </div>
    40 </form>
    41 </div>
    42 </body>
    43 </html>
    44

      Master page code-behind (only one function):

    1     Protected Sub NavigationTree_SelectedNodeChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles NavigationTree.SelectedNodeChanged
    2            Select Case NavigationTree.SelectedNode.Value
    3                Case "Forms"
    4                    Response.Redirect("serror.aspx")
    5                Case "CMS_Root"
    6                    Response.Redirect("intro.aspx")
    7                Case Else
    8                    Response.Redirect("http://www.google.com")
    9            End Select
    10       End Sub
    
      

    A content page (serror.aspx):

     

    1    <%@ Page Language="VB" MasterPageFile="~/Site.master" AutoEventWireup="false" CodeFile="serror.aspx.vb" Inherits="serror" title="Untitled Page" %>
    2    <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server">
    3 We're on serror.aspx.<br />
    4 <b>Placeholder junk.</b>
    5 </asp:Content>

      Another content page (intro.aspx):

     

    1    <%@ Page Language="VB" MasterPageFile="~/Site.master" AutoEventWireup="false" CodeFile="intro.aspx.vb" Inherits="intro" title="Untitled Page" %>
    2    <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server">
    3 We're on intro.aspx.<br />
    4 <b>CONTENT!</b>
    5 </asp:Content>

      

    Any ideas or suggestions are appreciated.  Thanks! 

  • Re: Master page: UpdatePanel and saving TreeView's state

    10-13-2006, 4:45 PM
    • Loading...
    • Matt M
    • Joined on 08-25-2006, 5:25 PM
    • Eastern WA, USA
    • Posts 128

    For what it's worth, I fixed the problem.  My page isn't quite complex enough to require a generalized, recursive solution, so I did the following:

     In my node population function, where the nodes are populated from a database query, I checked my Session variables to see if the node I was creating had to be expanded or collapsed.  I then set the node's Expanded property as required.

    In my nodeCollapsed handler, I marked the Session variable related to the node with a "collapsed" flag.  Same thing with nodeExpanded, but marked it was "expanded."  I only had two nodes to do this with, so storing a couple of booleans in Session probably won't do much harm.
     

    Pseudo-code for the node population function:

    dim newnode as treenode

    if Session("node1_expanded") then

    newnode.expanded = true

    else

    newnode.expanded = false

    end if

     

    I hope this helps someone out!
     

  • Re: Master page: UpdatePanel and saving TreeView's state

    11-09-2006, 3:27 PM
    Answer
    • Loading...
    • bryanowen
    • Joined on 05-01-2003, 11:43 AM
    • Posts 26

    Matt,

    I write a general routine that does the state caching, but it was for leaving a page and coming back to it.  (i.e., select a node, then click "Edit" -- which shoots you to an "Edit" page that's not in a hidden panel on the same page... after you're done editing, you get Response.Redirect-ed back to the TreeView page and the expand/collapse/selected-node state is all gone... that's the issue this addresses.)

    It's implemented in two static methods that I popped into a "TreeViewHelper" static class.  Whenever you need to leave the page, call the "StoreTreeViewStateToSession" method.  In your method to bind data to your TreeView, call RestoreTreeViewStateFromSession.  If there's no stored state, it just returns silently.  If there is stored state, it'll restore it (which nodes are expanded and collapsed, and which node is selected).

    They use Session state, which seems appropriate since it's obviously by-user, and cross-page.  But the Session variable key has the page name embedded in it, which would cause it *not* to work in your solution (since it's a different page trying to restore the state).  A minor tweak to this routine would remedy that issue, however.  Store as the variable name suffix a "key" common to your PageA, PageB, etc., instead of Request.ServerVariables["path_info"].

    Here's the code.  I've just recently adapted it from the IEWebControls TreeView add-on (circa .NET 1.0) and updated it to work with the ASP.NET 2.0 TreeView control.  It seems to work like a charm:

          public static void StoreTreeViewStateToSession(TreeView tvIn)
          {
             // Takes the TreeView's state and saves it in a Session variable
             // Call this method before leaving the page if we expect to be back
             string strVarName;
             string strList = "";
    
             strVarName = "tv_" + HttpContext.Current.Request.ServerVariables["path_info"];
             if (HttpContext.Current.Session[strVarName] + "" != "")
             {
                HttpContext.Current.Session.Remove(strVarName);
             }
             
             StoreTreeViewStateToSession_Recurse(tvIn.Nodes[0], ref strList);
    
             strList = tvIn.SelectedNode.ValuePath + strList;
    
             HttpContext.Current.Session.Add(strVarName, strList);
          }
    
    
          private static void StoreTreeViewStateToSession_Recurse(TreeNode tnIn, ref string strList)
          {
             if (tnIn.Expanded == true)
             {
                strList = "," + tnIn.ValuePath + strList;
             }
             foreach (TreeNode tnCurrent in tnIn.ChildNodes)
             {
                StoreTreeViewStateToSession_Recurse(tnCurrent, ref strList);
             }
          }
    
    
          public static void RestoreTreeViewStateFromSession(TreeView tvIn)
          {
             // Takes the Session-stored TreeView state and restores it
             // to the passed-in TreeView control.
             // Call this method on entry to the page.  Nothing will
             // happen if the variable doesn't exist.
             string strVarName;
    
             // See if stored data exists for this treeview
             strVarName = "tv_" + HttpContext.Current.Request.ServerVariables["path_info"];
             if (HttpContext.Current.Session[strVarName] + "" != "")
             {
                string strSelectedNodeIndex = "";
                foreach (string strCurrent in HttpContext.Current.Session[strVarName].ToString().Split(','))
                {
                   if (strSelectedNodeIndex == "") // First element in list is selected node
                   {
                      strSelectedNodeIndex = strCurrent;
                   }
                   else
                   {
                      try
                      {
                         tvIn.FindNode(strCurrent).Expanded = true;
                      }
                      catch
                      {
                         //eat exception
                      }
                   }
                }
    
                try
                {
                   // Verify that node exists before setting SelectedNodeIndex
                   TreeNode tnTest = tvIn.FindNode(strSelectedNodeIndex);
    // Select the node (will only happen if it exists) tvIn.FindNode(strSelectedNodeIndex).Select();
    // Ensure the selected node's parent is expanded ((TreeNode)tvIn.FindNode(tvIn.SelectedNode.ValuePath).Parent).Expanded = true;
                }
                catch
                {
                   // eat exception
                }
    
                HttpContext.Current.Session.Remove(strVarName);
             }
          }
    

     

    ... Hope that helps!!!  See my other post on client-side node selection without forcing PostBack (http://forums.asp.net/thread/1452479.aspx) if you're interested in another of my TreeViewHelper tools, which I'm pretty pleased with lately.  Microsoft so needs to hire me.  ;-)

    -Bryan

     

  • Re: Master page: UpdatePanel and saving TreeView's state

    11-09-2006, 5:02 PM
    • Loading...
    • Matt M
    • Joined on 08-25-2006, 5:25 PM
    • Eastern WA, USA
    • Posts 128
    Bryan, that appears to be just the solution I was looking for and didn't want to write.  :)  I'll keep it in mind if I use a more complex TreeView.  Thanks!
  • Re: Master page: UpdatePanel and saving TreeView's state

    01-28-2008, 12:31 PM
    • Loading...
    • bishpop999
    • Joined on 07-13-2007, 1:52 PM
    • Posts 2
    Hi Bryan, The code works a treat, thank you!
    You created a static logged on user variable?
Page 1 of 1 (5 items)
Microsoft Communities
Page view counter