How to build an infinite tree hierarchy? - asp.net

I've been working on this since yesterday and I'm completely stumped. I'm working in VB.NET but I can understand C# if you'd prefer to answer it that way.
Basically I have items from a SQL database with IDs and parent IDs and I need to put them in a tree like so:
<ul>
<li>Some item
<ul>
<li>Another item
<ul>
<li>This could go forever
<ul>
<li>Still going</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
Currently I have a nested repeater which works but it only gets me to the second level. It looks something like this:
<asp:Repeater ID="parent" runat="server">
<ItemTemplate>
<ul>
<li><span><%#Container.DataItem("Name")%></span>
<asp:Repeater ID="child" DataSource='<%#CType(Container.DataItem, DataRowView).Row.GetChildRows("relation")%>' runat="server">
<ItemTemplate>
<ul>
<li><span><%#Container.DataItem("Name")%></span></li>
</ul>
</ItemTemplate>
</asp:Repeater>
</li>
</ul>
</ItemTemplate>
</asp:Repeater>
The relation is like this:
ds.Relations.Add("relation",
ds.Tables("Items").Columns("ID"),
ds.Tables("Items").Columns("ParentID"),
False)
I understand why it won't work because it doesn't have a template to continue the tree. So I'm trying to figure out a way around that.
I've considered writing a function to just build the string in the code behind and stick it in the html with an asp tag. I wasn't sure how to go about this while pulling the data out of the database.

I found a temporary solution though it's extremely inefficient. In case anyone stumbles across my question this will at least get you by. For large amounts of data it could take up to a full minute to load.
If anyone could comment on how to make this more efficient that would be nice.
Code behind:
Private Dim dt As New DataTable
Public Function BuildTree(ByVal ID As String) As String
Dim sb As New StringBuilder
dt = YourDatabase.GetChildren(ID) '<-- You'll have to write this function
sb.AppendLine("")
If dt.Rows.Count > 0 Then
sb.AppendLine("<ul>")
If dt.Rows.Count > 1 Then
For Each row As DataRow In dt.Rows
sb.AppendLine("<li>" & row("ItemName"))
' Recursive call
sb.AppendLine(BuildTree(row("ID").ToString))
sb.AppendLine("</li>")
Next
Else
sb.AppendLine("<li>" & dt.Rows(0)("ItemName").ToString & "</li>")
End If
sb.AppendLine("</ul>")
End If
Return sb.ToString
End Function
Then in your aspx page do something like this:
<%#BuildTree(Container.DataItem("ID").ToString)%>
EDIT: Declaring the DataTable outside of the function helps the efficiency a bit.

Related

ASP content display issue

I moved my site to another hosting, but the language menu <a> tags between the content of the database does not print between the label. But inside the href parameter it draws the same query.
When I examined the output of the code:
Output code picture
Below is my code in that line:
<ul class="headerLanguage"><li><%=Session("lang")%><img alt="" title="" src="/images/icon/08.png" /><ul><%
Set a = SQL.Execute("SELECT kisa FROM diller WHERE kisa<>'"& Session("lang") &"'")
Do while not a.Eof %><li><%=a("kisa")%></li><% a.MoveNext:Loop
a.Close
Set a = Nothing %></ul></li></ul>
When I delete the query inside the href parameter, the other query runs and the content is appearing.
My application is Classic ASP, but I did not have a problem with my old site, why was this so? How can I solve?
I'm grateful for your help.
I've encountered this problem before. I'm not sure exactly why it happens (hopefully someone else can explain), but sometimes if you reference data directly from a recordset multiple times it can return an empty value. The solution that worked for me was to assign the data to a variable first and reference the variable instead. Try this:
<ul class="headerLanguage">
<li>
<a href="/?lang=<%=Session("lang")%>"><%=Session("lang")%>
<img alt="" title="" src="/images/icon/08.png" />
</a>
<ul><%
Dim a, kisa
Set a = SQL.Execute("SELECT kisa FROM diller WHERE kisa<>'"& Session("lang") &"'")
Do while not a.Eof
kisa = a("kisa") %>
<li><%=kisa%></li><%
a.MoveNext:Loop
a.Close
Set a = Nothing
%></ul>
</li>
</ul>

