Blog

Useful tidbits related to software development, that I think might be of use or interest to everyone else (or to me when I forget what I did!)

Silverlight Cross Zone Problem

November 09, 2010

The Silverlight client has some self imposed URL access restrictions, which may catch you out from time to time. The most common, cross domain calls, are easy to spot as when something doesn't work you will no doubt fire up fiddler and see the request for the cross domain policy file. This will remind you what you need to do ;) I haven't come across the need to implement cross scheme calls yet, but cross-zone calls have caught me out, as until you encounter it you might not even think it would be an issue. There is no error generated for a cross-zone call being blocked, there is also no traffic generated from Silverlight (so nothing in Fiddler), the call is blocked before any connection is attempted. So to the developer, your app loads and any calls to the services hosted 'cross zone' will not work, with no apparent reason. If this happens to you, you need to check your Internet Explorer settings (even for Firefox hosted Silverlight) to make sure that the client and the server are in the same zone. If they are not you can change your client/server URLs, where they are hosted or change the zone settings in IE (your system administrator may need to push a policy out to intranet users if you choose this option)

ASP.NET 4 GridView EditIndex

October 25, 2010

Prior to version 4 of ASP.NET the GridView's EditIndex property was not automatically set when firing the 'RowEditing' event. It was up to the programmer to set this value before rebinding, if they wanted the row to go into edit mode i.e.:
Private Sub GridView1_RowEditing(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewEditEventArgs) Handles GridView1.RowEditing
         GridView1.EditIndex=e.NewEditIndex
         BindGrid()
End Sub
When the grid rebinds, it would now be in edit mode on the selected index. Sometimes you handle the RowEditing event and do something other than put the row into edit mode, such as showing a custom popup window as the editor. Previously this meant intentionally not adding the above line which meant when the grid was rebound it would not be in edit mode. ASP.NET 4 is now automatically setting the EditIndex property, which is affecting backward compatibility in converted projects. The only way to prevent the grid from now going into edit mode on a row is to set 'e.Cancel=True' in the 'RowEditing' event e.g.:
Private Sub GridView1_RowEditing(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewEditEventArgs) Handles GridView1.RowEditing
        'we only want the unique ID of the data in the row
        Dim primaryKey As Integer = CInt(GridView1.DataKeys(e.NewEditIndex).Value)

        'show a popup window to edit the data for this row
        popEditWindow.CurrentRecordId = primaryKey
        popEditWindow.DataBind()

        mpeEditPopup.Show()
        e.Cancel = True 'we now need to add this line to prevent the row from going into edit mode
End Sub
A quick search on Google shows that this problem has already been reported to Microsoft which can be tracked here.

Blog is Back!

October 19, 2010

My blog is now back online after spending several months in a dormant state (thanks to Google switching off FTP publishing on Blogger!) Needless to say I have now stopped blogging with Blogger and switched to WordPress.

AJAX Calendar Extender IE8 Bug

January 07, 2010

The calendar extender that comes with the AJAX Control Toolkit has a bug in IE8 whereby the 'left/right' arrows to scroll through months do not work.

This is caused by the fact that the 'title' div spans the full width of the control and so the links end up behind this div.

To fix this problem, add the following line to your stylesheet:
.ajax__calendar_title {width:150px; margin:auto; }

Disabling Weekends in the ASP Calender Control

December 04, 2009

