in

ASP.NET Weblogs

-[Danny Chen]- Blog of an ASP.NET QA tester

Tips and info about Site Navigation, ImageMap, Menu and other cool ASP.NET v2.0 features.

Customizing Individual Menu Items

One question I see very frequently is how to customize the styles on an individual menu item. Declaratively, this isn't possible, although it is on the list of features for a future release. However, that doesn't mean it can't be done at all. It does take a little more work.  So, here are a couple variations on a theme to help customize various items. The ideas here can be extended on easily and I expect we might see some pretty interesting Menu's out there in the future.

Here's the most straight forward brute force way of setting an individual item style:

 <script runat="server">
Shared colorWheel As System.Collections.Generic.List(Of System.Drawing.Color)
Shared currentColor As System.Collections.Generic.List(Of System.Drawing.Color).Enumerator
Public Function GetBackColor() As System.Drawing.Color
   If colorWheel Is Nothing Then
      populatecolors()
   End If
   currentColor.MoveNext()
   Return currentColor.Current
End Function
Private Sub populatecolors()
   colorWheel =
New System.Collections.Generic.List(Of System.Drawing.Color)
   colorWheel.Add(Drawing.Color.Purple)
   colorWheel.Add(Drawing.Color.Green)
   colorWheel.Add(Drawing.Color.Brown)
   colorWheel.Add(Drawing.Color.Chartreuse)
   currentColor = colorWheel.GetEnumerator()
End Sub
</
script>

<asp:Menu ID="Menu1" runat="server" Orientation="Horizontal">

   <Items>

      <asp:MenuItem Text="one" Value="one"></asp:MenuItem>

      <asp:MenuItem Text="two" Value="two"></asp:MenuItem>

      <asp:MenuItem Text="three" Value="three"></asp:MenuItem>

      <asp:MenuItem Text="four" Value="four"></asp:MenuItem>

   </Items>

   <StaticItemTemplate>

      <asp:Label runat="server" ID="Label1" Text='<%# Eval("Text") %>'

         BackColor='<%# GetBackColor() %>' />

   </StaticItemTemplate>

</asp:Menu>

 

Now, this isn't the most ideal code for a number of reasons, but it gets the job done. So lets make 2 improvements. First, lets use the Style property so that we're not limited to a single style. Second, lets get away from relying on matching the order in our style list and the order of declared items.

<script runat="server">
Shared
styles As System.Collections.Generic.Dictionary(Of String, String)
Public Function GetStyle(ByVal value As String) As String
   If styles Is Nothing Then
      populateStyles()
   End If
   Return styles(value)
End Function
Private Sub populateStyles()
   styles =
New System.Collections.Generic.Dictionary(Of String, String)
   styles(
"one") = "background-color:Blue; color:White;"
   styles("two") = "background-color:Black; color:Yellow;"
   styles("three") = "background-color:Purple; color:Black;"
   styles("four") = "background-color:Green; color:Red;"
End Sub
</script>

<asp:Menu ID="Menu1" runat="server" Orientation="Horizontal">

   <Items>

      <asp:MenuItem Text="one" Value="one"></asp:MenuItem>

      <asp:MenuItem Text="two" Value="two">

         <asp:MenuItem Text="three" Value="three"></asp:MenuItem>

         <asp:MenuItem Text="four" Value="four"></asp:MenuItem>

      </asp:MenuItem>

   </Items>

   <StaticItemTemplate>

      <asp:Label runat="server" ID="Label1" Text='<%# Eval("Text") %>'

         Style='<%# GetStyle( Eval("Value") ) %>' />

   </StaticItemTemplate>

   <DynamicItemTemplate>

      <asp:Label runat="server" ID="Label1" Text='<%# Eval("Text") %>'

         Style='<%# GetStyle( Eval("Value") ) %>' />

   </DynamicItemTemplate>

</asp:Menu>

 

With this improvement, we have a lot control. Dynamic items also aren't a problem (although technically they weren't with the first method). With a bit of a variation, we can also make this work with a databound Menu like this:

 <script runat="server">
Shared styles As New System.Collections.Generic.Dictionary(Of String, String)
Protected Sub Menu1_MenuItemDataBound(ByVal sender As Object, _
                                      
ByVal e As System.Web.UI.WebControls.MenuEventArgs)
   styles(e.Item.ValuePath) =
