Gorgo.Live.ToString()

Mariusz, Gorzoch tech Blog

Archive for January 2010

Two tricks around ASP.NET menu stuff

leave a comment »

Today I would like to describe two tricks around menu stuff in asp.net which I was looking for a while and on the end manage to sort them out. So… let go:

1. How to extend breadcrumb menu to not only use site map xml file but also include some dynamic pages name.

Here is an example: We have our site described with site map xml file like the one shown below:

<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
    <siteMapNode url="~/default.aspx" title="Home"  description="Strona główna aplikacji">
      <siteMapNode url="~/Faktura/default.aspx" title="Faktury"  description="">
        <siteMapNode url="~/Faktura/Faktura.aspx?ID=0" title="Nowa faktura" description="Pozwala na wprowadzenie nowej faktury do systemu"></siteMapNode>
        <siteMapNode url="~/Faktura/Faktura.aspx" visible="false" title="Faktura" description="Pokazuje szczegóły wybranej faktury"></siteMapNode>
        <siteMapNode url="~/Faktura/ListaFaktur.aspx" title="Lista faktur" description="Prezentuje liste wszystkich faktur wprowadzonych do "></siteMapNode>
        <siteMapNode url="~/Faktura/Wyszukaj/default.aspx" title="Wyszukaj" description="Pozwala na wyszukiwanie faktur na bazie zadanych parametrów">
          <siteMapNode url="~/Faktura/Wyszukaj/MojeFaktury.aspx" title="Moje faktury" description="Prezentuje liste faktur utworzonych przez aktualnego użytkownika"></siteMapNode>
          <siteMapNode url="~/Faktura/Wyszukaj/AdvSearch.aspx" title="Wyszukiwanie zaawansowane" description=""></siteMapNode>
        </siteMapNode>
      </siteMapNode>
</siteMap>

and now … the file “/Faktura/Faktura.aspx” is responsible for presenting particular “Faktura” object based on the ID passed as a parameter. If we will go normal way and just use “SiteMapPath” control to build breadcrumb menu, then for all “Faktura” object we will get the same entry “Faktura”. To overcome this what you need is to get rid of default “SiteMapPath” and build our own one. To do so, just add “ascx” control to your project, switch to code behind and replace your code with this one:

public string CurrentItemName
{
    get
    {
        return Convert.ToString(ViewState[this.ClientID + "_curItemName"]);
    }
    set
    {
        ViewState[this.ClientID + "_curItemName"] = value;
    }
}

protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);

    Stack<Control> MenuOptions = new Stack<Control>();

    SiteMapNode current = SiteMap.CurrentNode;

    if (current != null)
    {
        if (!String.IsNullOrEmpty(CurrentItemName))
            MenuOptions.Push(new LiteralControl(CurrentItemName));

        do
        {
            if (MenuOptions.Count > 0)
                MenuOptions.Push(new LiteralControl(" > "));

            HtmlAnchor anh = new HtmlAnchor();
            anh.InnerHtml = current.Title;
            anh.HRef = current.Url;
            MenuOptions.Push(anh);

            current = current.ParentNode;
        } while (current != null);

        while (MenuOptions.Count > 0)
            this.Controls.Add(MenuOptions.Pop());
    }
}

As you can see, this control is using “SiteMap” to retrieve list of nodes and extend them with additional “CurrentItemName” property. After placing this control on you master page you need to let the “layout pages” to set-up this value according to the item their presenting and DONE! Each time someone visit your “dynamic content page” he will get on the end of breadcrumb menu your object name.

2. How to hide some menu options from ASP.NET “Menu” control when he is using “SiteMapDataSource” object (created based on SiteMap, which is using XML site map file).

If you think that it is easy and logic, then go and try … to find quite soon, that you were wrong. Anyway after going thru few blogs I combined this in one peace of code and here it is :

protected override void OnInit(EventArgs e)
{
    base.OnInit(e);
    //we are hooking to item databound event so we can remove unnecesary nodes
    Menu1.MenuItemDataBound += new MenuEventHandler(Menu1_MenuItemDataBound);
}

void Menu1_MenuItemDataBound(object sender, MenuEventArgs e)
{
    SiteMapNode mapNode = e.Item.DataItem as SiteMapNode;
    if (mapNode!=null)
        if (mapNode["visible"] != null && mapNode["visible"] == "false")
        {
            RemoveMenuItem(Menu1.Items,e.Item);                   
        }
}

private bool RemoveMenuItem(MenuItemCollection MenuItems,MenuItem itemToRemove)
{
    for (int i = 0; i < MenuItems.Count; i++)
        if (MenuItems[i] == itemToRemove)
        {
            MenuItems.Remove(itemToRemove);
            return true;
        }
        else
            if (RemoveMenuItem(MenuItems[i].ChildItems, itemToRemove))
                return true;
    return false;
}

So i nut shell I’m hooking to the “MenuItemDataBound” event and looking for attribute “visible” of my site node (of course this “visible” attribute need to be added to your xml site map file <- you can see how I did in on the example of this file shown on the begging of this post). As soon I found this attribute and confirm that his value is “false” then I’m looking thru all items inside Menu to find my current item and removing it from the nodes tree. This works like a charm… and “visible=’false’” nodes do not pop-up on the menu, while they still available for other “navigation” controls.

happy coddling from Igorus and his fatherus.

Advertisements

Written by Mariusz Gorzoch

26 January 2010 at 21:29

Posted in Bez kategorii

Extending onClick HTML element object thru Javascript without use of attachEvent method

leave a comment »

Today I came across task where I needed to extend or maybe I should say replace already defined “onClick” event with new handler performing the same think with was doing previous with the extension of calling special custom function.

So we start with element definition like that:

<a href=”http://wp.pl” onClick=”Alert(‘Orginal handler’);return false”/>

now, as I can not touch the source which generated this line I needed to find a way to inject my function to onClick event keeping somehow current handler in place. Please notice here “return false” on the end of OnClick event which causing that if you go for “attachEvent” method, then your method still will not be called.

On the end I manager to do so, by the script (it is performing this operation for all “A” elements on the page):

<html>
<head>
<script language="Javascript">
function CorrectOnClick()
{
    var hrefs = document.getElementsByTagName("a");
    for(var i=0;i<hrefs.length;i++)
    {
        var strFun  = ""+hrefs[i].onclick;
        strFun = strFun.substring(strFun.indexOf("{")-1);
        hrefs[i].onclick = function ExtendedOnClik() {
                                                    alert("hi");
                                                    eval(strFun);
                                                }

    }
}
</script>
</head>
<body onLoad="CorrectOnClick()">
<a href="http://wp.pl" onClick="alert(this.href);return false;">wp.pl</a>
<a href="http://onet.pl" onClick="alert(this.href);return false;">onet.pl</a>
<a href="http://tvn24.pl" onClick="alert(this.href);return false;">tvn24.pl</a>
<a href="http://wykop.pl" onClick="alert(this.href);return false;">wykop.pl</a>
</body>

The trick is to get onClick definition and switch it with new one, which will do additional stuff and on the end call original handler.

ps. If you get interested what I needed this for, then I needed this to implement Omniture file download tracking on SharePoint site. “A” elements get generated by SharePoint core function and there is no way to change how they are doing this (and even if I could I don’t think I would go for that). On the other hand “Omniture” requires peace of code to track file downloads, so I needed to make those two work together somehow… and I think I did it.

Written by Mariusz Gorzoch

7 January 2010 at 12:03

Posted in Bez kategorii