ASP.NET includes a built in calendar control 'asp:Calendar' which is an easy way to display a calendar on your page. This is fully customisable in terms of look and feel and you can use this functionality to apply restrictions to the selectable dates. A lot times when building commercial websites the calendar week should only really apply to week days and not weekends. Although you should be checking this in your business logic layers, it is helpful to users if we do not let them submit invalid information. The following shows how you disable the weekend days by 'greying them out' (there is no reason why you could simply hide them altogether). Declare the calendar control:
<asp:Calendar ID="calCollectionDate" runat="server" SelectionMode="Day" WeekendDayStyle-CssClass="disabledDay"></asp:Calendar>
Use Javascript to find the 'disabledDay' items and remove the links:
<script type="text/javascript" language="javascript">
<!--
  var disabledDays=getElementsByClassName('disabledDay', '.*', document.getElementById('<%=calCollectionDate.ClientID %>'));
  for(var i=0;i<disabledDays.length;i++)
  {
    disabledDays[i].innerHTML="<span>"+disabledDays[i].innerHTML.replace(/\<a.*?\>(.*?)\<\/a\>/ig,'$1')+"</span>";

    disabledDays[i].style.color='#8e8e8e';
  }
-->
</script>
This script relies on a third party script for 'getElementByClassName' which can be downloaded from www.robertnyman.com

Recreating SSI Functionality in ASP.NET

August 11, 2009

If your site runs on an ASP.NET server and does not support the classic SSI method of including files for output.

i.e.

<!--#include virtual="/myinclude.asp" -->

Then you may wish to implement your own version of SSI.

In my particular example, I am reading from a HTML file on the server and processing the content before rendering it to the client. I don't see any reason however, why this technique could not be applied in the 'Render' phase of any ASP.NET page.

Essentially, the following function will look for any server side include code in the HTML it is about the render. It will then attempt to process the files that have been included and then swap out the content in the original HTML.

Public Function RunSSI(ByVal htmlContent As String) As String
        'look for SSI
        While htmlContent.Contains("<!--#include virtual=")
                Dim startOfSSI As Integer = htmlContent.IndexOf("<!--#include virtual=")
                Dim LengthOfSSI As Integer = htmlContent.IndexOf("-->", startOfSSI) + "-->".Length - startOfSSI

                Dim ssiText As String = htmlContent.Substring(startOfSSI, LengthOfSSI)
                Dim ssiFile As String = ssiText.Substring(ssiText.IndexOf("virtual=""") + "virtual=""".Length, ssiText.IndexOf("""", ssiText.IndexOf("virtual=""") + "virtual=""".Length) - (ssiText.IndexOf("virtual=""") + "virtual=""".Length))

                'execute the file
                Dim contentResponse As String
                Try
                        Dim wc As New Net.WebClient()
                        Dim URL As String = ""
                        If ssiFile.startsWith("/") Then
                                URL = Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, ssiFile)
                        Else
                                URL = ssiFile
                        End If

                        contentResponse = wc.DownloadString(URL)
                Catch ex As Exception
                        contentResponse = "<!--SSI Processing Error: " & ex.Message & " " & """" & URL & """-->"
                End Try
                'now replace the include with the response
                htmlContent = htmlContent.Remove(startOfSSI, LengthOfSSI)
                htmlContent = htmlContent.Insert(startOfSSI, contentResponse)

        End While

        Return htmlContent
End Function

