Oliver
Oliver

Reputation: 11607

Find child elements of a control based on its attributes?

I want to make a navigation bar for my website. This bear will have various links to pages, and the link to the page the user is currently on should be highlighted.

At the moment I have html like this:

<div id="navbar" runat="server">
    <a href="/" runat="server" id="lnkHome">Home</a> |
    <a href="~/info.aspx" runat="server" id="lnkInfo">Info</a> |
    <a href="~/contacts.aspx" runat="server" id="lnkContacts">Contacts</a> |
    <a href="~/settings.aspx" runat="server" id="lnkSettings">Settings</a>
</div>

And code in my PageLoad event like this:

//Show the currently selected page
String filename = System.IO.Path.GetFileNameWithoutExtension(Request.Path).ToLower();
if (filename == "default")
    lnkHome.Attributes.Add("class", "selected");
else if (filename == "info")
    lnkInfo.Attributes.Add("class", "selected");
else if (filename == "contacts")
    lnkContacts.Attributes.Add("class", "selected");
else if (filename == "settings")
    lnkSettings.Attributes.Add("class","selected");

This is difficult to maintain. If I want to add a link, I have to give it an id, and add info for it to the if statement. I want a more flexible system, where I can dynamically add links to the navigation bar, and have them appear highlighted when the user is on the right page.

How do I do this? Is it possible to search navbar for child elements based on their href property? It would be best if these elements did not have to have the runat="server" attribute, so they can be treated just as regular HTML. Or is there a different implementation I should consider?

Upvotes: 4

Views: 1624

Answers (1)

Halcyon
Halcyon

Reputation: 14971

I've run into many situations where I need to find a descendant or ancestor. In response to this, I've written some extension methods which help me out a lot. I would suggest using these with the following code:

Required using statements:

using System.Collections.Generic;
using System.Web.UI;

Extension methods:

/// <summary>
/// Finds a single, strongly-typed descendant control by its ID.
/// </summary>
/// <typeparam name="T">The type of the descendant control to find.</typeparam>
/// <param name="control">The root control to start the search in.</param>
/// <param name="id">The ID of the control to find.</param>
/// <returns>Returns a control which matches the ID and type passed in.</returns>
public static T FindDescendantById<T>(this Control control, string id) where T : Control
{
    return FindDescendantByIdRecursive<T>(control, id);
}

/// <summary>
/// Recursive helper method which finds a single, strongly-typed descendant control by its ID.
/// </summary>
/// <typeparam name="T">The type of the descendant control to find.</typeparam>
/// <param name="root">The root control to start the search in.</param>
/// <param name="id">The ID of the control to find.</param>
/// <returns>Returns a control which matches the ID and type passed in.</returns>
private static T FindDescendantByIdRecursive<T>(this Control root, string id) where T : Control
{
    if (root is T && root.ID.ToLower() == id.ToLower())
    {
        return (T)root;
    }
    else
    {
        foreach (Control child in root.Controls)
        {
            T target = FindDescendantByIdRecursive<T>(child, id);
            if (target != null)
            {
                return target;
            }
        }

        return null;
    }
}

Your C# code-behind:

var fileName = Path.GetFileNameWithoutExtension(Request.Path);
var controlId = "lnk" + fileName;
var anchorTag = navbar.FindDescendantById<HtmlAnchor>(controlId);
anchorTag.Attributes.Add("class", "selected");

Upvotes: 1

Related Questions