Categories
ASP.net VB.net Web Forms

Disabling mobile master pages with ASP.NET Friendly URLs

ASP.NET Friendly URLs automatically configures default routes that:

  1. drop the .aspx extension, and
  2. redirect to mobile .Mobile.aspx pages, inclusive of .Mobile.Master pages.

While I want the default routes to drop the .aspx extension, I don’t want the default routes to redirect to a mobile version. Unfortunately, I haven’t been able to find a way to disable the latter with Microsoft.AspNet.FriendlyUrls.Core 1.0.2. I use responsive web design to reflow content on mobile devices and avoid maintaining a second presentation layer with .Mobile.aspx pages. While I can happily ignore any .Mobile.aspx pages, deleting the included Site.Mobile.Master page when installing ASP.NET Friendly URLs or creating any new master pages causes my web application to generate an error that it can’t find a mobile version of the master page.

To resolve this issue, I’ve hacked together a custom WebFormsFriendlyUrlResolver that does not attempt to retrieve a mobile version of a master page (which generates the error):

''' <summary>
''' HACK: Disable ".Mobile.Master" master pages.
''' </summary>
Public Class MyWebFormsFriendlyUrlResolver
	Inherits Microsoft.AspNet.FriendlyUrls.Resolvers.WebFormsFriendlyUrlResolver

	''' <summary>
	''' Disable ".Mobile.Master" master pages.
	''' </summary>
	Protected Overrides Function TrySetMobileMasterPage(httpContext As HttpContextBase, page As UI.Page, mobileSuffix As String) As Boolean
		If mobileSuffix = "Mobile" Then
			Return False
		Else
			Return MyBase.TrySetMobileMasterPage(httpContext, page, mobileSuffix)
		End If
	End Function
End Class

I then register this custom WebFormsFriendlyUrlResolver in App_Start/RouteConfig.vb in my web application:

Imports System.Web.Routing
Imports Microsoft.AspNet.FriendlyUrls

Public Module RouteConfig

	Public Sub RegisterRoutes(ByVal routes As RouteCollection)
		Dim settings As FriendlyUrlSettings = New FriendlyUrlSettings()
		settings.AutoRedirectMode = RedirectMode.Permanent
		routes.EnableFriendlyUrls(settings, New Microsoft.AspNet.FriendlyUrls.Resolvers.IFriendlyUrlResolver() {New MyWebFormsFriendlyUrlResolver()})
	End Sub
End Module

I can now remove Site.Mobile.Master, ViewSwitcher.ascx and ignore creating .Mobile.Master pages without causing my web application to error when viewed on mobile devices.

I realise this is a hack, however this is the best solution I’ve been able to come up. Do you know how to disable the default routes for mobile devices? Please send me a comment and enlighten me to a more robust solution.

Varsågod!

Thanks to:

Categories
ASP.net VB.net

Adding EmptyDataTemplate to Repeater and TreeView

The GridView control contains an EmptyDataTemplate property to display alternative content when the data source is empty. However, other data bound controls such as Repeater and TreeView do not have this property. We can add this functionality to existing data bound controls by:

  1. subclassing the existing data bound control,
  2. adding the EmptyDataTemplate property, and
  3. overriding the Render event to conditionally render the EmptyDataTemplate if the data source is empty and the EmptyDataTemplate property is specified.

Let’s create MyRepeater using these steps:

Imports System.ComponentModel
Imports System.Web.UI

''' <summary>
''' Adds <b>EmptyDataTemplate</b> property.
''' </summary>
Public Class MyRepeater
	Inherits WebControls.Repeater
	Implements INamingContainer

	<BrowsableAttribute(False)>
	<PersistenceMode(PersistenceMode.InnerProperty)>
	<TemplateContainer(GetType(MyRepeater))>
	Public Overridable Property EmptyDataTemplate() As ITemplate

	Protected Overrides Sub Render(writer As HtmlTextWriter)
		If Items.Count = 0 AndAlso EmptyDataTemplate IsNot Nothing Then
			Dim container As New Control()
			EmptyDataTemplate.InstantiateIn(container)
			container.RenderControl(writer)
		Else
			MyBase.Render(writer)
		End If
	End Sub
End Class

Items.Count is used to check whether the data source is empty for the Repeater control. We can create MyTreeView in a similar way:

Imports System.ComponentModel
Imports System.Web.UI

''' <summary>
''' Adds <b>EmptyDataTemplate</b> property.
''' </summary>
Public Class MyTreeView
	Inherits WebControls.TreeView
	Implements INamingContainer

	<BrowsableAttribute(False)>
	<PersistenceMode(PersistenceMode.InnerProperty)>
	<TemplateContainer(GetType(MyTreeView))>
	Public Overridable Property EmptyDataTemplate() As ITemplate

	Protected Overrides Sub Render(writer As HtmlTextWriter)
		If Nodes.Count = 0 AndAlso EmptyDataTemplate IsNot Nothing Then
			Dim container As New Control()
			EmptyDataTemplate.InstantiateIn(container)
			container.RenderControl(writer)
		Else
			MyBase.Render(writer)
		End If
	End Sub
