1

Resolved

Embedded Header Styles

description

The ASP.NET Menu control automatically adds several styles to the embedded header style. For example:
 
.ctl00_ucMenu_MenuCBM_0 { background-color:white;visibility:hidden;display:none;position:absolute;left:0px;top:0px; }
 
This is unnecessary and probably breaks your ability to fully style your menu with external stylesheets.
 
The culprit is an internal method of the Menu control called EnsureRenderSettings. It calls this.Page.Header.StyleSheet.CreateStyleRule() several times, adding style rules to the embedded stylesheet. EnsureRenderSettings is called at the beginning of the Menu's OnPreRender method. Hey, the menu adapter exposes OnPreRender! That means we're getting closer to an answer, right? Right, but if we override OnPreRender and don't call the base implementation we'll be missing some important functionality. This uses internal methods so we cannot emulate it in the menu adapter.
 
The solution is a bit of a hack, but it's quick and direct. We override OnPreRender and after calling the base implementation as normal, we clear the embedded header styles that were just added. Add this to MenuAdapter.cs:
 
using System.Reflection;
using System.Collections;
 
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
 
// Clear the embedded header styles that ASP.NET automatically adds for the menu
ArrayList selectorStyles = (ArrayList)GetPrivateField(Page.Header.StyleSheet, "_selectorStyles");
selectorStyles.Clear();
}
 
 
/// <summary>
/// Gets the value of a non-public field of an object instance. Must have Reflection permission.
/// </summary>
/// <param name="container">The object whose field value will be returned.</param>
/// <param name="fieldName">The name of the data field to get.</param>
private object GetPrivateField(object container, string fieldName)
{
Type type = container.GetType();
FieldInfo fieldInfo = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
return fieldInfo.GetValue(container);
}
 
Notice how it accesses the private member _selectorStyles using a little reflection. Like I said, a bit of a hack, but it gets rid of those embedded header styles as promised!
 
(P.S. Lutz Roeder's .NET Reflector was instrumental in allowing me to easily track down the problem.)

comments

wrote Sep 12, 2007 at 2:22 PM

Resolved with changeset 4439.

AntonBreusov wrote Mar 3, 2008 at 3:28 PM

Please reopen this issue.

Solution to it is too hackery, it brokens some hostings with restrictive settings:

Server Error in '/' Application.
Attempted to access a field that is not accessible by the caller.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.FieldAccessException: Attempted to access a field that is not accessible by the caller.

Source Error:

Line 113: Type type = container.GetType();
Line 114: FieldInfo fieldInfo = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
Line 115: return (fieldInfo == null ? null : fieldInfo.GetValue(container));
Line 116: }
Line 117: }


Source File: e:*\App_Code\Adapters\Helpers.cs Line: 115

Stack Trace:

[FieldAccessException: Attempted to access a field that is not accessible by the caller.]
System.Reflection.RtFieldInfo.PerformVisibilityCheckOnField(IntPtr field, Object target, IntPtr declaringType, FieldAttributes attr, UInt32 invocationFlags) +0
System.Reflection.RtFieldInfo.InternalGetValue(Object obj, Boolean doVisibilityCheck, Boolean doCheckConsistency) +178
System.Reflection.RtFieldInfo.GetValue(Object obj) +8
CSSFriendly.Helpers.GetPrivateField(Object container, String fieldName) in e:*
\App_Code\Adapters\Helpers.cs:115
CSSFriendly.MenuAdapter.OnPreRender(EventArgs e) in e:*************\App_Code\Adapters\MenuAdapter.cs:374
System.Web.UI.Control.PreRenderRecursiveInternal() +2069293
System.Web.UI.Control.PreRenderRecursiveInternal() +161
System.Web.UI.Control.PreRenderRecursiveInternal() +161
System.Web.UI.Control.PreRenderRecursiveInternal() +161
System.Web.UI.Control.PreRenderRecursiveInternal() +161
System.Web.UI.Control.PreRenderRecursiveInternal() +161
System.Web.UI.Control.PreRenderRecursiveInternal() +161
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1360

Version Information: Microsoft .NET Framework Version:2.0.50727.42; ASP.NET Version:2.0.50727.210

I think, it must be done in some other way, or this change must be reverted, because catching exception each time is also not very good way to work.

AntonBreusov wrote Mar 4, 2008 at 10:24 AM

Investigated it issue today... Unfortunately, I'm not seeing a better solution to remove this styles, too much internal stuff. But is it really needed at all? This style rules will not be applied anyway to our menu, because with our adapters, "class" selectors will be different, you can easily check. So, it will be only few lines of garbage in page's text, not a very big issue... Also, why I don't like this fix: adapter removes all contents of "_selectorStyles", what if some other controls puts something here? It can be possible, if you decided not to use CSS decorators for all classes, but only for some. Or more likely, some other, non-MS custom controls put styles via Page.Header.StyleSheet.CreateStyleRule() .

AntonBreusov wrote Mar 4, 2008 at 10:36 AM

Seen same hack-fix also in TreeView, but I have not tested, if this styles gets applied to adapted tree view elemens, or not...

bdemarzo wrote Mar 6, 2008 at 1:08 PM

** Closed by bdemarzo 9/12/2007 7:22 AM

bdemarzo wrote Mar 6, 2008 at 1:08 PM

wrote Mar 6, 2008 at 1:08 PM

wrote May 6, 2008 at 2:28 PM

Fixed on changeset 11160

wrote May 6, 2008 at 2:28 PM

wrote Feb 14, 2013 at 8:44 PM

wrote May 16, 2013 at 10:19 AM

wrote May 16, 2013 at 10:19 AM

wrote Jun 14, 2013 at 7:20 AM