Chris Cavanagh’s Blog

Disabling HTML Input Elements

May 28, 2009 · 4 Comments

If you’re using IE, you can disable all input elements inside a div by setting “disabled=true”.  Unfortunately (thanks to standards compliance nonsense ;) ) it doesn’t work anywhere else.

If you’re using jQuery, take a look here.  If not, this bit of script might help:

function SetDisabledStates( container, newStates )
{
    var states = [];

    var replace = function( element )
    {
        states[ element.id ] = element.disabled;
        element.disabled = isArray( newStates ) ? newStates[ element.id ] : newStates;
    };

    var inputs = container.getElementsByTagName("input");
    for ( var i = 0; i < inputs.length; ++ i ) replace( inputs[ i ] );

    var selects = container.getElementsByTagName( "select" );
    for ( var i = 0; i < selects.length; ++ i ) replace( selects[ i ] );

    return states;
}

function isArray( obj )
{
   return obj.constructor.toString().indexOf("Array") != -1;
}

To preserve and override the states, do something like this:


var disabledStates = SetDisabledStates( myContainer, true );

 

Then to restore the states, do this:


SetDisabledStates( myContainer, disabledStates );

 

→ 4 CommentsCategories: .NET

HomeLight – 100% Silverlight website, plus physics

May 22, 2009 · 2 Comments

Speaks for itself: http://rene-schulte.info/ :)   It almost works with Moonlight Preview 3 too! (fingers crossed Preview 4 will nail it :) ).

Unfortunately I didn’t write or have anything to do with the site; I just thought it looked cool :)

→ 2 CommentsCategories: .NET

ASP.NET 4.0 Beta 1 and IIS App Pools

May 21, 2009 · Leave a Comment

If you install .NET 4.0 Beta 1 on a machine that also needs to run ASP.NET 1.1 applications, you may need to tweak some IIS settings.  The Beta 1 installer appears to assign .NET 4.0 to all existing application pools.  Make sure your ASP.NET 1.1 pool looks like this:

image

Hope this helps! :)

→ Leave a CommentCategories: .NET

ASP.NET WebForm Routing with SiteMaps

May 19, 2009 · 8 Comments

I’ve updated my ASP.NET WebForm Routing demo with basic SiteMap support.  It’s targeting ASP.NET 3.5 SP1 and expects your WebForm (Page) to implement the IRoutablePage interface (or you can derive from RoutablePage).  If you’re using ASP.NET 4.0 you can omit this dependency (as RequestContext is already available on the Page :) ).

SiteMap raises the SiteMapResolve event whenever its CurrentNode property is read.  This gives you an opportunity to determine the current node based on the route, without requiring a custom SiteMapProvider.  The example below takes the current route URL, removes any route values and looks for a matching SiteMapNode:

private SiteMapNode SiteMap_SiteMapResolve( object sender, SiteMapResolveEventArgs e )
{
    var routable = e.Context.CurrentHandler as IRoutablePage;

    if ( routable != null )
    {
        var rc = routable.Routing.RequestContext;
        var route = rc.RouteData.Route;
        var segments = route.GetVirtualPath( rc, null ).VirtualPath.Split( '/' );
        var path = "~/" + string.Join( "/", segments.Take( segments.Length - rc.RouteData.Values.Count ).ToArray() );

        return SiteMap.Provider.FindSiteMapNodeFromKey( path );
    }

    return null;
}

You create a .sitemap file in the usual way, but put your “routed” URLs in instead:

<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
    <siteMapNode url="~/" title="Home" description="Home">
        <siteMapNode url="~/Search" title="Search" description="Search" />
        <siteMapNode url="~/Details" title="Details" description="Details" />
        <siteMapNode url="~/NoMaster" title="No Master" description="No Master">
            <siteMapNode url="~/NoMaster/MySub" title="My Subpage" description="My Subpage"/>
        </siteMapNode>
    </siteMapNode>