End Class

Nodes.Count is used to check the TreeView control instead.

Finally, substitute <asp:Repeater> or <asp:TreeView> with new MyRepeater or MyTreeView controls, add the EmptyDataTemplate property and voilà, you can now specify your own custom message when the data source is empty.

Varsågod!

Thanks to:

Categories
ASP.net VB.net

TreeView’s TreeNodeCheckChanged and Firefox

Let’s create a TreeView control with checkboxes and a define a TreeNodeCheckChanged event:

<asp:TreeView runat="server" ID="TreeView1" ShowCheckBoxes="All" OnTreeNodeCheckChanged="TreeView1_TreeNodeCheckChanged"></asp:TreeVIew>

Unfortunately, this definition alone does not generate the required code that fires the TreeNodeCheckChanged event when a checkbox is changed. We need to manually fire this event with Javascript:

<script language="javascript" type="text/javascript">
	function onClientTreeNodeCheckChanged(e) {
		var o = e ? e.target : window.event.srcElement;
		if (o.tagName == "INPUT" && o.type == "checkbox") {
			__doPostBack("", "");
		}
	}
</script>

Older versions of Internet Explorer define the window.event object (which also exists for newer versions of Internet Explorer and Chrome), however this object is undefined for other modern browsers such as Firefox. Modern web browsers (including newer versions of Internet Explorer) define e.target instead. By checking whether e is defined, we can use e.target for modern web browsers, including Firefox, otherwise we use window.event.srcElement for older versions of Internet Explorer.

Next, we need to bind the onClientTreeNodeCheckChanged javascript function to the TreeView control:

Private Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
	If Not IsPostBack Then
		TreeView1.Attributes.Add(HtmlTextWriterAttribute.Onclick.ToString(), "onClientTreeNodeCheckChanged(event)")
	End If
End Sub

Finally, add the TreeNodeCheckChanged event to your code behind file:

Private Sub TreeView1_TreeNodeCheckChanged(sender As Object, e As TreeNodeEventArgs)
	' Your code here
End Sub

And you’ve now got everything you need to run the TreeNodeCheckChanged event for both modern web browsers and older versions of Internet Explorer.

Varsågod!

Thanks to:

Categories
ASP.net VB.net

StringToBitmap()

I created a .ashx generic web handler that returns images. However, I wanted to return a meaningful response if there was any problem returning the requested image (for example, an invalid ID, missing file, corrupted file, etc). Since the application calling the web handler only expected an image, I needed to return the meaning response as an image. Here’s what I wrote:

Imports System.Drawing
Imports System.Windows.Forms

Public Function StringToBitmap(s As String, font As Drawing.Font, brush As Drawing.Brush) As Drawing.Bitmap
	Dim textSize As Drawing.Size = Windows.Forms.TextRenderer.MeasureText(s, font)
	Dim bitmap As New Drawing.Bitmap(textSize.Width, textSize.Height)

	Using graphics As Drawing.Graphics = Drawing.Graphics.FromImage(bitmap)
		graphics.DrawString(s, font, brush, 0, 0)
	End Using
	Return bitmap
End Function

I also created a small wrapper with a default font and brush:

Public Function StringToBitmap(s As String) As Drawing.Bitmap
	Return StringToBitmap(s, Drawing.SystemFonts.DefaultFont, Drawing.Brushes.Black)
End Function

Varsågod!

Categories
ASP.net VB.net

The file ‘DynamicData/FieldTemplates/EmailAddress_Edit.ascx.vb’ does not exist.

Have you installed DynamicDataTemplatesVB 1.0.1 and encountered the following errors on compilation:

The file 'DynamicData/FieldTemplates/EmailAddress_Edit.ascx.vb' does not exist.
The file 'DynamicData/FieldTemplates/Url_Edit.ascx.vb' does not exist.

in addition to other errors in “EmailAddress_Edit.ascx”, “EmailAddress_Edit.ascx.vb”, “Url_Edit.ascx” and “Url_Edit.ascx.vb”?

These compilation errors are caused by three typos in DynamicDataTemplatesVB 1.0.1:

  1. On line 1 in “EmailAddress_Edit.ascx.vb”, replace:
    CodeFile="EmailAddress_Edit.ascx.vb"

    with:

    CodeBehind="EmailAddress_Edit.ascx.vb"
  2. On line 1 in “Url_Edit.ascx.vb”, replace:
    CodeFile="Url_Edit.ascx.vb"

    with:

    CodeBehind="Url_Edit.ascx.vb"
  3. On line 1 in “Url_Edit.ascx.vb”, replace the double period in the “inherits” tag with a single period, for example:
    Inherits="Namespace..Url_EditField"

    with:

    Inherits="Namespace.Url_EditField"

While I found the third error was easy to spot, the first two errors uses the older Web Site Project syntax (CodeFile) to reference the associated code file instead of the newer Web Application Project syntax (CodeBehind). The similarity between these tags made it more difficult to spot. Recompile and you’re up and running.