JavaScript and CSS controls for ASP.NET

by Matt 21. February 2009 08:32

It’s often troublesome to link to JavaScript and CSS files in ASP.NET pages when the desired files can’t easily be accessed from the current path. For example, when setting up SyntaxHighlighter on this site, it seemed like good practice to not include the JS and CSS files in the BlogEngine.NET theme directory, but in the root of the application. After all, any theme should be able to access these files. ASP.NET provides the root operator for just this purpose. Unfortunately, it doesn't work with client-side markup, and there are no server-side controls for styles or scripts. One solution is to resolve the path manually:

<script type="text/javascript" src='<%= ResolveUrl("~/path/to/script.js") %>'></script>

...but that's not particularly clean. A better way is to create a control specifically for this purpose, so one can do the following:

<controls:JavaScript Src="~/path/to/script.js" runat="server" />
<controls:Css Href="~/path/to/style.css" runat="server" />

The source for these controls is really simple. The JS and CSS controls contain the Src and Href properties to be similar to their respective markup. The JS control:

public class JavaScript : Control
{
    public String Src { get; set; }

    protected override void Render(HtmlTextWriter writer)
    {
        writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
        writer.AddAttribute(HtmlTextWriterAttribute.Src, ResolveUrl(Src));
        writer.RenderBeginTag(HtmlTextWriterTag.Script);
        writer.RenderEndTag();
    }
}

And the CSS control:

public class Css : Control
{
    public String Href { get; set; }

    protected override void Render(HtmlTextWriter writer)
    {
        writer.AddAttribute(HtmlTextWriterAttribute.Rel, "stylesheet");
        writer.AddAttribute(HtmlTextWriterAttribute.Type, "text/css");
        writer.AddAttribute(HtmlTextWriterAttribute.Href, ResolveUrl(Href));
        writer.RenderBeginTag(HtmlTextWriterTag.Link);
        writer.RenderEndTag();
    }
}

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

Tips and Tricks

An indexed foreach method for C#

by Matt 18. February 2009 23:37

Foreach loops are rather convenient. In most cases, it's cleaner to write:

var list = new List { 5, 2, 3, 10, 9 };
foreach(var item in list)
{
    doSomething(item);
}

Rather than:

var list = new List<int> { 5, 2, 3, 10, 9 };
for(int i = 0; i < list.Count; i++)
{
    doSomething(list[i]);
}

...which only works with collections that implement ICollection. Say you wanted to perform some action that required a reference to the item and an index or counter associated with the item. You'd have a separate counter variable, which is slightly messy:

var list = new List<int> { 5, 2, 3, 10, 9 };
int count = 0;
foreach(var item in list)
{
    Console.WriteLine(String.Format("Item {0} has index of {1}.", item, count));
    count++;
}

Using extension methods in C# 3.0, we can extend IEnumerable to have an "indexed" foreach that supplies us with an index. The following extension method lets us do just that.

public static void ForEach<T>(this IEnumerable<t> self, Action<T, int> action)
{
    int index = 0;
    foreach (var item in self)
    {
        action(item, index);
        index++;
    }
}

Then, our foreach can be rewritten:

var list = new List<int> { 5, 2, 3, 10, 9 };
list.ForEach(delegate(int item, int index)
{
    Console.WriteLine(String.Format("Item {0} has index of {1}.", item, index));
});

It can be made more compact using lambda expressions:

var list = new List<int> { 5, 2, 3, 10, 9 };
list.ForEach((item, index) =>
{
    Console.WriteLine(String.Format("Item {0} has index of {1}.", item, index));
});

We can also define an extension method for the non-generic IEnumerable interface:

public static void ForEach(this IEnumerable self, Action<Object, int> action)
{
    int index = 0;
    foreach (var item in self)
    {
        action(item, index);
        index++;
    }
}

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , , ,

Tips and Tricks

Using IShellItemImageFactory to get thumbnails on Vista

by Matt 28. July 2007 01:06

In Vista, Explorer produces really nice thumbnails of your files. It will generate a preview for supported file types, such as images, PDFs, Word documents and others. If it's not a type that has a preview, it will show its associated icon.

Vista thumbnails

I’ve been working on an experimental dock application for Vista, and I needed a way to get the icon of the files the user drags onto the dock. At first I looked at the Icon.ExtractAssociatedIcon method in System.Drawing, but it gave a 48x48 XP-style icon at best, which is unacceptable for a dock. I needed Vista’s 256x256 icons and nothing less. I began poking around the Vista APIs and found the IThumbnailCache COM interface. The only problem was that it could only retrieve a thumbnail, not an icon. Eventually I found IShellItemImageFactory which gets a thumbnail when possible and reverts to a fancy Vista icon when not.

I don’t know that much about COM, so it took me a while, but I managed to make everything fit together. The C# definition of IShellItemImageFactory looks like so:

[ComImportAttribute()]
[GuidAttribute("bcc18b79-ba16-442f-80c4-8a59c30c463b")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IShellItemImageFactory
{
    void GetImage(
        [In, MarshalAs(UnmanagedType.Struct)] SIZE size,
        [In] SIIGBF flags,
        [Out] out IntPtr phbm);
}

It seems that the IShellItem implementation used in Vista also implements IShellItemImageFactory, so to get a thumbnail, all you need to do is get an IShellItem for your desired item, cast it to IShellItemImageFactory and call GetImage. The easiest way to get an IShellItem for a given path is to use SHCreateItemFromParsingName. From there you can use the Imaging.CreateBitmapSourceFromHBitmap method in System.Windows.Interop to create a WPF BitmapSource object (for use with a WPF Image control) from the IntPtr pointing to the GDI+ bitmap returned by GetImage. Or, if you aren’t using WPF, you can use whatever other methods available in System.Drawing or elsewhere to convert your shiny new thumbnail to a more usable format.

Using the code below, you should be able to get beautiful thumbnails like these in your WPF app in no time!

Thumbnails in WPF

The following class, ThumbnailGenerator, and supporting COM interface definitions, structs and enums were either defined by me or taken from the wonderful PInvoke.net. All the interop stuff I discovered while working on this has since been posted on PInvoke.net. The following code is from my Vista Toolbelt project, which is just my collection of useful Vista, WPF and related classes and code. Feel free to use it or modify it however you like. I haven’t tested it extensively, but there’s not much too it, so I don’t foresee any problems. Should you find something though, please let me know!

ThumbnailGenerator.cs:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows;
using VistaToolbelt.Interop.Native;


namespace VistaToolbelt.Shell
{
    /// <summary>
    /// Assists in generating thumbnails for shell items.
    /// </summary>
    public static class ThumbnailGenerator
    {
        /// <summary>
        /// Generates an Explorer-style thumbnail for any file or shell item. Requires Vista or above.
        /// </summary>
        /// <param name="filename" />The filename of the item.</param>
        /// <returns>The thumbnail of the item.</returns>
        public static ImageSource GenerateThumbnail(String filename)
        {
            IShellItem ppsi = null;
            IntPtr hbitmap = IntPtr.Zero;
            Guid uuid = new Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe");

            // Create an IShellItem from the filename.
            UnsafeNativeMethods.SHCreateItemFromParsingName(filename, IntPtr.Zero, uuid, out ppsi);

            // Get the thumbnail image.
            ((IShellItemImageFactory)ppsi).GetImage(new SIZE(256, 256), 0x0, out hbitmap);

            // Convert from GDI HBITMAP to WPF BitmapSource.
            BitmapSource source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                hbitmap,
                IntPtr.Zero,
                Int32Rect.Empty,
                System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
                
            // Release COM stuff to avoid memory leaks.
            Marshal.ReleaseComObject(ppsi);
            Marshal.Release(hbitmap);
            return source;
        }
    }
}

NativeInterop.cs:

using System.Collections.Generic;
using System.Text;
using System.Security;
using System.Runtime.InteropServices;
using System;
namespace VistaToolbelt.Interop.Native
{
    [StructLayout(LayoutKind.Sequential)]
    public struct SIZE
    {
        public int cx;
        public int cy;
        public SIZE(int cx, int cy)
        {
            this.cx = cx;
            this.cy = cy;
        }
    }

    public enum SIGDN : uint
    {
        NORMALDISPLAY = 0,
        PARENTRELATIVEPARSING = 0x80018001,
        PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
        DESKTOPABSOLUTEPARSING = 0x80028000,
        PARENTRELATIVEEDITING = 0x80031001,
        DESKTOPABSOLUTEEDITING = 0x8004c000,
        FILESYSPATH = 0x80058000,
        URL = 0x80068000
    }

    [Flags]
    public enum SIIGBF
    {
        SIIGBF_RESIZETOFIT = 0x00,
        SIIGBF_BIGGERSIZEOK = 0x01,
        SIIGBF_MEMORYONLY = 0x02,
        SIIGBF_ICONONLY = 0x04,
        SIIGBF_THUMBNAILONLY = 0x08,
        SIIGBF_INCACHEONLY = 0x10,
    }

    [ComImport]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
    public interface IShellItem
    {
        void BindToHandler(IntPtr pbc,
        [MarshalAs(UnmanagedType.LPStruct)]Guid bhid,
        [MarshalAs(UnmanagedType.LPStruct)]Guid riid,
        out IntPtr ppv);
        void GetParent(out IShellItem ppsi);
        void GetDisplayName(SIGDN sigdnName, out IntPtr ppszName);
        void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
        void Compare(IShellItem psi, uint hint, out int piOrder);
    };

    [ComImportAttribute()]
    [GuidAttribute("bcc18b79-ba16-442f-80c4-8a59c30c463b")]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IShellItemImageFactory
    {
        void GetImage(
            [In, MarshalAs(UnmanagedType.Struct)] SIZE size,
            [In] SIIGBF flags,
            [Out] out IntPtr phbm);
    }

    [SuppressUnmanagedCodeSecurity]
    public static class UnsafeNativeMethods
    {
        [DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
        public static extern void SHCreateItemFromParsingName(
        [In][MarshalAs(UnmanagedType.LPWStr)] string pszPath,
        [In] IntPtr pbc,
        [In][MarshalAs(UnmanagedType.LPStruct)] Guid riid,
        [Out][MarshalAs(UnmanagedType.Interface, IidParameterIndex = 2)] out IShellItem ppv);
    }
}

Currently rated 5.0 by 3 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , , ,

Windows API