You will notice, that the SSI markup should be the 'virtual' version and that the code will only work with an absolute path (either http://... or /...).

For the avoidance of a permaloop and to help debugging any SSI which is not successfully processed is replaced with a comment stating the error and URL which it attempted to process.


Definining Event Handlers for Custom Events In the Markup

July 17, 2009

When you have controls nested inside a databound control, you have two options to hook up their events to event handlers. One option is to handle the 'OnItemDataBound'/'OnRowDataBound' or equivalent event of the parent control, find the child control and then use 'AddHandler' to hook up the event handler. The second option is to declaratively put the name of the event handler sub in the markup of the child control and have 'AutoEventWireup' do the mapping for you. With some of the built in ASP.NET controls, you will see the events available to you in the Intellisense dropdown. For example, when nesting a repeater you will see 'OnItemDataBound', 'OnDisposed', 'OnInit' etc. When you have a UserControl inside the databound control you may have declared some custom events but none of these will appear in Intellisense. However, ASP.NET will apply the same rules when it comes to wiring up your events. All you need to do, is set an attribute on your custom control (in the markup) which consists of 'On' and the event name. ASP.NET will recognise the 'On' prefix, marry it up with the existing event declaration in the UserControl and will treat the value part of the attribute declaration as a named public/protected sub in the current context (i.e. the codebehind for the code infront you are working with). To exemplify this idea, the following shows a basic implementation: UserControl codebehind:
Namespace UserControls
    Partial Public Class ExampleUserControl
        Inherits System.Web.UI.UserControl

        Public Event ButtonClick(ByVal sender As Object, ByVal e As System.EventArgs)

        Private Sub btnCompare_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnClickMe.Click
            RaiseEvent ButtonClick(Me, EventArgs.Empty)
        End Sub
    End Class
End Namespace
Page codeinfront:
<%@ Register src="/UserControls/ExampleUserControl.ascx" tagname="ExampleUserControl" tagprefix="MyCtrls" %>

<asp:Repeater ID="rptExample" runat="server">
        <ItemTemplate>
            <MyCtrls:ExampleUserControl ID="ExampleUserControl1" runat="server" OnButtonClick="ButtonClickHandler"></MyCtrls:ExampleUserControl>
        </ItemTemplate>
    </asp:Repeater>
Page codebehind:
Protected Sub ButtonClickHandler(ByVal sender As Object, ByVal e As EventArgs)
        'handle the user control event
    End Sub
Of course, you need to have 'AutoEventWireup=True' in the web.config or @Page directive for this to work.

Fixing Binary Formatted Data After a Code Change

June 05, 2009

I seem to have a habit of changing classnames and/or namespaces as a project develops to make them more meaningful and to help make space for classes which would have benefited from another's name/namespace. When using the binary formatter to serialize an instance of an object, it is important to note that the type information (assembly, namespace, classname) is stored with the data; as opposed to how the XML formatter works, where it is up to the developer to pass in the type, as a parameter, to deserialize. This presents a problem when you rename a class/namespace, as the deserialization will fail. One such occurance was when I was developing my bespoke CMS system, I had a namespace of: CwCms.DataTypes This namespace contained the data types that each of my CMS controls would expose (to give a brief background, I have developed templateable CMS controls which deal with the persistence and editing of data, of a given custom type, by serializing the data using the binary formatter and then exposing the typed data though 'container.dataitem' to the developer when implementing the control on a site.) Anyway, I changed this namespace to 'CwCms.ControlDataTypes' to make it more descriptive, forgetting that I already had some content stored under the old namespace in the database. This of course broke all the content that I had so far and not being one to redo content work, I decided to hack together a snippet of code that would loop through the raw binary data stored in the CMS database and fix the namespace. Looking at the data in a hex editor, I can't say I had time to fully reverse engineer the format, but I got enough understanding to do the job. In summary here is what I noted: There is a 0x16byte header, followed by the size of the assembly name, followed by the assembly name. In my case there were then 5bytes to skip, followed by the size of the fully qualified class name, followed by the fully qualified class name (being the namespace and classname). Using this information, the following code replaces the target namespace with the new namespace, moves the data to create the new space for the longer namespace name and fixes the 'size of fully qualified classname':
Dim SIZEOF_HEADER As Integer = &H16
Dim SKIP_BYTES_AFTER_ASMNAME As Integer = 5

Dim oldNamespace As String = "CwCms.DataTypes"
Dim newNamespace As String = "CwCms.ControlDataTypes"

Dim sizeof_ASMname As Integer = rawCmsRecord.Value(SIZEOF_HEADER)
Dim start_of_classname_struct As Integer = SIZEOF_HEADER + 1 + sizeof_ASMname + SKIP_BYTES_AFTER_ASMNAME

Dim newRecord((rawCmsRecord.Value.Length - 1) + newNamespace.Length - oldNamespace.Length) As Byte
Dim offset As Integer = 0

For i As Integer = 0 To rawCmsRecord.Value.Length - 1
 'copy the bytes to the new record
 If i <> start_of_classname_struct Then
  newRecord(i + offset) = rawCmsRecord.Value(i)
 Else
  'weve reached the start of the classname struct. inject the custom stuff
  Dim sizeof_classname As Integer = rawCmsRecord.Value(start_of_classname_struct)
  Dim oldclassname As String = ""

  For i2 = i + 1 To i + sizeof_classname
   oldclassname &= ChrW(rawCmsRecord.Value(i2))
  Next

  oldclassname = Right(oldclassname, oldclassname.Length - oldNamespace.Length)

  Dim newFQCN As String = newNamespace & oldclassname
  offset = newFQCN.Length - sizeof_classname

  'overwrite the new length
  newRecord(i) = CByte(newFQCN.Length)

  'put the new classname on the end
  Dim ctr As Integer = 0
  For ctr = 1 To newFQCN.Length
   newRecord(i + ctr) = CByte(AscW(newFQCN(ctr - 1)))
  Next

  i += sizeof_classname
 End If
Next

rawCmsRecord.Value = newRecord
rawCmsRecord.Save()
As the format is not documented by Microsoft the format may change without notice and as I have mentioned this is only a quick interpretation of what I saw, to get the job done. Before implementing this code for yourself, check it will not break your data, I am not responsible!

Setting a validation group on a User Control

May 15, 2009

Using the built in .NET validation, you can assign a 'validation group' to a set of validation controls, submit buttons and validation summary.

This creates a separation between different sets of controls on the page, so that only the relevant controls are validated for a specific submit button.

When you design your application as separate user control components, you may want to set a different validation group on the validators of the user control, depending on the context.

To allow this, I have exposed a 'ValidationGroup' property on the user control. This subsequently makes a recursive to all its child controls to set the validation group of all the validators.

This way, you can set the validation group of the user control instance as you would any other control and it will assign all of its contained validators to that group.

Code:

Public Property ValidationGroup() As String
 Get
  Return CStr(ViewState("ValidationGroup"))
 End Get
 Set(ByVal value As String)
  SetValidationGroupOnChildren(Me, value)
  ViewState("ValidationGroup") = value
 End Set
End Property

Private Sub SetValidationGroupOnChildren(ByVal parent As Control, ByVal validationGroup As String)
	For Each ctrl As Control In parent.Controls
		If TypeOf ctrl Is BaseValidator Then
			CType(ctrl, BaseValidator).ValidationGroup = validationGroup
		ElseIf TypeOf ctrl Is IButtonControl Then
			CType(ctrl, IButtonControl).ValidationGroup = validationGroup
		ElseIf ctrl.HasControls() And ctrl.Visible = True Then
			SetValidationGroupOnChildren(ctrl, validationGroup)
		End If
	Next
End Sub

Running Initialization Code in Visual Studio Unit Testing

May 12, 2009

Often in a project, you have code which runs when the application starts (such as in Program.cs or in your Global.asax file) which will set up all of your shared resources for the rest of the project, such as initializing global variables, configuring the BLL, connecting to a datasource etc. As mentioned above, their are places to put this code provided for you. When you are working within a unit test project you can also have some initialization code run when you run the tests. To do this, you create a test class with a static method and decorate it with the 'AssemblyInitializeAttribute'. I usually create a class called 'InitTestEnv.cs' which consists of something like the following:
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace YourSolutionNamespace.Test
{
    [TestClass]
    public static class InitTestEnv
    {
        [AssemblyInitialize]
        public static void Initialize(TestContext context)
        {
            //configure the BLL
            BLL.Configuration.EnableCaching = false;

            //connect it to a datasource
            if (!BLL.DataProviders.HasValidDatasource())
                BLL.DataProviders.SetDatasource(BLL.DataProviders.ProviderList.SqlDataProvider("connection string"));
        }
    }
}
  Normally, I would reference the ConfigurationManager classes and then share my 'connectionStrings.config' and 'appSettings.config' files from my UI layer to keep the tests in sync with the settings that will be deployed, but in the above example I have left this out.