Asp.net menu – show parent as selected

I’ve been working on a group of tools for my company in asp.net, and have been using an ASP Master page with an ASP menu sourced from a Web.SiteMap xml file. Looks a little like this:

Default menu

I’ve got sub-menu’s under each heading, that look like this:

Showing submenu from parent hover

 

The problem I’ve been struggling with is that upon selecting an item from the sub-menu, the sub-menu is hidden (which is desired) but the parent doesn’t show as selected.

This is because in Net 4.0, you can only have one item in a menu set as selected, which happens to be the actual page that is displayed. Since my sub-menu is hidden except at rollover, I don’t care that the child is shown as selected.

I finally found a solution that worked for me, based on this StackOverflow question.

However, it is a bit of a hack instead of a real solution. Basically, we deselect the child, and select the parent instead. For those who don’t hide the sub-menu, this may not be appropriate (which was the original intent of the StackOverflow question above.

The other downside of this method is that due to the organization of the web.SiteMap xml file, while on the Home page it doesn’t show as selected. This is a small price to pay for the overall benefit of parent selection in my case.

aspx page:

<asp:Menu 
            ID="Menu1"
            CssClass="menu" 
            runat="server" OnMenuItemDataBound="Menu1_MenuItemDataBound"
            DataSourceID="SiteMapDataSource1" Orientation="Horizontal" 
            StaticDisplayLevels="2" RenderingMode="List" >
            <StaticSelectedStyle CssClass="selected" />
</asp:Menu>

 

aspx codebehind

protected void Menu1_MenuItemDataBound(object sender, MenuEventArgs e)
    {
        if (SiteMap.CurrentNode != null)
        {
            if (e.Item.Selected == true)
            {
                e.Item.Selected = true;
                e.Item.Parent.Selectable = true;
                e.Item.Parent.Selected = true;
            }
        }
    }

Web.SiteMap

<siteMapNode roles="*">
    <siteMapNode url="~/Default.aspx" title="Home" description="Default" roles="*">
    </siteMapNode>
    <siteMapNode  roles="*" title="Time & Equip" description="Time & Equip" >
		<siteMapNode url="~/TimeEquip/TimeSheet.aspx" title="TimeSheet" description="Timesheet entry" />
		<siteMapNode url="~/TimeEquip/WeekTmSht.aspx" title="Weekly Timesheet"  description="Weekly Timesheet" />
		<siteMapNode url="~/TimeEquip/TmShtApproval.aspx" title="Timesheet Approval" description="Timesheet Approval" />
    </siteMapNode>
    <siteMapNode roles="*"  title="Project Tools" description="Project Tools" >   
		<siteMapNode url="~/Projects/ProjectPerformance.aspx" title="Project Performance" description="Project Performance Entry Site" />
    </siteMapNode>
    <siteMapNode roles="*" title="Finance/Acct" description="Fin/Acct Dashboard">
		<siteMapNode url="~/AccFin/FinAcct_Dashboard.aspx" title="Finance/Acct" description="Finance/Accounting Main" />
    <siteMapNode roles="*" title="HR Tools" description="" >
		<siteMapNode url="~/HRTools/RecognitionApproval.aspx" title="Recognition Approval" description="" />
    </siteMapNode>
  </siteMapNode>

Final Result:

Parent selected instead of child

Adobe Flash “InstallAx.exe” Update

This is an update to my earlier post on December 6th, 2010 here.Adobe Flash Player

Since the original post, a few developments have been made through personal findings and the comments.

The “Installax.exe is not marked for installation” issue is still occurring with the latest versions of Flash, so I have decided to abandon GPO deployment through software assignment altogether.

Instead, I will be using the following batch script in a startup script GPO, and keep it updated accordingly. Thanks to Chris Deeming who made some improvements to the original script and posted it in the comments on my original post.

I have tried to be as verbose as possible in the comments so that this is easy to follow along with.


:: Adobe flash giving "installax" errors when updating from GPO
:: This batch file will remove those errors and install latest version
:: This batch file should be run from a shutdown or startup script
 
@echo off

:: These are the only changes that need to be made for an updated version. 
:: Simply put in the new version number, and the path to the new MSI file. 
Set "LatestVersionNumber=11.0.1.152"
Set "LatestVersionMSI_Path=\\domain\Public\General\adobeflash\install_flash_player_11.0.1.152.msi"

 
:: Check Windows Version
ver | findstr /i "5\.1\." > nul
IF %ERRORLEVEL% EQU 0 goto ver_XP
ver | findstr /i "6\.1\." > nul
IF %ERRORLEVEL% EQU 0 goto ver_Win7
 
 
:ver_Win7
::Run Windows 7 specific commands here
Set "AdobeVersion=" & setlocal & Set "$V="
:: Look in the Uninstall area of registry, where the installed version of Flash player is listed
Set "RegKey=HKLM\SOFTWARE\Wow6432Node\Macromedia\FlashPlayerActiveX"
Set "RegItem=Version"
:: Take the output of a REG QUERY to the location above, and put it into the AdobeVersion variable
For /f "tokens=3*" %%! in (
   '2^>nul Reg.exe QUERY "%RegKey%" /v "%RegItem%" ^|(
   Findstr.exe /ri "\<%RegItem%\>"^)') Do Set "$V=%%!"
endlocal & call Set "AdobeVersion=%$V%"

:: If what is currently installed matches latest version, exit. Otherwise, go to install section
IF DEFINED AdobeVersion (goto :Win7vercheck) ELSE (goto :install)
:Win7vercheck
if %AdobeVersion% == %LatestVersionNumber% goto :exit
goto :Install
 
 
:ver_XP

::Run Windows XP specific commands here
Set "AdobeVersionXP=" & setlocal & Set "$T="
:: Look in the Uninstall area of registry, where the installed version of Flash player is listed
Set "RegKeyXP=HKLM\SOFTWARE\Macromedia\FlashPlayerActiveX"
Set "RegItemXP=Version"
:: Take the output of a REG QUERY to the location above, and put it into the AdobeVersion variable
For /f "tokens=3*" %%! in (
   '2^>nul Reg.exe QUERY "%RegKeyXP%" /v "%RegItemXP%" ^|(
   Findstr.exe /ri "\<%RegItemXP%\>"^)') Do Set "$V=%%!"
endlocal & call Set "AdobeVersionXP=%$V%"

:: If what is currently installed matches latest version, exit. Otherwise, go to install section
IF DEFINED AdobeVersionXP (goto :WinXPvercheck) ELSE (goto :install)
:WinXPvercheck
if %AdobeVersionXP% == %LatestVersionNumber% goto :exit
goto :Install
 
:Install

 
::Uninstall current versions of Flash from DFS share
msiexec /uninstall %LatestVersionMSI_Path% /quiet

:: Remove offending registry entries causing the error
REG DELETE HKEY_CLASSES_ROOT\Installer\Features\4E867BFF724E3554CB631AF1E5269AD4 /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Features\6D98A6046E9005543B07D873D6BD65EB /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Products\4E867BFF724E3554CB631AF1E5269AD4 /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Products\6D98A6046E9005543B07D873D6BD65EB /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Features\00B86459180C72B4CA69A0A21353E906 /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Products\00B86459180C72B4CA69A0A21353E906 /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Products\68AB67CA7DA73301B744AA0000000010 /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Products\A42FBB8140D64AC46B4BC13F2761E2CE /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Products\C460100B160DEAB4091314A638D85563 /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Features\4195BD842778D2748BFD2E90B25E896F /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Features\B0E390BD43903814AB06C5A1CDF94642 /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Products\4195BD842778D2748BFD2E90B25E896F /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Products\B0E390BD43903814AB06C5A1CDF94642 /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Features\D9D09CCDD8F460A40905DABD82F49FAF /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Products\D9D09CCDD8F460A40905DABD82F49FAF /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Features\2DAEED307B3FFB5409602AD510F5002D /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Features\5258E229E7CA4924CAAA3417D244320C /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Products\2DAEED307B3FFB5409602AD510F5002D /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Products\41699E6F240F9544287BB8832B063165 /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Products\5258E229E7CA4924CAAA3417D244320C /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Features3797D32A1CEE534388FAABEEF25730B /F
REG DELETE HKEY_CLASSES_ROOT\Installer\Products3797D32A1CEE534388FAABEEF25730B /F

 
:: Install latest version of Flash
msiexec /i %LatestVersionMSI_Path% /quiet

:: Copy mms.cfg to disable auto-update
copy /Y \\domain\Public\General\adobeflash\mms.cfg %systemdrive%\windows\system32\Macromed\Flash
copy /Y \\domain\Public\General\adobeflash\mms.cfg %systemdrive%\windows\SysWOW64\Macromed\Flash
goto :exit
 
:exit

 

 

ReportViewer doesn’t take full width in Internet Explorer

I’ve been struggling with an issue with a ReportViewer control in an aspx page for a while now, and finally dedicated some time to getting it resolved.

My control looks like this:

rsweb:ReportViewer ID="ReportViewer1" runat="server" Font-Names="Verdana" Font-Size="1em"
          ProcessingMode="Remote" AsyncRendering="False" SizeToReportContent="True" ShowBackButton="True"
          EnableViewState="True" Width="100%"

 

With these attributes, the report displayed correctly in Firefox, but not in Internet Explorer 9. There was a small area to the right of the report body that the report wouldn’t expand into. I’ve seen this reported elsewhere such as this stackoverflow topic.

Here’s what it looked like:

Comparison of width
Firefox display vs. Internet Explorer display

 

After digging a little deeper with the developer tools, I’ve found that the report body is broken into two cells in a table.

The first has a unique identifier, with no width specified. The second has a width of 100%, but is empty. It’s this empty TD that is causing the space:

HTML code showing the problem TD

For some reason Firefox ignores width on the second TD, but IE gives it space.

I wasted a lot of time trying to use javascript, report server attributes and c# code to get rid of this, but finally discovered an easy way to fix it using CSS.

That first TD uses a unique identifier, but always ends with “oReportCell”. So I used wildcard CSS selectors to give it a width value:

 td[id*='oReportCell'] {width:100% !important;}

 

Now the width appears correctly within IE!

Create a discussion board for Mindtouch wiki

A recent comment on my Mindtouch intro page asked how I built the discussion board.

I originally got the code from the Mindtouch Developer site here, however I can’t seem to find the complete source code anymore. Either way, I’m pretty sure neilw, a valuable contributor to the Mindtouch community is the author of this code and full credit goes to him.

The actual implementation is very simple. You just need to make a couple of templates.

First, create the discussion board page template, or topic list:

Template:/ForumTopicList

 
<div block="var homepath=(args.path ?? '/Forum_Topics');
if (homepath[0] == '/') {
let homepath = page.path .. homepath;
}">
	<p>{{wiki.create(&quot;Create New Topic&quot;,homepath,(args.template ?? &quot;ForumTopic&quot;),true,&quot;Put Your Title Here&quot;)}}</p>
	<table block="var homepage = wiki.getpage(homepath);
    var empty = true;
    if (homepage != nil) {
    let empty = (#homepage.subpages == 0);
    }
    var topics = [];
    if (!empty) {
    foreach (var p in homepage.subpages) {
    var entry = wiki.page(p.path);
    var originator = entry['//*[@id=\'ForumEntryOriginator\']'];
    if (xml.text(originator) == nil) { let originator = web.link(p.author.uri, p.author.name); }
    var lastAuthor = p.author;
    var lastDate = p.date;
    var lastChange = string.contains(p.editsummary, 'page created') ? '(new)' : '(E)';
    if (#p.comments != 0) {
    var lastComment = list.reverse(p.comments)[0];
    if (date.isafter(lastComment.date, p.date)) {
    let lastAuthor = lastComment.author;
    let lastDate = lastComment.date;
    let lastChange = '(C)';
    }
    }
    let lastAuthor = web.link(lastAuthor.uri, lastAuthor.name);
    let lastDate = date.format(lastDate, 's');
    var sticky = (p.tags.sticky != nil ? 'STICKY!' : '');
    let lastDate = (sticky != '' ? 'z' : 'a') .. lastDate;
    let topics ..= [ { page:p, originator:originator, author:lastAuthor, date:lastDate, change:lastChange, sticky:sticky } ];
    }
    let topics = list.sort(topics, 'date', true);
    }" border="1" cellpadding="4" cellspacing="0" class="table">
		<tbody>
			<tr>
				<th style="width: 55%" valign="top">Topic</th>
				<th style="text-align: center; width: 10%" valign="top">Starter</th>
				<th style="text-align: center; width: 5%" valign="top">Replies</th>
				<th style="text-align: center; width: 25%" valign="top">Last Comment(C) or Edit(E)</th>
				<th style="width: 5%" valign="top">Views</th>
			</tr>
			<tr class="{{__count % 2 == 0 ? 'bg1' : 'bg2'}}" foreach="var t in topics" if="!empty">
				<td valign="top"><font size="1" style="font-size: 16px"><strong><font style="color: #598527; font-size: 10px">{{t.sticky}}</font> {{web.link(t.page.uri, t.page.title)}}</strong></font><br />
					&nbsp;</td>
				<td style="text-align: center; vertical-align: top">{{ t.originator; }}</td>
				<td style="text-align: center; vertical-align: top">{{#t.page.comments}}</td>
				<td style="text-align: center" valign="top"><font style="font-size: 12px">{{date.format(string.substr(t.date,1),'yyyy-M-d H:mm');' '; if (t.change != '(new)') { 'by '; t.author; ' '; } t.change; }}<br />
					</font></td>
				<td style="text-align: center" valign="top">{{t.page.viewcount}}</td>
			</tr>
			<tr if="empty">
				<td colspan="5"><font style="font-size: 14px"><strong>(no topics yet)</strong></font></td>
			</tr>
		</tbody>
	</table>
	<br />
	&nbsp;</div>

Then call this template somewhere on your page, with this:

{{ template("ForumTopicList") }}

Now, you need to create the template for the actual new topic post:

Template:/ForumTopic

<table border="0" cellpadding="5" cellspacing="0" style="border-bottom: black thin solid; border-left: black thin solid; background-color: #eeeeee; width: 100%; border-top: black thin solid; border-right: black thin solid">
	<tbody style="vertical-align: top">
		<tr>
			<td>Created by <span id="ForumEntryOriginator">{{ edit: web.link(user.uri, user.name) }}</span> on {{ edit:&quot;{{ save:date.now}}&quot; }}<br />
				&nbsp;</td>
			<td style="text-align: center; vertical-align: top"><span style="color: #598527; font-weight: bold">{{ if (page.tags.sticky != nil) { &quot;STICKY&quot;; } }}</span></td>
			<td style="background-image: none; text-align: right; vertical-align: top">{{ web.link(page.feed, &quot;Track this page&quot;) }}<br />
				&nbsp;</td>
		</tr>
	</tbody>
</table>
<p>&nbsp;&nbsp;</p>
<p>&nbsp;</p>
<table border="1" cellpadding="5" cellspacing="0" class="comment" style="background-color: #ffdddd; border-collapse: separate; vertical-align: top">
	<tbody style="vertical-align: top">
		<tr>
			<td style="width: 100%">
				<p><span class="comment"><font style="font-size: 21px"><strong>Instructions:</strong></font></span></p>
				<ol>
					<li><span class="comment">These instructions will only appear while you are editing.&nbsp; No need to delete them!</span></li>
					<li style="font-weight: bold"><span class="comment">Please enter your Subject in the title above.</span></li>
					<li><span class="comment">Enter the content of your message in whatever form you like below.</span></li>
					<li><span class="comment"><span style="font-weight: bold">Adding comment: </span><em>If you're not the original creator of this topic, please reply by using the &quot;Add Comment&quot; field at the bottom of the page. </em>The comment added will be tabulated in the &quot;Reply&quot; field.<br />
						&nbsp;</span></li>
				</ol>
			</td>
		</tr>
	</tbody>
</table>
<p>&nbsp;</p>
<p>&lt;enter your topic message/description here&gt;</p>

You can modify this template to include whatever instructions you like.

That’s it! To create a new topic, navigate to the page that you call the ForumTopicList template, and click the “Create New Topic” button:

When others comment on the page that’s created, it will count as “replies” and show in the topic list.

Redirect HTTP to HTTPS with IIS

I’ve got an internal website that runs in IIS 6, for which I have enabled SSL. Due to the nature of this website, and the login credentials used, I want to make sure any access is always encrypted, but still allow my users to access it at http:// for ease of use.

 

Fortunately I found a pretty simple way of doing this with IIS 6 (I don’t know whether it’s still supported in IIS 7, but I imagine it would).

First, create a file called sslredirect.htm, with the contents as:

<SCRIPT type=text/javascript>
<!--
if (location.protocol != 'https:')
{
window.location = 'https://'+ location.host + location.pathname + location.search;
//alert(location.host + location.pathname + location.search); Just for sanity check
 
}
// -->
</SCRIPT>

 

Then, go into the properties of your IIS site, and on the Custom Errors tab, change error 403:4 to point to your sslredirect.htm file.

IIS custom error configuration
 

Now, if someone opens up http://www.website.com, it will automatically redirect them to https://www.website.com.