show/hide css menu item depending user role using asp.net

I have the css layout: One column fixed width layout, from maxdesign.com
I have two menu items defined like the following:
<div id="navigation">
<ul>
<li>Data Entry</li>
<li>Reports</li>
</ul>
</div>
Now, let's say that I have two roles: guest and operator, and I want that for example if a user with role guest is logged in, then just the Report item from the menu appear, and in case of a user operator is logged in, then both options appear.
How can I accomplish that?
EDIT:
Based on your responses, I'll go with the server side logic to deal with this:
<div id="navigation">
<ul>
<li><asp:LinkButton ID="lkbDataEntry" runat="server">Data Entry</asp:LinkButton></li>
<li><asp:LinkButton ID="lkbReports" runat="server">Reports</asp:LinkButton></li>
</ul>
</div>
Thanks!
You can put this in the Page_Load..
Dim cs As ClientScriptManager = Page.ClientScript
If Not cs.IsClientScriptBlockRegistered(Me.GetType(), "RoleVariable") Then
Dim js As New String
js = "var _role = " & role & ";"
cs.RegisterStartupScript(Me.GetType(), "RoleVariable", js, True)
End If
And from there, you will have the role in the Javascript realm, where you can manipulate the visibility of the items you want.
So...
<script type="text/javascript">
function hideStuff() {
if (_role === "operator") {
// hide/show your elements here
}
else if (_role === "guest") {
// hide/show your elements here
}
}
</script>
Keep in mind that this approach is all client-side and is therefore easy for another developer to manipulate if they really wanted to. But on the other hand, it's the simplest. Don't use this approach for high-security situations.
You could give your menu elements an ID attribute and then in your codebehind either use RegisterClientSideScriptBlock or use Response.Write to send JavaScript to the client to hide (or show) elements based on some condition.
how about something simple like?
<% if(Page.User.IsInRole("operator") || Page.User.IsInRole("guest")) { %>
<div id="navigation">
<ul>
<% if(Page.User.IsInRole("operator")) { %>
<li>Data Entry</li>
<% } %>
<li>Reports</li>
</ul>
</div>
<% } %>
I don't 100% think you can (or should) be doing logic operations with stylesheets. You may need to have some javascript and then decide based on guest or operator which style to display

FindControl results in null reference

I have anchor tags like this
<div id="menu_container">
<ul id="nav-bar">
<li>Home</li>
<li><a href="Account.aspx" runat="server" id="menu_item_account" >Account</a></li>
<li>Servers</li>
<li>Statistics</li>
<li>Tutorials</li>
<li>Contact us</li>
<div id="login_registration_container">
Sign in / Register
</div>
</ul>
</div>
I want to change the CSS class for menu_item_default this way:
WebControl wc = (WebControl)FindControl("#menu_item_default");
wc.Attributes.Add("class", "value");
error: null reference exception
How can this be done?
You shouldn't use '#' symbol in the FindControl argument:
WebControl wc = (WebControl)FindControl("menu_item_default");
Using a MasterPage and the element is in a ContentPlaceholder:
If so then you must retrieve the ContentPlaceholder first, and from it retrive the element you want.
If your page has the following Content-area for example:
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
Then you would do the following (error handling omitted):
var mainCtrl = Master.FindControl("MainContent");
var anchor = (HtmlAnchor) mainCtrl.FindControl("menu_item_default");
anchor.Attributes.Add("class", "value");
Using a MasterPage and the element is in the MasterPage:
use:
var anchor = (HtmlAnchor) Master.FindControl("menu_item_default");
Is there a particular reason you are using a regular anchor tag? Why not use an ASP.Net LinkButton? That will make it much easier to reference in the code.
If the control is on the master page, try
Master.FindControl()

change master page <a href link from content page