</siteMap>

You can use the regular SiteMapPath control to show the current location.  However, you might prefer to render your own.  The sample project includes a couple of helper methods to achieve just that:

/// <summary>
/// Gets SiteMap path for the current request.
/// </summary>
/// <returns></returns>
public string SiteMapPath()
{
    var pages = SiteMap.CurrentNode.For( n => n != null, n => n.ParentNode ).Reverse().Select( n => SiteMapLink( n ) );

    return string.Join( " > ", pages.ToArray() );
}

/// <summary>
/// Gets a SiteMap link.
/// </summary>
/// <param name="node">The node.</param>
/// <returns></returns>
private string SiteMapLink( SiteMapNode node )
{
    var span = string.Format( "<span class=\"siteMapLink\">{0}</span>", node.Title );

    return ( node != SiteMap.CurrentNode )
        ? string.Format( "<a href=\"{0}\">{1}</a>", node.Url, span )
        : span;
}

You can read more about the “For” extension method in my earlier post.

Source code for the sample project is available on CodePlex.  You can try the demo online right here :) .

→ 8 CommentsCategories: .NET

IF + Silverlight + Linux … Almost!

May 19, 2009 · Leave a Comment

Using the latest preview of Moonlight 2.0, SilverGlulxe (Interactive Fiction interpreter) is [almost] running on Linux!

Here’s what it currently looks like:

image

Text is missing formatting (there should be some bold and italic in there) and the Enter key doesn’t work, which makes it tricky to give it commands :)   Also the scale slider has some issues (text not reformatting to fit when it’s resized).  Other than that it’s looking great!

SilverGlulxe does some interesting things with threads, so it could be that’s behind the “Enter issue”.  I’ll probably wait a few more [Moonlight] previews, then have a closer look at my code… :)

→ Leave a CommentCategories: .NET

More Extension Methods

May 18, 2009 · 2 Comments

Here are a couple more extension methods you may find useful:

Collection splitter:

Split a collection into a sequence of fixed-size groups:

/// <summary>
/// Splits the specified items into fixed-size groups.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source">The source.</param>
/// <param name="size">The group size.</param>
/// <returns>Returns collection of groups.</returns>
public static IEnumerable<IGrouping<int, T>> Split<T>( this IEnumerable<T> source, int size )
{
    var index = 0d;

    return source.GroupBy( v => (int)( ( index++ ) / size ) );
}

You could use it like this:

var items = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
var chunks = items.Split( 5 );

“For” enumerator:

Generate a sequence of elements like a for loop:

/// <summary>
/// Generates a sequence of elements while <paramref name="predicate"/> is <see cref="true"/>.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value">The initial value.</param>
/// <param name="predicate">A predicate to control continuation of the sequence.</param>
/// <param name="selector">A function to retrieve the next element.</param>
/// <returns>Returns a sequence of elements.</returns>
public static IEnumerable<T> For<T>( this T value, Predicate<T> predicate, Func<T, T> selector )
{
    while ( predicate != null && predicate( value ) )
    {
        yield return value;

        value = ( selector != null ) ? selector( value ) : default( T );
    }
}

It’s good for making non-enumerable sequences enumerable:

var pages = SiteMap.CurrentNode.For( n => n != null, n => n.ParentNode ).Reverse();

return string.Join( " > ", pages.Select( p => p.Title ).ToArray() );

Comments, suggestions and improvements always welcome!

→ 2 CommentsCategories: .NET

More on Moonlight 2.0 Preview 1

May 6, 2009 · 3 Comments

Mark Monster made a great post about running Silverlight on Linux using Moonlight 2.0 Preview 1.  If you’re on Windows and just want a peek, there’s a nifty project called Portable Ubuntu for Windows (in IE8 you might need Compatibility View for the site).  You can install it like a regular app, then just fire up Ubuntu within Windows whenever you like – you don’t even need a Virtual PC :)