CType(e.Item.DataItem, SiteMapNode)("style")
End Sub </script>
<asp:Menu ID="Menu1" runat="server" DataSourceID="SiteMapDataSource1"
   OnMenuItemDataBound="Menu1_MenuItemDataBound">
   <StaticItemTemplate>
      <asp:Label runat="server" ID="Label1" Text='<%# Eval("Text") %>'
         
Style='<%# styles( Eval("ValuePath") ) %>' />
   </StaticItemTemplate>
   <DynamicItemTemplate>
      <asp:Label runat="server" ID="Label1" Text='<%# Eval("Text") %>'
         
Style='<%# styles( Eval("ValuePath") ) %>' />
   </DynamicItemTemplate>
</asp:Menu>



[web.sitemap]
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
   <
siteMapNode url="root.aspx" title="root" style="">
      <
siteMapNode url="one.aspx" title="one" style="background-color:Blue; color:White;" />
      <
siteMapNode url="two.aspx" title="two" style="background-color:Black; color:Yellow;">
         <
siteMapNode url="three.aspx" title="three" style="background-color:Purple; color:Black;" />
         <
siteMapNode url="four.aspx" title="four" style="background-color:Green; color:Red;" />
      </
siteMapNode>
   </
siteMapNode>
</
siteMap>

Comments

 

Stefaan Rillaert said:

Hello Danny,

thanks for blogging, your posts help me to make better use of these controls you are working on as a QA tester. Still have a question though, a little bit connected with this post : Is there a way that we can customize the styles on individual tree nodes in the TreeView ? I ask this question because the TreeView doesn't support node templates (like the Menu control inheritance). Therefore I was looking into a solution that tries to add this to the TreeView by inheritance.

Since beta 2 the TreeView has become more extensible by adding the overridable function CreateNode() As TreeNode. This way someone can inherit from TreeView and change its behavior so that it uses a subclass that inherits from TreeNode instead of just instantiating default TreeNode(s).

However, when inheriting from TreeNode the only points where someone can change its appearance seems to be by overriding the RenderPreText and RenderPostText methods. This is great for adding stuff before or after the text of the tree node like checkboxes, images, etc. but it doesn't seem to help very much when one wants to change the style of the text of the tree node itself.

Even when one does something like :

Protected Overrides Sub RenderPreText(ByVal writer As System.Web.UI.HtmlTextWriter)
MyBase.RenderPreText(writer)
writer.AddStyleAttribute(HtmlTextWriterStyle.Color, "Grey")
End Sub

This results into an attribute 'style' with value "color:Grey;" being added to the hyperlink tag surrounding the text. But in IE this css-style is overridden by the css-styles defined in the 'class' attribute on the same (hyperlink) tag (in my test case class="TreeView1_0 TreeView1_1"). The css-styles in the 'class' attribute are defined by the server styles on the TreeView itself (NodeStyle, SelectedNodeStyle, …). So it are still these TreeView styles that decide the style of the node and not the css-styles added during RenderPreText.

Any ideas about how to inherit from the TreeView and getting styles on individual nodes working ?

Thanks in advance ! And enjoy Christmas and new year.

Stefaan
December 21, 2005 8:20 AM
 

Stefaan Rillaert said:

To the readers of this blogentry :

I posted this question also on the asp.net forums (was quite impatient because the answer to this question meant the difference between creating a whole new custom treeview for our project or being able to inherit from the standard treeview) and Danny Chen was so kind enough to reply me over there (http://forums.asp.net/1152605/ShowPost.aspx). It seems that one can indeed adapt the style of individual TreeNodes. This will save a lot of time and effort. :)
December 29, 2005 10:09 AM
 

Danny Chen said:

Thank you Stefaan for posting the follow up. The code he posted above actually does work. Call it a prototype; now that the model is set, it's a straight forward task to simply decide on the styles you want to provide, how to store them in your custom nodes, and add them to the rendering.
--
Danny
December 29, 2005 11:53 AM
 

debasree said:

can you please provide a solution in C#?

November 7, 2008 4:51 AM

Leave a Comment

(required)  
(optional)
(required)  
Add