Wiki

SharpKit 5

What's New

  • C# debugging support in Google Chrome! (See instructions below)
  • Brand new mono-based C# code and assembly parser (NRefactory and Cecil)
  • Basic yield support - using eager list addition (not using state machine yet)
  • Automatic inlining of compile-time values - consts, string concatenations, etc...
  • C# style params array support
  • Parallel execution
  • Getting Started

    Create a new project by opening Visual Studio and create a new "SharpKit 5 Web Application" project.

    Service Mode

    SharpKit can now run as a local HTTP service, inside a windows service container, or in console mode. You can find the windows service by running "services.msc" and finding a service named SharpKit. If this service is stopped, SharpKit will revert to command line execution mode.

    To start and stop the SharpKit Windows Service:

    Run: services.msc

    Find the service, click start / stop.

    It is also possible to run the service manually in console mode using the following command:

    skc5.exe /service:console
    

    SharpKit is hosted on port 7667 by default, it is possible to execute GET command on it, with the same command line arguments format:

    http://localhost:7667/Compile?CommandLineArgs=/why

    Any command line argument can be set in the CommandLineArgs query string parameter, this specific command may help you debug license validation issues.

    Debugging

    Debugging of C# code from within the browser is currently supported in Google Chrome only.

    A demo of debugging is available in our open-source SVN repository, named: SharpKitSourceMappingSample

    http://code.google.com/p/sharpkit/source/checkout

    To enable debugging of C# code, please follow these steps:

  • In your AssemblyInfo.cs file, add the following code:
  • [JsExport(GenerateSourceMaps = true)]
    
  • Add a reference to SharpKit.Web assembly (found in .NET references dialog, or in the SharpKit program files folder)
  • Add the SourceMaps handler in your web.config file:
  • <add name="SourceMapsHandler" type="SharpKit.Web.Server.Handlers.SourceMapsHandler, SharpKit.Web" verb="*" path="SourceMaps.ashx" />
    
  • Enable source maps in chrome: show development bar, click options wheel, check enable source maps.
  • Build your project
  • View in Google Chrome browser
  • Debug!
  • Upgrading from SharpKit v4 to SharpKit v5

    Upgrading from SharpKit v4 to v5 is very simple, and can be easily reverted back.

    Before upgrading, check-in / commit your code, after upgrade, build your project and compare your newly generated js files with your previous ones.

    Edit your .csproj file, replace the SharpKit v4 import line, with the SharpKit v5 import line:

    WebApp.csproj
    <!--Replace this line:-->
    <Import Project="$(MSBuildBinPath)\SharpKit\4\SharpKit.Build.targets" />
    <!--With this line:-->
    <Import Project="$(MSBuildBinPath)\SharpKit\5\SharpKit.Build.targets" />
    

    Troubleshooting

    Parallel Execution

    Parallel execution can be disabled in skc5.exe.config file, set appSettings "Parallel" to "false".

    License Validation Issues

    If you're running an activated version of SharpKit, but still getting license errors, run the following:

    skc5.exe /why
    

    Since SharpKit runs as a different user when in windows service, it's best to run it as an HTTP request while the service is running:

    http://localhost:7667/Compile?CommandLineArgs=/why

    Check the filename of the license, if it doesn't exist, run the /why in command line, and copy the file there.

    Support

    If you run into any issues using SharpKit 5, please email us or post in our forum.

    Integrating SharpKit

    This chapter will explain how to start using SharpKit in new or existing projects, SharpKit easily integrates into any project type, whether it's a web application, class library or even console application.

    Creating a new project with SharpKit

  • Open Visual Studio
  • Click File -> New Project
  • Select Visual C# -> SharpKit -> SharpKit Web Application and click Ok.
  • SharpKit enabled ASP.NET web application will be created with an aspx page, and a C# code-behind with a class that is converted to JavaScript during compilation.

    Integrating SharpKit into an existing project

    Add the following line on your .csproj file: (add it right after all 'Import' sections in your project file)

    WebApp.csproj
    <!--This line is in any .csproj file: -->
    <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
    <!--Add this line after all Import sections: -->
    <Import Project="$(MSBuildBinPath)\SharpKit\5\SharpKit.Build.targets" />
    

    Add the following references into your project:

  • SharpKit.JavaScript.dll (you must add this reference)
  • SharpKit.Html.dll
  • SharpKit supports any C# type project, including web, console (.exe) and class library (.dll) project types.

    Upgrading from SharpKit v2 to SharpKit v5

    Edit your .csproj file, replace the SharpKit v2 import line, with the SharpKit v4 import line:

    WebApp.csproj
    <!--Replace this line:-->
    <Import Project="$(MSBuildBinPath)\SharpKit\SharpKit.Build.targets" />
    <!--With this line:-->
    <Import Project="$(MSBuildBinPath)\SharpKit\5\SharpKit.Build.targets" />
    

    Update assembly references, make sure to update SharpKit.JavaScript.dll, never reference both assemblies. SharpKit v4 assemblies are located in your Program Files\SharpKit\4\ folder.

    Upgrading from SharpKit v4 to SharpKit v5

    Edit your .csproj file, replace the SharpKit v4 import line, with the SharpKit v5 import line:

    WebApp.csproj
    <!--Replace this line:-->
    <Import Project="$(MSBuildBinPath)\SharpKit\4\SharpKit.Build.targets" />
    <!--With this line:-->
    <Import Project="$(MSBuildBinPath)\SharpKit\5\SharpKit.Build.targets" />
    

    Using SharpKit

    In this chapter you will learn about the different SharpKit C# to JavaScript conversion modes. Each mode has a unique set of rules designed to help you achieve any type of JavaScript code, while remaining in a fully typed, valid C#.

    Writing your first class

    To convert a class into JavaScript, all you have to do is to decorate your class with a JsType attribute. There are several modes in which SharpKit can convert C# to JavaScript, by setting the JsMode parameter on the JsType attribute.

    Create a new class and add the following code:

    Site.cs
    [JsType(JsMode.Global, Filename="Site.js")]
    class Site : HtmlContext
    {
        public static void btnHello_click(HtmlDomEventArgs e)
        {
               document.body.appendChild(document.createTextNode("Hello SharpKit!"));
        }
    }
    
    Site.js
    function btnHello_click(e)
    {
        document.body.appendChild(document.createTextNode("Hello SharpKit!"));
    }
    
    Site.htm
    <html>
        <head>
            <script src="Site.js"></script>
        </head>
        <body>
            <button onclick="btnHello_click(event);">Click me</button>
        </body>
    </html>
    

    The JsType attribute will cause SharpKit to convert the class Site into the file Site.js, you can see the js file included in the htm file.

    Global Mode

    A class decorated with a JsType(JsMode.Global) attribute will be converted in the following rules:

    Static methods are converted to global functions (named functions)Static fields are converted to global variablesStatic constructor is converted to global code

    Global.cs
    [JsType(JsMode.Global, Filename="Global.js")]
    class Global
    {
        static JsNumber MyNumber = 0;
        static Global()
        {
            HelloWorld();
        }
        public static void HelloWorld()
        {
            document.body.appendChild(document.createTextNode("Hello SharpKit!"));
        }
    }
    
    Global.js
    var MyNumber = 0;
    HelloWorld();
    function HelloWorld()
    {
        document.body.appendChild(document.createTextNode("Hello SharpKit!"));
    }
    

    Although it seems that the C# and JavaScript code is similiar, notice that it's fully typed. Write this code in a visual studio project, and hover with your mouse over the token 'document' or 'createTextNode' and see the documentation. Any attempt to modify the code to an unexisting method will result in compilation error.

    Prototype Mode

    A class decorated with a JsType(JsMode.Prototype) attribute will be converted in the following rules:

    Constructor is converted into a JavaScript constructor functionInstance methods are converted into prototype functionsStatic methods are converted into functions on the constructor function

    Contact.cs
    [JsType(JsMode.Prototype, Filename="Contact.js")]
    class Contact
    {
        public void Call()
        {
        }
        public static Contact Load()
        {
             return null;
        }
    }
    
    Contact.js
    Contact = function()
    {
    }
    Contact.prototype.Call = function()
    {
    }
    Contact.Load = function()
    {
        return null;
    }
    

    Json Mode

    A class decorated with a JsType(JsMode.Json) attribute will not be exported at all, it will give you the ability to use JSON notation on typed classes.

    MyOptions.cs
    [JsType(JsMode.Json)]
    class MyOptions
    {
        public JsString Name { get; set; }
        public bool IsCool { get; set; }
    }
    
    MyPage.cs
    [JsType(JsMode.Global, Filename="MyPage.js")]
    class MyPage
    {
        public static void Main()
        {
            var options = new MyOptions { Name="MyName", IsCool=true };
        }
    }
    
    MyPage.js
    function Main()
    {
        var options = {Name:"MyName", IsCool:true};
    }
    

    This technique is very useful on constructor Options class, for example, jQuery.ajax(options) function. It also gives you the ability to share web service data contracts between client and server. Simply mark your data contract classes with the JsType(JsMode.Json) and you'll be able to use them in a C# to JavaScript context.

    Using Web Libraries with SharpKit

    SharpKit supports any JavaScript based library, in order to use a library with SharpKit, you must obtain a C# 'header' file of the library. A C# header file contains all the classes and methods that a certain library offers. The methods in this file have no implementation, and are not converted into JavaScript, they simply give you the ability use them from C#. You can use these files by adding them to your project, or by referencing compiled assembly version of them.

    To make things easy, we have created a googlecode project called SharpKit SDK, which contains open-source C# header files for all popular web libraries such as: jQuery, ASP.NET Ajax, ExtJS, jQTouch and more... These libraries are maintained by us and by members of the SharpKit community, if you'd like to contribute, let us know. You should know that all of these libraries are also included in the SharpKit installer, and are available on your SharpKit Program Files folder.

    Using jQuery

    In order to use jQuery, you'll need to add a reference to SharpKit.jQuery assembly, or include the SharpKit.jQuery cs file in your project. There's a header file and an assembly for each version of jQuery. You'll also have to add jQuery js file to pages that use it.

    HelloWorld.htm
    <html>
        <head>
            <title>Hello World</title>
            <script src="res/jquery-1.4.4.min.js" type="text/javascript"></script>
            <script src="res/HelloWorld.js" type="text/javascript"></script>
            <script>            $(HelloWorldClient_Load);</script>
        </head>
        <body>
            <button>Click me</button>
        </body>
    </html>
    
    HelloWorld.cs
    using SharpKit.JavaScript;
    using SharpKit.Html;
    using SharpKit.jQuery;
    
    namespace SharpKitWebApp
    {
        [JsType(JsMode.Global, Filename = "~/res/HelloWorld.js")]
        public class HelloWorldClient : jQueryContext
        {
            static void HelloWorldClient_Load()
            {
                //J() is instead of $() which is not allowed in C#
                J("button").click(e => alert("Hello world"));
            }
        }
    }
    
    HelloWorld.js
    function HelloWorld_Load()
    {
        $("button").click(function(e){ return alert("Hello world")});
    }
    

    Writing your own Header Files

    There are situations in which you will have to write your own header files, this happens in two common scenarios:

  • You use a custom library that is not available in SharpKit SDK
  • You haven't converted all of your current JavaScript code into C#, but you still need to use this code from C#.
  • In order to create a header file, all you have to do is to create empty implementation of the needed classes and members, decorate them with a JsTypeAttribute in the proper mode, and prevent these classes of being exported.

    To prevent C# class with JsTypeAttribute from being exported into JavaScript, use the JsTypeAttribute.Export property.

    MyExternalLib.cs
    [JsType(JsMode.Prototype, Name="MyExternalLibrary", Export=false)]
    class MyExternalLibrary
    {
        public static void DoSomething(){}
    }
    [JsType(JsMode.Global, Export=false)]
    class MyExternalFunctions
    {
        public static void DoSomethingElse(){}
    }
    
    MyPage.cs
    [JsType(JsMode.Global, Filename="MyPage.js")]
    class MyPage
    {
        public static void Main()
        {
            MyExternalLibrary.DoSomething();
            MyExternalFunctions.DoSomethingElse();
        }
    }
    
    MyPage.js
    function Main()
    {
        MyExternalLibrary.DoSomething();
        DoSomethingElse();
    }
    

    Please note that the JsTypeAttribute.Mode and Name properties are very important, even if you're not exporting this code. These attributes are used by SharpKit compiler to generate the proper code when you refer to these classes. The JsType(Name="MyExternalLibrary") is very important, since it tells SharpKit to ignore this type's namespace and name, and use the specified name instead.

    Applying Metadata

    SharpKit uses Custom Attributes to control and customize JavaScript code generation behavior. These attributes are located in the SharpKit.JavaScript namespace, every type and member in C# has its own attribute, like: JsTypeAttribute, JsMethodAttribute, JsPropertyAttribute and JsFieldAttribute. Attributes can be applied directly on a type / member like this:

    Grid.cs
    [JsType(JsMode.Prototype)]
    public class Grid
    {
        [JsMethod(Name="render")]
        public void Render()
        {
        }
        [JsProperty(Name="element")]
        public HtmlElement Element { get;set; }
    }
    
    Grid.js
    Grid = function()
    {
    }
    Grid.prototype.render = function()
    {
    }
    

    Attributes can also be applied externally, on types / members within a project, or even on external referenced types:

    AssemblyInfo.cs
    [assembly: JsType  (TargetType = typeof(Grid),   Mode=JsMode.Prototype)]
    [assembly: JsMethod(TargetType = typeof(Grid),   TargetMethod="Render",      Name = "render")]
    [assembly: JsMethod(TargetType = typeof(string), TargetMethod = "Substring", Name = "substr", NativeOverloads = true)]
    

    In the example above we can see that it is possible to mark external types such as System.String, and allow SharpKit to generate proper code when it is used. In this case, if anyone uses the Substring() method, it will be generated as substr() in JavaScript.

    JsTypeAttribute

    JsTypeAttribute is the most basic and important attribute in SharpKit, it marks any type that should be exported to JavaScript in an "opt-in" style, just like DataMemberAttribute in WCF. JsTypeAttribute has many options that control code generation behavior, but in order to make things more simple, we have created 4 templates, or, modes, in which all options are set to a default value according to popular usage types. So, setting a JsType to Prototype mode:

    Tree.cs
    [JsType(JsMode.Prototype)]
    public class Tree
    {
    }
    

    Is the equivalent of setting all of these flags:

    Tree.cs
    [JsType(Native                        = true,
            NativeOverloads               = true,
            NativeDelegates               = true,
            AutomaticPropertiesAsFields   = true,
            NativeConstructors            = true,
            NativeEnumerator              = true,
            NativeFunctions               = true,
            NativeJsons                   = true,
            IgnoreGenericTypeArguments    = true,
            IgnoreGenericMethodArguments  = true,
            InlineFields                  = true)]
    public class Tree
    {
    }
    

    Available modes are: Global, Prototype, Json and Clr. You can read more about these modes here.

    Performing Optimizations

    There are two very popular techniques for optimizing JavaScript and CSS files - consolidation and minification. The JsMergedFileAttribute allows you to do both:

    AssemblyInfo.cs
    [assembly: JsMergedFile(Filename="MySite.js",     Sources = new string[] { "jQuery.js", "MyPage1.js", "MyPage2.js" })]
    [assembly: JsMergedFile(Filename="MySite.min.js", Sources = new string[] { "MySite.js" }, Minify = true)]
    

    In this code example, a consolidated file named MySite.js will be created, containing all code from jQuery.js, MyPage1.js and MyPage2.js files. The second attribute, takes the previously consolidated file MySite.js, and generates a minified version of it in MySite.min.js. The same rules apply to css files, if the file extension is '.css' the file will be minified in CSS rules minification algorithm.

    Writing Native JavaScript

    SharpKit allows you to write native JavaScript code, just as if you've written it yourself. Although C# and JavaScript share many similarities, there are differences as well. This section will explain you how to bridge the gap between the two worlds, and allow you to enjoy the freedom of JavaScript, while retaining the type-checking and validity of C#.

    Primitive types

    Every JavaScript primitive type has an equivalent C# type. In order to avoid ambiguity with native C# types, a "Js" prefix is added to each JavaScript primitive type. For example the String object is named JsString in C#, Number is named JsNumber and so on...

    Although a class is named JsString in C#, when it is converted to JavaScript, it will be written as String. This is achieved by using the JsTypeAttribute.Name property:

    JsString.cs
    [JsType(JsMode.Prototype, Name="String", Export=false)]
    public class JsString
    {
    }
    
  • JsObject
  • JsArray
  • JsString
  • JsNumber
  • JsDate
  • Keywords

    All of JavaScript keywords are located in a single class called JsContext, you can either call the methods on it by qualifying them with the class name, e.g.: JsContext.@typeof(obj); Or, you can simply inherit from the JsContext class and use the methods directly without any prefix.

    The verbatim (@) literal is used in C# to disambiguate between reserved C# keywords and other member / variable names. The 'typeof' method for example is a keyword in C#, so in order to disamibuate it with the JavaScript 'typeof' function, the verbatim (@) literal is needed.

  • @this
  • @typeof
  • @return
  • arguments
  • eval
  • delete
  • instanceof
  • Casting between Types

    The As<T>() extension method, located in the SharpKit.JavaScript namespace, helps you to cast from one type to another without affecting the generated JavaScript code. for example:

    MyPage.cs
    using SharpKit.JavaScript;
    using SharpKit.Html;
    
    [JsType(JsMode.Global, Filename="MyPage.js")]
    class MyPage : HtmlContext
    {
        public static void Main()
        {
            var input = document.getElementById("input1").As<HtmlInputElement>();
            input.value = "MyValue";
        }
    }
    
    MyPage.js
    function Main()
    {
        var input = document.getElementById("input1");
        input.value = "MyValue";
    }
    

    Building a Client-Side Grid Control using SharpKit

    Introduction

    This sample code will show you how to write a client-side grid control using SharpKit. SharpKit is a tool that allows you to write C# code and convert it JavaScript during compilation. This process helps you write code much faster, and with less errors, it also helps you to document your code so other developers can use it more easily. So, to avoid confusion, all the code here will be shown in C#, but in fact, it is standard JavaScript code, and can be used with / without SharpKit afterwards.

    We'll start by learning how to implement the Grid, then move on to using the Grid, and finally, learn how to optimize DOM manipulation to support legacy browsers.

    Implementing the Grid

    Grid Class

    Grid.cs
    [JsType(JsMode.Prototype, Filename = "Grid.js")]
    public class Grid : HtmlContext
    {
        public Grid()
        {
                Rows = new JsArray<GridRow>();
        }
        public HtmlElement Element { get; set; }
        public HtmlElement GridBody { get; set; }
        public JsArray<GridRow> Rows { get; set; }
        public void Render()
        {
            if (Element == null)
                return;
            Element["_Grid"] = this;
            if (GridBody == null || GridBody.nodeName != "TBODY")
            {
                GridBody = document.createElement("TBODY");
                Element.appendChild(GridBody);
            }
        }
    }
    

    This Grid class is designed to be used in the following pattern:

    GridDemo.cs
    var grid = new Grid { Element = document.getElementById("MyGrid") };
    grid.Render();
    

    GridRow Class

    The grid class has a collection of Rows where each row is of type GridRow. GridRow is a json class, which means that it's used only to contain data about the row, in our case we will contain a reference to the table row element (a TR element), and a Data property that associates the row with some data object.

    GridRow.cs
    [JsType(JsMode.Json)]
    public class GridRow
    {
        public HtmlElement Element { get; set; }
        public object Data { get; set; }
    }
    

    The GridRow class is designed to be used in the following pattern:

    GridDemo.cs
    var row = new GridRow { Element = grid.CreateRow(document.getElementById("MyGridRowTemplate")) };
    grid.AddRow(row);
    

    Adding a Row

    Now it's time to implement an AddRow method, that will add a GridRow to our Rows collection, and append it to the DOM.

    Grid.cs
    public void AddRow(GridRow gr)
    {
        var body = GridBody;
        body.appendChild(gr.Element);
        gr.Element["_GridRow"] = gr;
        Rows.push(gr);
    }
    

    Creating a Row element from template

    Grid.cs
    public HtmlElement CreateRowElement(HtmlElement template)
    {
        return template.cloneNode(true);
    }
    

    This method simply clones an element, in our case this will be TR element to be cloned for each row in the grid.

    Using the Grid

    Now, we're ready to use the grid:

    GridDemo.htm
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>Grid Demo - SharpKitSamples</title>
        <link href="Grid.css" rel="stylesheet" type="text/css" />
        <script src="res/jquery-1.6.4.min.js" type="text/javascript"></script>
        <script src="Grid.js" type="text/javascript"></script>
        <script src="GridDemo.js"></script>
        <script>$(Load);</script>
    </head>
    <body>
    <h1>Grid Demo</h1>
        <table id="MyGrid" class="Grid">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Age</th>
                    <th>Phone Number</th>
                    <th>Description</th>
                </tr>
            </thead>
            <tbody style="display: none">
                <tr id="MyGridRowTemplate">
                    <td class="CellName"></td>
                    <td class="CellAge"></td>
                    <td class="CellPhoneNumber"></td>
                    <td class="CellDescription"></td>
               </tr>
            </tbody>
        </table>
    </body>
    </html>
    

    This html file contains a TABLE element, for the grid, with some headers and a row template. We can clone this row to create new rows in the grid. You can also notice the $(Load) code which is in fact the jQuery ready event, this means that when the DOM is ready the Load() function will be invoked.

    GridDemo.cs
    [JsType(JsMode.Global, Filename = "GridDemo.js")]
    public class GridDemoClient : jQueryContextBase
    {
        public static void Load()
        {
            var list = new JsArray<Contact>();
    
            for (var i = 0; i < 30; i++)
            {
                var c = new Contact { Name = "MyContact" + i, Age = i, PhoneNumber = "44557799" + i, Description="This is a contact "+i };
                list.push(c);
            }
            var grid = new Grid { Element = document.getElementById("MyGrid") };
            grid.Render();
            foreach (var c in list)
            {
                var row = new GridRow { Element = grid.CreateRow(document.getElementById("MyGridRowTemplate")), Data = c };
                var tr = J(row.Element);
                tr.find(".CellName").text(c.Name);
                tr.find(".CellPhoneNumber").text(c.PhoneNumber);
                tr.find(".CellAge").text(c.Age.ToString());
                tr.find(".CellDescription").text(c.Description);
                grid.AddRow(row);
    
            }
        }
    }
    

    In this code, we're generating 30 sample contacts, then we create GridRow objects for them, we also create TR elements cloned from our template and bind the data from the contact instance to each row.

    Implementing Sorting

    With a solid grid API, it's easy to implement a feature like sorting, all we want to do is to clear the rows in the grid, sort them, and render them back to the grid.

    GridDemo.cs
    public static void SortByName()
    {
        var rows = Grid.Rows.OrderBy(t => t.Data.As<Contact>().Name);
        Grid.DeleteAllRows();
        foreach (var row in rows)
            Grid.AddRow(row);
    }
    

    OrderBy method is a custom extension method implemented in a small utility class:

    JsArrayExtensions.cs
    [JsType(JsMode.Prototype, Filename = "GridDemo.js")]
    static class JsArrayExtensions
    {
        public static JsArray<T> OrderBy<T>(this JsArray<T> array, JsFunc<T, object> selector, bool desc)
        {
            var array2 = array.slice(0);
            if (!desc)
                array2.sort((x, y) => Compare(selector(x), selector(y)));
            else
                array2.sort((x, y) => CompareDesc(selector(x), selector(y)));
            return array2;
        }
        static JsNumber Compare(object x, object y)
        {
            var xx = x.As<int>();
            var yy = y.As<int>();
            if (xx > yy)
                return 1;
            if (xx < yy)
                return -1;
            return 0;
        }
    }
    

    This is a simplified LINQ sorting implementation on JavaScript arrays, it is implemented using extension methods, to make code usage easier and more readable. It is still converted to plain JavaScript by SharpKit.

    The next step is to support sorting by any property and remember the last sorting we've done, to toggle between Ascending and Descending sorting. We should also change the look of the currently sorted column, so the user will understand that the grid is sorted.

    Grid.cs
    static Grid Grid;
    static HtmlTableCell LastSortHeader;
    static JsString LastSort;
    static bool IsLastSortDescending;
    public static void SortBy(HtmlTableCell header, JsString pe)
    {
        J(LastSortHeader).removeClass("Sorted").removeClass("Descending");
        IsLastSortDescending = LastSort == pe && !IsLastSortDescending;
        LastSort = pe;
        LastSortHeader = header;
        J(LastSortHeader).addClass("Sorted");
        J(LastSortHeader).toggleClass("Descending", IsLastSortDescending);
    
        var rows = Grid.Rows.OrderBy(t => t.Data.As<JsObject>()[pe], IsLastSortDescending);
        Grid.DeleteAllRows();
        foreach (var row in rows)
            Grid.AddRow(row);
    }
    

    Now all that's missing is to call the SortBy method when clicking the grid headers:

    GridDemo.htm
    <table id="MyGrid" class="Grid">
        <thead>
            <tr>
                <th onclick="SortBy(this, 'Name');">Name</th>
                <th onclick="SortBy(this, 'Age');">Age</th>
                <th onclick="SortBy(this, 'PhoneNumber');">Phone Number</th>
                <th onclick="SortBy(this, 'Description');">Description</th>
            </tr>
        </thead>
        <tbody style="display: none">
            <tr id="MyGridRowTemplate">
                <td class="CellName"></td>
                <td class="CellAge"></td>
                <td class="CellPhoneNumber"></td>
                <td class="CellDescription"></td>
            </tr>
        </tbody>
    </table>
    

    That's it, simple, straightforward, fast and highly customizable.

    Optimizing the Grid

    Legacy browsers can slow, sometimes a simple jQuery usage can result in hard performance penalties in browsers like IE7, sometimes you have to get your hands dirty and implement optimized DOM APIs of your own. SharpKit allows me to do this easily by implementing extension methods. It makes the code easy to read and easy to maintain.

    GridExtensions.cs
    [JsType(JsMode.Prototype, Filename = "Grid.js")]
    static class Extensions
    {
        public static void AppendChildFast(this HtmlElement el, HtmlElement newElement, HtmlElement lastChild)
        {
            if (lastChild != null && SupportsInsertAdjacentElement)
                lastChild.insertAdjacentElement("afterEnd", newElement);
            else
                el.appendChild(newElement);
        }
    }
    

    Now, I can use el.AppendChildFast(), as if it was a method on HtmlElement, when in fact, it's a static extension method. About this method, in old browsers like IE7, appendChild() can be very slow, since the browser iterates over all the siblings until it reaches the end in order to add the element. This method gets a pointer to the lastChild and uses insertAdjacentElement method to add the element without this overhead.

    Points of Interest

    In conclusion, writing JavaScript code can sometimes be complicated, using SharpKit allows us to focus on writing the code, and perform refactorings and cleanups afterwards. It's also possible to add xml documentation and generate a help file to allow other developers to easily integrate your component.

    Extending SharpKit

    SharpKit is an extensible compiler, with plugin support, this means that it is possible to implement a plugin and add new features.

    Compiler plugins can add and customize C# and JavaScript code, or even support conversion to a different target language.

    Writing your first plugin

    In order to write a plugin, follow these steps:

  • Create a new .NET 4 library project (dll)
  • Add a refernece to skc5.exe (found in C:\Windows\Microsoft.NET\Framework\v4.0.30319\SharpKit\5\)
  • Add the following code:
  • MyPlugin.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using SharpKit.Compiler;
    
    namespace MySharpKitPlugin
    {
        public class MyPlugin : ICompilerPlugin
        {
            public ICompiler Compiler { get; set; }
            public void Init(ICompiler compiler)
            {
                Compiler = compiler;
                Compiler.AfterParseCs += new Action(Compiler_AfterParseCs);
                Compiler.BeforeSaveJsFiles += new Action(Compiler_BeforeSaveJsFiles);
            }
    
            void Compiler_AfterParseCs()
            {
                Console.WriteLine("MyPlugin: AfterParseCs: Hello");
            }
    
            void Compiler_BeforeSaveJsFiles()
            {
                Console.WriteLine("MyPlugin: BeforeSaveJsFiles: Hello again");
            }
    
    
        }
    }
    
  • Compile your plugin
  • Copy the output dll file into the skc5.exe directory (create a post-build event for easier updates)
  • Create a test project for your plugin:

  • Create a new SharpKit 5 Web Application Project named MySharpKitPluginTest
  • Install the plugin by editing the .csproj file, and add an ItemGroup element with your plugin fully qualified type name:
  • MySharpKitPluginTest.csproj
      <ItemGroup>
        <SkcPlugin Include="MySharpKitPlugin.MyPlugin, MySharpKitPlugin"><InProject>false</InProject></SkcPlugin>
      </ItemGroup>
    
  • Reload your test project
  • Build your test project
  • Look at the build output and verify that your plugin was loaded, and that the "Hello" output is written.
  • You can use the 'Compiler' object to read and modify the C# and JavaScript in-memory model. In order to use the C# model, you'll need to add a reference to NRefactory assemblies as well.

    Table of Contents

    © Copyright 2005-2015 SharpKit