i have this on my master.page
<ul class="menu">
<li class="first" runat="server" id="Li2">
<a runat="server" id="A1" href="../NewEntry.aspx">Create a New Entry</a>
</li>
</ul>
when i go to content page ("NewEntry.aspx") i want the link name to be changed to "Update Entry"
<ul class="menu">
<li class="first" runat="server" id="Li2">
<a runat="server" id="A1" href="../UpdateEntry.aspx">Update Entry</a>
</li>
</ul>
any feedback?
Make the link an asp:Hyperlink. Then have the master page expose a function or property:
public void SetLink(string href, string text)
{
A1.NavigateURL = href;
A1.Text = text;
}
Call the function from the main page.
You can use a hyperlink control <asp:hyperlink> and set the url as well as the text values.
I would recommend handling this as a HyperLink control as others have mentioned. If for some reason you must handle this as a server-side HTML anchor, you can access it using the following code from your webform code-behind:
HtmlAnchor link = (HtmlAnchor)(this.Master).FindControl("A1");
link.InnerText = "Update Entry";
You can also define a content place holder where you have "Create a New Entry". Leave that as the default inside that place holder, and only in the content page set content for it to Update Entry.

Share variable across site ASP.NET

I have a class isSearching with a single boolean property in a 'functions' file in my webapp. On my search page, I have a variable oSearchHandler declared as a Public Shared variable. How can I access the contents of oSearchHandler on other pages in my webapp?
Code with Session....
'search.aspx
Public Function oSearchString(ByVal oTextBoxName As String) As String
For Each oKey As String In Request.Form.AllKeys
If oKey.Contains(oTextBoxName) Then
Session.Add("searching", True)
Session.Add("search-term", Request.Form(oKey))
Return Request.Form(oKey)
End If
Next
Return ""
End Function
'theMaster.master
<%
If Session("searching") Then
%><ul style="float: right;">
<li>
<div class="gsSearch">
<asp:TextBox ID="searchbox" runat="server"></asp:TextBox>
</div>
</li>
<li>
<div class="gsSearch">
<asp:Button ID="searchbutton" runat="server" Text="search" UseSubmitBehavior="true" PostBackUrl="search.aspx" CssClass="searchBtn" />
</div>
</li>
</ul>
<%
End If
%>
I think that the session will work just fine.
If you're talking about accessing these variables between page interactions, you need to bear in mind that the state is discarded between pages.
Instead, you need to store this data in Session State.
If it's not across page interactions, but simply accessing the data from other parts of the code, the key is that the ASP.NET page becomes a class and your public shared variable, a static property of that class.
So, you'd access it from elsewhere using PageName.oSearchHandler
[EDIT] Can you give us some more information about what oSearchHandler is and how you're intending using it? We can probably offer a more considered recommendation, then.
If you want it accessible from multiple pages you should pull it off that individual page class and put it in a more globally accessable place such as the Application collection. Given the naming of the variable, is 0SearchHandler a delegate? I'm not as familiar with VB.NET as much or the terminology.
Update: Steve Morgan mentioned using the Session collection, when "static" or "shared" was mentioned, i was thinking more globally. Depending on how your using the variable you can use the "Application" if it will be shared between users and sessions, or the "session" if it will be used by one user in one session. In VB.NET they are both easy to use:
Session("yourKey") = YourObjectYouWantToSave
Application("yourKey") = YourObjectYouWantToSave
Very simple stuff.
'search.aspx
Public Function oSearchString(ByVal oTextBoxName As String) As String
For Each oKey As String In Request.Form.AllKeys
If oKey.Contains(oTextBoxName) Then
Session("searching") = True
Session("search-term") = Request.Form(oKey)
Return Request.Form(oKey)
End If
Next
Return ""
End Function
' theMaster.master.vb
In PageLoad Method:
...
Dim bSearching as Boolean
bSearching = IIf(Session("searching") is Nothing, False, Session("searching") )
ulSearch.visible = bSearching
...
'theMaster.master
<ul style="float: right;" runat="server" id="ulSearch">
<li>
<div class="gsSearch">
<asp:TextBox ID="searchbox" runat="server"></asp:TextBox>
</div>
</li>
<li>
<div class="gsSearch">
<asp:Button ID="searchbutton" runat="server" Text="search" UseSubmitBehavior="true" PostBackUrl="search.aspx" CssClass="searchBtn" />
</div>
</li>
</ul>
Ok, that is some extra code but I think you would have less problems with it. Plus my VB is a bit rusty. Actually, If the master page is the page you will be using it on, I would put the variable as a public property on that masterpage. You can access the pages master page with this.Master (at least in C#, I think it's Me.Master in VB.NET).

Resources