I threw a few of my demos at it and it’s great to see soft-body physics running pretty well (it’s a bit slower than Silverlight, but I’m sure they’ll improve that before release).  Some of my other demos didn’t do so well – interestingly they were apps that used WebClient (or maybe HttpWebRequest) to request something from the server.  No doubt that’ll be fixed soon too :) (those Mono guys rock).

Here’s a screenshot (it’s Firefox on Linux, in a regular Vista window! :) ):

image

→ 3 CommentsCategories: .NET

“Is XML fragment” Regex / Extension Method

April 16, 2009 · 1 Comment

Here’s a small extension method to roughly determine if a string contains an XML fragment (it’s not foolproof, but is usually adequate):

private static Regex isXmlFragment = new Regex( @"<([^>]+)>(.*?</(\1)>|[^>]*/>)" );

public static bool IsXmlFragment( this string content )
{
    return isXmlFragment.IsMatch( content );
}

Of course you could just use the regular expression by itself if you prefer… :)

→ 1 CommentCategories: .NET

XPath (barely) in Silverlight 2

April 11, 2009 · 3 Comments

If you need to perform very simple element-only XPath queries in Silverlight 2.0, this extension method might help:

/// <summary>
/// Selects an <see cref="XElement"/> using a non-attributed Micro XPath (MXPath) expression.
/// </summary>
/// <remarks>This is a very simplistic Micro XPath (http://senzhang.netfirms.com/mxpath.htm) implementation.</remarks>
/// <param name="element">The element.</param>
/// <param name="xpath">The xpath.</param>
/// <returns>Returns the element, or null if not found.</returns>
public static XElement MMXPathSelectElement( this XContainer element, string xpath )
{
    return xpath.Split( '/' ).Aggregate( element, ( e, name ) => e.Element( name ) ) as XElement;
}

As you can see it only supports a simple element path (like root/child1/child2) so attributes and complex expressions won’t work, but you might still find a use for it (I did! :) ).

→ 3 CommentsCategories: .NET

Binding Dynamic Properties in XAML, ASP.NET…

April 6, 2009 · 6 Comments

Here’s a useful helper to build a dynamic wrapper / facade class from an arbitrary collection of properties.  The wrapper implements INotifiyPropertyChanged so can be used with two-way databinding in WPF, Silverlight and ASP.NET (probably other places too):

/// <summary>
/// IDynamicPropertyProvider interface
/// </summary>
public interface IDynamicPropertyProvider
{
    object GetPropertyValue( string name );
    void SetPropertyValue( string name, object value );
    event PropertyChangedEventHandler ValueChanged;
}

/// <summary>
/// DynamicTypeFactory class
/// </summary>
public class DynamicTypeFactory
{
    #region DynamicBase class

    /// <summary>
    /// Base for dynamic class
    /// </summary>
    public class DynamicBase : INotifyPropertyChanged, IDisposable
    {
        private IDynamicPropertyProvider provider;

        public DynamicBase( IDynamicPropertyProvider provider )
        {
            this.provider = provider;

            if ( provider != null ) provider.ValueChanged += OnPropertyChanged;
        }

        protected object GetValue( string name ) { return provider.GetPropertyValue( name ); }

        protected void SetValue( string name, object value )
        {
            provider.SetPropertyValue( name, value );
        }

        private void OnPropertyChanged( object sender, PropertyChangedEventArgs e )
        {
            if ( PropertyChanged != null ) PropertyChanged( this, e );
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            if ( provider != null ) provider.ValueChanged -= OnPropertyChanged;
        }

        #endregion
    }

    #endregion

    /// <summary>
    /// Creates the type.
    /// </summary>
    /// <param name="name">The name.</param>
    /// <param name="properties">The properties.</param>
    /// <returns></returns>
    public static Type CreateType( string name, IEnumerable<string> properties )
    {
        // Define dynamic assembly
        var assemblyName = new AssemblyName( "DynamicAssembly" );

        var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( assemblyName, AssemblyBuilderAccess.Run );
        var module = assemblyBuilder.DefineDynamicModule( "DynamicModule" );

        // Define type
        var typeBuilder = module.DefineType(
            name,
            TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,
            typeof( DynamicBase ) );

        var baseConstructor = typeof( DynamicBase ).GetConstructor( new[] { typeof( IDynamicPropertyProvider ) } );

        // Define constructor
        var constructor = typeBuilder.DefineConstructor(
            MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
            CallingConventions.Standard,
            new[] { typeof( IDynamicPropertyProvider ) } );

        var ilGen = constructor.GetILGenerator();
        ilGen.Emit( OpCodes.Ldarg_0 );
        ilGen.Emit( OpCodes.Ldarg_1 );
        ilGen.Emit( OpCodes.Call, baseConstructor );
        ilGen.Emit( OpCodes.Ret );

        // Prepare to define properties
        var getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
        var getMethodInfo = typeof( DynamicBase ).GetMethod( "GetValue", BindingFlags.NonPublic | BindingFlags.Instance );
        var setMethodInfo = typeof( DynamicBase ).GetMethod( "SetValue", BindingFlags.NonPublic | BindingFlags.Instance );

        foreach ( var property in properties )
        {
            // Define property get method
            var getMethod = typeBuilder.DefineMethod( "get_" + property, getSetAttr, typeof( object ), Type.EmptyTypes );
            ilGen = getMethod.GetILGenerator();
            ilGen.Emit( OpCodes.Ldarg_0 );
            ilGen.Emit( OpCodes.Ldstr, property );
            ilGen.Emit( OpCodes.Callvirt, getMethodInfo );
            ilGen.Emit( OpCodes.Ret );

            // Define property set method
            var setMethod = typeBuilder.DefineMethod( "set_" + property, getSetAttr, null, new[] { typeof( object ) } );
            ilGen = setMethod.GetILGenerator();
            ilGen.Emit( OpCodes.Ldarg_0 );
            ilGen.Emit( OpCodes.Ldstr, property );
            ilGen.Emit( OpCodes.Ldarg_1 );
            ilGen.Emit( OpCodes.Callvirt, setMethodInfo );
            ilGen.Emit( OpCodes.Ret );

            // Define property
            var propertyBuilder = typeBuilder.DefineProperty( property, PropertyAttributes.HasDefault, typeof( object ), Type.EmptyTypes );

            propertyBuilder.SetGetMethod( getMethod );
            propertyBuilder.SetSetMethod( setMethod );
        }

        // Materialize type
        return typeBuilder.CreateType();
    }
}

To use it you’ll need to implement the IDynamicPropertyProvider interface on your own “provider” class. Its job is to get and set a property value and raise “ValueChanged” events when values are changed (externally or by SetPropertyValue).

A basic provider could look something like this (see CreateDynamicWrapper for usage details):

public class MyDynamicProvider : IDynamicPropertyProvider
{
    public Dictionary<string, object> MyValues { get; private set; }

    public MyDynamicProvider()
    {
        MyValues = new Dictionary<string, object>();
    }

    public object CreateDynamicWrapper()
    {
        var type = DynamicTypeFactory.CreateType( "MyWrapper", MyValues.Keys.ToArray() );
        return Activator.CreateInstance( type, this );
    }

    public object GetPropertyValue( string name )
    {
        return MyValues[ name ];
    }

    public void SetPropertyValue( string name, object value )
    {
        MyValues[ name ] = value;
        if ( ValueChanged != null ) ValueChanged( this, new PropertyChangedEventArgs( name ) );
    }

    public event PropertyChangedEventHandler ValueChanged;
}

From there of course you can bind directly to the wrapper in XAML, ASP.NET or whatever and maybe avoid some typing :)

→ 6 CommentsCategories: .NET