Chris Pietschmann

A passionate software developer from Wisconsin.

Managed JScript on the DLR from Microsoft is DEAD!! WHY?!??

clock June 12, 2009 15:03 by author Chris Pietschmann

I’ve been questioning here and there as to what happened to Managed JScript on the Dynamic Language Runtime. The most recent preview release is really old, and it has since been taken out of any further preview releases of the DLR, where as IronRuby and IronPython continue on.

No More Managed JScript on the DLR?

For some time I never really got any good answers. Well, it’s really sad to hear that apparently Microsoft decided to drop it completely.

According the this link, a member on the DLR team has this to say:

“The DLR JScript was experimental for informing the design of the DLR (expression trees, interop, callsites, hosting, etc.). The JS we released with asp futures and the Silverlight dynamic sdk became very old and unserviceable as the DLR continued evolving for release in CLR 4.0. unfortunately, there are no plans at this time to develop and release a DLR hostable JScript.”

“Experimental for informing the design”??

I understand what this means, but since Managed JScript was used to help build the DLR from the beginning then “Why didn’t they keep it up to date?”

Plus if you go read the Initial Announcement of Managed JScript over on the JScript Blog you will see the following statement:

“We are working to make sure that Managed JScript is a first class language on top of DLR.”

What part of that post and the above statement specify that it’s only “experimental” and not to actually ever get released?

I’m really curious to find out who actually made the decision to drop it, and what the real reason is. Was it you ScottGu?

Why not Open Source it?

Well, the next logical question to ask is “Why not release what was done for Managed JScript as Open Source under a Public License?” At least this way it would allow the community to take it and run with it.

Are there Alternative Implementations?

None that I could find for .NET and/or the DLR. If you know of any, please let me know!

I did however find the Rhino project from Mozilla, but it’s for Java. According to Mozilla, “Rhino is an open-source implementation of JavaScript written entirely in Java. It is typically embedded into Java applications to provide scripting to end users.”  I guess this could be a start at building one for .NET/DLR, but…

Update: I did find the MyJScript project on CodePlex; it’s not a complete implemenation, but it does show the basics on how to create your own scripting language on the DLR. There is also an companion article to the MyJScript project: http://www.dotnetguru.org/us/dlrus/DLR2.htm

Further Info

Here’s a few links that have some small bits of info in addition to that linked above:

http://dlr.codeplex.com/Thread/View.aspx?ThreadId=58121

http://dlr.codeplex.com/Thread/View.aspx?ThreadId=41990

http://channel9.msdn.com/posts/Charles/Jimmy-Schementi-Inside-IronRuby/?CommentID=472955

http://channel9.msdn.com/shows/Going+Deep/John-Lam-and-Martin-Maly-Deep-DLR/?CommentID=472957

http://blogs.msdn.com/jscript/archive/2007/05/04/managed-jscript-announced.aspx

Conclusion

I’ve very disappointed to hear this sad news. However, I guess I could always work on building my own Managed JScript compiler/library for the DLR; if I could only find the time in between my other open source work and other paying gigs.

Until then, I guess I can only hope that Microsoft (or would it be ScottGu) decides to reconsider.



Building JavaScript / HTML based Applications using Adobe Air for FREE

clock June 10, 2009 20:44 by author Chris Pietschmann

As a web developer I use JavaScript, HTML and CSS a lot. I do however build desktop applications too, but can’t use those same tools/languages to build them. So, for quite some time now I’ve wanted to be able to build Desktop Applications using the same JavaScript, HTML and CSS that I use to build Web Applications. Now with the help of Adobe AIR it can finally be done with ease, and even have multi-platform support.

In this post I’m going to discuss the basics of creating a JavaScript/HTML based Desktop Application using Adobe AIR, and point you to some of the online resources that have helped me to get started.

Also, just in case you didn’t know, you can create/build JavaScript / HTML based Adobe AIR applications for FREE. The runtime and sdk are both free.

Getting Started with Adobe AIR

First you’ll need to get the following two things installed:

Here’s a couple excellent resources I found to getting started with Adobe AIR and JavaScript:

You can follow those two guides linked to above to get you started using Adobe AIR to build JavaScript / HTML based Desktop Applications.

Setting Up the Adobe AIR SDK on Windows

I had a couple small setup issues with getting the Adobe AIR SDK setup on my Windows development box.

The Adobe AIR SDK download is just a Zip Archive containing the files necessary for the SDK; it contains no setup EXE. To get it setup, you’ll need to follow the below steps:

  • Extract the SDK to some folder of your choosing. For Example: C:\AdobeAIRSDK
  • Add the “C:\AdobeAIRSDK\bin” folder to the System Path so you can execute it easily from within the Command Line.

Also, to use the Adobe Developer Tool (adt) you’ll need Java installed, and you’ll need to make sure that the path to where Java is installed is also included within the System Path as described above.

To edit the “System Path” in Windows just follow these steps:

  • Open the System Properties dialog box and click the Advanced tab. You can find this in the System settings within the Control Panel.
  • Click the Environment Variables button.
  • Select the PATH entry and then click the Edit button. Add the desired path to the end of the current variable value, separating it from previous values with a semicolon. For Example “;C:\AdobeAIRSDK\bin”
  • Click OK to Save.

Tips to Make Building and Testing Easier

Here are some simple tips to make building and testing your applications easier.

Create .BAT files to Build and Test

One thing that you'’ll want to do to make it a little easier to Build (using adt) and Test (using adl) your HTML-based Adobe AIR applications is create some simple “build.bat” and “test.bat” DOS Batch files so you don’t have to type in the command-line parameters every time you want to build or test your application.

build.bat example:

adt –package –storetype pkcs12 –keystore certificate MyApp.air application.xml .

test.bat example:

adl application.xml

This way you can just simply double-click on the specific .BAT file within Windows Explorer to either Build or Test your application.

Also, in the above build.bat example, my Certificate file is simply named “certificate”, and it’s a self-signed certificate.

Place Application Files within a Sub-Folder

If you rename your “.air” file that was built using the above mentioned .BAT file to be a “.ZIP” file and then open it, you’ll notice that the .BAT files and your Certificate were included within the Build. This is because the above mentioned call to “adt” tells it to include all files and folders within the build. To prevent the .BAT files and Certificate from being included, you’ll need to place them within a separate folder.

The easiest way to do this is to place all you Application files within a Sub-Folder within the main folder that your .BAT files and Certificate are located. For example you could name it “App_Files”. Then make the following changes to the .BAT files to point it to the new file/folder locations appropriately:

build.bat example:

cd App_Files
adt –package –storetype pkcs12 –keystore ../certificate ../MyApp.air application.xml .

test.bat example

cd App_Files
adl application.xml

Also, one thing to note about the above modified “build.bat” file is that it will place the Built “.air” file/application within the Main folder where the .BAT files and Certificate are located.

Creating a Self-Signed Certificate

One thing that you’ll notice above is that I’m using a Certificate file names “certificate”. This is a self-signed certificate that I generated using the “adt” tool. To generate a self-signed certificate, you can execute “adt” using the following command-line parameters:

adt –certificate –cn SelfSigned 1024-RSA certificate.pfx samplePassword

Conclusion

The above is pretty much the extent of my knowledge of building Adobe AIR applications using JavaScript / HTML at the time of this writing. I decided to take a few minutes to figure out the basics, so I thought I’d share some of the tidbits I found out.

Since, I’ve wanted to build desktop applications using JavaScript, HTML and CSS for some time now I find it relieving that it can be done with Adobe AIR and for Free.



Bing Maps for Enterprise (formerly Virtual Earth) Licensing FAQ

clock June 2, 2009 18:52 by author Chris Pietschmann

I usually get at least 1 email a month asking me some form of the below questions about Virtual Earth / Bing Maps for Enterprise Licensing. So, I’ve decided to post some information on the topic.

I’m not a Bing Maps for Enterprise (formerly  Virtual Earth) Reseller, nor do I work for Microsoft or represent Microsoft in any way; so the following tips/faq are my own opinion and do not represent Microsoft in any way. For official answers, please contact Microsoft directly and refer to the “Microsoft Virtual Earth Platform API Terms of Use.”

Email all licensing questions directly to Microsoft at maplic@microsoft.com

I encourage you to read through the official “Microsoft Virtual Eearth Platform API Terms of Use”. You can also find more information on the “Microsoft Bing Maps for Enterprise – Licensing And Pricing Options” page.

I am not responsible for any actions you take based on the tips/faq provided below. The below is provided for informational purposes only and is not provided as legal advise. It is always recommended you consult an attorney.

Now that I have the disclaimer out of the way…

Q: Do I need to Pay / License Bing Maps for Enterprise to use the JavaScript Map Control and/or Web Services within my Website / Application?

A: There are really two separate answers: 

1) Non-Commercial Use: No, If you are developing or hosting an online application that uses the service to display results for Non-Commercial Use, you do not need to purchase a license agreement from Microsoft. 

2) Commercial  or Government Use: Yes, If you are developing or hosting an online application that uses the service to display results for Commercial or Government Use, then you are required to purchase a license agreement from Microsoft. However, you are allowed to use the service for a 90 day evaluation period before you are required to purchase a license agreement.

There are some additional restrictions for using the Service for Non-Commercial use and within the 90 day evaluation period; these restrictions are outlined within the official “Microsoft Virtual Earth Platform API Terms of Use.”

Q: Can I use the Silverlight Virtual Earth Control CTP within a Website / Application?

A: Officially, the answer is No. Since the current release of the Silverlight Virtual Earth Control is a Community Technology Preview (CTP) it is not licensed for use in any Production environments.  Also, there is no support provided for any bugs that you may encounter while using it.

However, you can use the Silverlight Virtual Earth Control CTP for Development and Testing purposes.

Q: Can I use Bing Maps (Virtual Earth) within a Windows Forms or WPF Desktop Application?

A: As far as Licensing, you’ll need to contact Microsoft. The “Microsoft Virtual Earth Platform API Terms of Use” doesn’t really cover this specific usage scenario.

Microsoft doesn’t have a Window Forms or WPF control that you can just drag onto a Window, but you could access the Web Service from your application or display the JavaScript Map Control within an embedded WebBrowser control.

Q: Can I use the Virtual Earth Server Control that’s part of the Windows Live Tools CTP within a Website / Application?

A: No, Since the Windows Live Tools is a Community Technology Preview (CTP) it is not licensed for use in any Production environments, and thus all the controls within the Windows Live Tools CTP are licensed the same.

Also, the Virtual Earth Server Control within the latest release of the Windows Live Tools CTP is buggy and only partially implemented.

If you are looking for an ASP.NET AJAX Bing Maps (Virtual Earth) Server Control that you CAN use in Production and is Fully Supported, then I encourage you to check out the Web.Maps.VE product by Simplovation.



Virtual Earth Shapes (VEShape) to WKT (Well-Known-Text) and Back using JavaScript

clock April 4, 2009 12:38 by author Chris Pietschmann

One of the standard methods of representing geometric shapes is by using the WKT (Well-Known-Text) standard. This is a human readable standard method of representing geometric shapes that can be used to easily pass spatial data between applications. I know GML or GeoRSS may be a little more applicable since they are based on XML, but WKT can work just fine in some cases.

If you don’t know what WKT is here are a couple links for reference:

http://geoapi.sourceforge.net/2.0/javadoc/org/opengis/referencing/doc-files/WKT.html

http://en.wikipedia.org/wiki/Well-known_text

This is also one of the things that Virtual Earth does NOT have support built in for. So I wrote a little code that simply converts VEShape objects to a WKT string representation.

The code below allows you to represent Pushpins, Polygons and Polylines as strings like the following:

POINT(-99.71000000000001 43.74999999999998)

POLYGON((-99.71000000000001 46.74999999999998,
 -96.71000000000001 46.74999999999998,
 -96.71000000000001 43.74999999999998,
 -99.71000000000001 46.74999999999998))

LINESTRING(-99.71000000000001 40.74999999999998,
 -102.71000000000001 40.74999999999998,
 -102.71000000000001 43.74999999999998)

Here are some examples of using the conversion methods:

// Get Map Center Point
 var centerPoint = map.GetCenter();

 // Create Pushpin VEShape and Get it's WKT representation
 var wktShape = VirtualEarthWKT.ShapeToWKT(new VEShape(VEShapeType.Pushpin, centerPoint));
 // Create a VEShape from the WKT representation
 var shape = VirtualEarthWKT.ShapeFromWKT(wktShape);
 // Add VEShape to Map
 map.AddShape(shape);


 // Create Polygons' Location Array
 var polygonLocations = [
    new VELatLong(centerPoint.Latitude + 3, centerPoint.Longitude),
    new VELatLong(centerPoint.Latitude + 3, centerPoint.Longitude + 3),
    new VELatLong(centerPoint.Latitude, centerPoint.Longitude + 3)
 ];

 // Create Polygon VEShape and Get it's WKT representation
 wktShape = VirtualEarthWKT.ShapeToWKT(new VEShape(VEShapeType.Polygon, polygonLocations));
 // Create a VEShape from the WKT representation
 shape = VirtualEarthWKT.ShapeFromWKT(wktShape);
 // Add VEShape to Map
 map.AddShape(shape);


 // Create Polylines' Location Array
 var polylineLocations = [
    new VELatLong(centerPoint.Latitude - 3, centerPoint.Longitude),
    new VELatLong(centerPoint.Latitude - 3, centerPoint.Longitude - 3),
    new VELatLong(centerPoint.Latitude, centerPoint.Longitude - 3)
 ];

 // Create Polyline VEShape and Get it's WKT representation
 wktShape = VirtualEarthWKT.ShapeToWKT(new VEShape(VEShapeType.Polyline, polylineLocations));
 // Create a VEShape from the WKT representation
 shape = VirtualEarthWKT.ShapeFromWKT(wktShape);
 // Add VEShape to Map
 map.AddShape(shape);

Here’s the full code to the VirtualEarthWKT object that contains the static methods:

// Create the singleton object that contains the WKT (Well-Known-Text) transformation methods.
var VirtualEarthWKT = new (function() {
    // Declare some "private" methods that will only be used internally
    var priv = {
        trimSpaces: function(str) {
            // Trim beginning spaces
            while (priv.startsWith(str, " ")) {
                str = str.substring(1);
            }
            // Trim ending spaces
            while (priv.endsWith(str, " ")) {
                str = str.substring(0, str.length - 1);
            }
            return str;
        },
        startsWith: function(str, startstr) {
            return str.substring(0, startstr.length) == startstr;
        },
        endsWith: function(str, endstr) {
            return str.substring(str.length - endstr.length) == endstr;
        }
    };
    // Declare the "public" methods that will be exposed
    var that = {
        ///<summary>Converts a VEShape object to WKT (Well-Known-Text) string representation.</summary>
        ShapeToWKT: function(shape) {
            if (shape == null) {
                throw "VirtualEarthWKT.ShapeToWKT: 'shape' parameter can not be null.";
            }
            var wktTemplate = "";
            var wktGeomPoints = "";

            // Figure out the WKT Geometry Type
            switch (shape.GetType()) {
                case VEShapeType.Pushpin:
                    wktTemplate = "POINT({points})";
                    break;
                case VEShapeType.Polygon:
                    wktTemplate = "POLYGON(({points}))";
                    break;
                case VEShapeType.Polyline:
                    wktTemplate = "LINESTRING({points})";
                    break;
                default:
                    throw "VirtualEarthWKT.ShapeToWKT: VEShapeType (" + shape.GetType() + ") not supported.";
                    break;
            }

            // Get the List of VELatLong objects represented as WKT compatible list of points
            var shapePoints = shape.GetPoints();
            for (var i = 0; i < shapePoints.length; i++) {
                if (wktGeomPoints.length > 0) {
                    wktGeomPoints += ", ";
                }
                wktGeomPoints += shapePoints[i].Longitude + " " + shapePoints[i].Latitude;
            }

            // return WKT representation of the VEShape
            return wktTemplate.replace("{points}", wktGeomPoints);
        },
        ///<summary>
        ///Converts WKT (Well-Known-Text) string representation of a point/polygon/linestring to a VEShape object.
        ///</summary>
        ShapeFromWKT: function(strWKT) {
            if (strWKT == null) {
                throw "VirtualEarthWKT.ShapeFromWKT: 'strWKT' parameter can not be null.";
            }
            if (strWKT.length == 0) {
                throw "VirtualEarthWKT.ShapeFromWKT: 'strWKT' parameter can not be an empty string.";
            }
            var shapeType = null;
            var wktPoints = null;

            // Get the Shape Type and list of "Longitude Latitude" location points
            switch (strWKT.substring(0, 5).toLowerCase()) {
                case "point":
                    shapeType = VEShapeType.Pushpin;
                    wktPoints = strWKT.substring(6, strWKT.length - 1);
                    break;
                case "polyg":
                    shapeType = VEShapeType.Polygon;
                    wktPoints = strWKT.substring(9, strWKT.length - 2);
                    break;
                case "lines":
                    shapeType = VEShapeType.Polyline;
                    wktPoints = strWKT.substring(11, strWKT.length - 1);
                    break;
                default:
                    throw "VirtualEarthWKT.ShapeFromWKT: Unknown WKT Geometry Type";
                    break;
            }

            // split out the wkt points to be seperate
            wktPoints = wktPoints.split(",");

            // Convert the WKT Points to VELatLong locations
            var shapePoints = new Array();
            for (var i = 0; i < wktPoints.length; i++) {
                // Split the "Longitude Latitude" apart
                var loc = priv.trimSpaces(wktPoints[i]).split(" ");
                // Create VELatLong location
                shapePoints[shapePoints.length] = new VELatLong(parseFloat(loc[1]), parseFloat(loc[0]));
            }
            
            // Return a VEShape that represents this WKT Geometry
            return new VEShape(shapeType, shapePoints);
        }
    };
    // Return the object that contains the "public" and "private" methods
    return that;
})();


Awarded 2009 Microsoft MVP - Windows Live Platform

clock April 1, 2009 16:17 by author Chris Pietschmann

Congratulations 2009 Microsoft MVP!

Dear Chris Pietschmann,

Congratulations! We are pleased to present you with the 2009 Microsoft® MVP Award! This award is given to exceptional technical community leaders who actively share their high quality, real world expertise with others.

The Microsoft MVP Award provides us the unique opportunity to celebrate and honor your significant contributions and say "Thank you for your technical leadership."

Toby Richards
General Manager
Community Support Services

I’m lucky enough to be one of the April Fools MVPs. Last year I wasn’t sure at first if the email was an April Fools joke or not, and this year I’m just as happy that it’s not. This marks the second time I’ve been awarded Microsoft MVP. Last year I was awarded in the “Virtual Earth” category, and last fall they renamed me to “Windows Live Platform”. This year I am awarded in the same “Windows Live Platform” category.

In case you’re not familiar with the Microsoft MVP Program, here’s a link for more information:

http://mvp.support.microsoft.com/gp/mvpawardintro

And Congratulations to all those that have also been awarded MVP on this April Fools day. Especially to those who’ve gotten awarded for the first time; keep it up.

Thanks!!

Chris Pietschmann



Prototype of VEToolkit + ASP.NET MVC 1.0 Component Checked In

clock March 28, 2009 16:17 by author Chris Pietschmann

I just checked in an initial prototype of a reusable Virtual Earth component for ASP.NET MVC. The “VEToolkit.Web.MVC.Map” component is written as an Extension to the ASP.NET MVC AjaxHelper class, and allows for a simpler experience when implementing Virtual Earth mapping within ASP.NET MVC applications. This is an early prototype of what the component will be; it’s not a final release; but you are free to use it.

Download the latest Change Set of VEToolkit

The code is subject to change at any time, since this is currently in a prototype stage, but below is a basic overview of what’s there so far.

Include a Basic Map on the Page

Include the “VEToolkit.Web.MVC” namespace within the page by adding the following Include directive to the top of the Page:

<%@ Import Namespace="VEToolkit.Web.MVC" %>

Add the following two JavaScript Includes (the Virtual Earth JavaScript API and jQuery) to the Page Header:

<script type="text/javascript" src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script>
<script type="text/javascript" src="/Scripts/jquery-1.3.2.js"></script>

Add the following to the Page to add a basic Virtual Earth map:

<%=Ajax.Map() %>

And that’s it; you’ll have “default” sized Virtual Earth map on the page.

Specify you own name for the global VEMap Variable that’s used

By default the component autogenerates an ID to use when naming the global VEMap object within the JavaScript that’s generated.

Here’s an example of how to specify your own ID (or variable name) to be used for the global VEMap object that’s created:

<%=Ajax.Map("myMap") %>

Now to get a hold of the global reference to the VEMap object, you can just access it by name, like so:

<input type="button" value="Zoom In" onclick="myMap.ZoomIn();" />
<input type="button" value="Zoom Out" onclick="myMap.ZoomOut();" />

 

Set a couple Map Properties

Here’s a modified example of the above that sets the Center Location, Zoom Level and Map Style:

<%=Ajax.Map("myMap")
    .SetCenter(new Location(44, -78))
    .SetZoom(6)
    .SetMapStyle(MapStyle.Road)%>

 

Call a JavaScript Function Once the Map is Loaded

Also you can specify a JavaScript Function to be called once the Map has finished loading within the Page.

Here’s an example:

<%=Ajax.Map("myMap")
    .SetOnMapLoaded("MapLoaded")%>

And, here’s a simple JavaScript Function named “MapLoaded” to match that adds a Pushpin to the Map at it’s Center Point:

<script type="text/javascript">
    function MapLoaded(sender) {
        // "sender" = The Map that was Loaded
        var map = sender;

        // Add a Shape to the Center of the Map now it's finished loading
        var s = new VEShape(VEShapeType.Pushpin, sender.GetCenter());
        s.SetTitle("Center Point");
        s.SetDescription("This was the original center point when the Map loaded.");
        map.AddShape(s);
    }
</script>

 

Conclusion

As stated above, this is just a basic overview of what’s been implemented so far in the VEToolkit ASP.NET MVC Prototype. There are a couple more things in there than I mentioned above, but I thought I’d keep the intro very basic until development moved further along.



ASP.NET MVC: Implement Theme Folders using a Custom ViewEngine

clock March 26, 2009 18:09 by author Chris Pietschmann

One of the things that ASP.NET MVC 1.0 is missing is the ability to easily implement Themes. The older, more mature standard ASP.NET framework includes theme support via the App_Themes folder; however limited it can be, it’s still more than ASP.NET MVC currently has. Well, at least until I wrote this little custom ViewEngine and ControllerBase class to help out and allow us to very easily implement Themes within our ASP.NET MVC applications.

A little history: A few months back I wrote up a post on “How To Setup Custom Theme Support In ASP.NET MVC Preview 4 using a Custom ViewEngine”, then a couple weeks later I posted an updated version that added Custom Themes to ASP.NET MVC Preview 5. So I’ve decided to update the code from Preview 5 and make it all work with ASP.NET MVC v1.0 Final Release.

Also, I took a tip from Michael Ryan and modified my previous theme implementation from Preview 5 to include both Views and Content folders within the Theme folder.

Download the Code

I know some of you may want to download the code and look at it before reading on, so here's the download link:

Source Code: AspNetMvc1CustomThemeImplementation.zip (262.99 kb)

 

Create “~/Themes” Folder and a “Default” View Theme

ASPNETMVC_1_CustomThemeFolderLayoutFirst, we’ll make some changes to the Views contained within the default ASP.NET MVC Template.

Here’s a brief summary of what changes are needed:

  1. Create a sub-folder named “Themes” within the website root folder
  2. Create a sub-folder names “Default” within the “Themes” folder
  3. Cut and Paste the “Views” folder into the new “~/Themes/Default” folder
  4. Cut and Past the “Content” folder into the new “~/Themes/Default” folder
  5. Modify the Pages (.aspx) to point correctly to the Master Page (.master) for the Theme their in, now that we moved the files.
  6. Modify the Master Pages (.master) to point to the CSS file within the “Content” folder correctly, now that we moved the files.
  7. To create additional Themes, just copy the “Default” folder and name it what you want for the desired Theme, and repeat Steps 5 and 6 above.

To the right is a screenshot displaying the layout of the Themes folder as described above.

Apply the Custom ViewEngine

Now that we have our Themes folder created, we can go ahead and implement our Custom ViewEngine. The Custom ViewEngine in this example was created by inheriting the WebFormViewEngine and just overriding/changing the necessary functionality.

First we need to tell our application to use the new “WebFormThemeViewEngine” ViewEngine class. To do this we’ll need to clear any existing ViewEngines and add a new instance of the ‘WebFormThemeViewEngine”. This needs to be done within the Application_Start method in the Global.asax. Below is what the Application_Start method will look like after we make the necessary changes.

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);

    // Replace the Default WebFormViewEngine with our custom WebFormThemeViewEngine
    System.Web.Mvc.ViewEngines.Engines.Clear();
    System.Web.Mvc.ViewEngines.Engines.Add(new WebFormThemeViewEngine());
}

Also, for reference here’s the complete code for the “WebFormThemeViewEngine” class that’s used in this example. I wont be discussing the steps necessary when creating your own custom ViewEngines; that would be a little more involved than I would like to get within the scope of this article. A link to download the entire code for the project I created when writing this article is located at the top of the article.

public class WebFormThemeViewEngine : WebFormViewEngine
{
    public WebFormThemeViewEngine()
    {
        base.MasterLocationFormats = new string[] {
            "~/Themes/{2}/Views/{1}/{0}.master", 
            "~/Themes/{2}/Views/Shared/{0}.master"
        };
        base.ViewLocationFormats = new string[] { 
            "~/Themes/{2}/Views/{1}/{0}.aspx", 
            "~/Themes/{2}/Views/{1}/{0}.ascx", 
            "~/Themes/{2}/Views/Shared/{0}.aspx", 
            "~/Themes/{2}/Views/Shared/{0}.ascx" 
        };
        base.PartialViewLocationFormats = new string[] {
            "~/Themes/{2}/Views/{1}/{0}.aspx",
            "~/Themes/{2}/Views/{1}/{0}.ascx",
            "~/Themes/{2}/Views/Shared/{0}.aspx",
            "~/Themes/{2}/Views/Shared/{0}.ascx"
        };
    }

    protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
    {
        try
        {
            return System.IO.File.Exists(controllerContext.HttpContext.Server.MapPath(virtualPath));
        }
        catch (HttpException exception)
        {
            if (exception.GetHttpCode() != 0x194)
            {
                throw;
            }
            return false;
        }
        catch
        {
            return false;
        }
    }

    public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
    {
        string[] strArray;
        string[] strArray2;

        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }
        if (string.IsNullOrEmpty(viewName))
        {
            throw new ArgumentException("viewName must be specified.", "viewName");
        }

        string themeName = this.GetThemeToUse(controllerContext);

        string requiredString = controllerContext.RouteData.GetRequiredString("controller");

        string viewPath = this.GetPath(controllerContext, this.ViewLocationFormats, "ViewLocationFormats",
                viewName, themeName, requiredString, "View", useCache, out strArray);

        string masterPath = this.GetPath(controllerContext, this.MasterLocationFormats, "MasterLocationFormats",
                masterName, themeName, requiredString, "Master", useCache, out strArray2);

        if (!string.IsNullOrEmpty(viewPath) && (!string.IsNullOrEmpty(masterPath) || string.IsNullOrEmpty(masterName)))
        {
            return new ViewEngineResult(this.CreateView(controllerContext, viewPath, masterPath), this);
        }
        return new ViewEngineResult(strArray.Union<string>(strArray2));
    }

    public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
    {
        string[] strArray;
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }
        if (string.IsNullOrEmpty(partialViewName))
        {
            throw new ArgumentException("partialViewName must be specified.", "partialViewName");
        }

        string themeName = this.GetThemeToUse(controllerContext);

        string requiredString = controllerContext.RouteData.GetRequiredString("controller");
        string partialViewPath = this.GetPath(controllerContext, this.PartialViewLocationFormats,
                "PartialViewLocationFormats", partialViewName, themeName, requiredString, "Partial", useCache, out strArray);
        if (string.IsNullOrEmpty(partialViewPath))
        {
            return new ViewEngineResult(strArray);
        }
        return new ViewEngineResult(this.CreatePartialView(controllerContext, partialViewPath), this);

    }

    private string GetThemeToUse(ControllerContext controllerContext)
    {
        string themeName = controllerContext.HttpContext.Items["themeName"] as string;
        if (themeName == null) themeName = "Default";
        return themeName;
    }

    private static readonly string[] _emptyLocations;

    private string GetPath(ControllerContext controllerContext, string[] locations, string locationsPropertyName,
        string name, string themeName, string controllerName, string cacheKeyPrefix, bool useCache, out string[] searchedLocations)
    {
        searchedLocations = _emptyLocations;
        if (string.IsNullOrEmpty(name))
        {
            return string.Empty;
        }
        if ((locations == null) || (locations.Length == 0))
        {
            throw new InvalidOperationException("locations must not be null or emtpy.");
        }

        bool flag = IsSpecificPath(name);
        string key = this.CreateCacheKey(cacheKeyPrefix, name, flag ? string.Empty : controllerName, themeName);
        if (useCache)
        {
            string viewLocation = this.ViewLocationCache.GetViewLocation(controllerContext.HttpContext, key);
            if (viewLocation != null)
            {
                return viewLocation;
            }
        }
        if (!flag)
        {
            return this.GetPathFromGeneralName(controllerContext, locations, name, controllerName, themeName, key, ref searchedLocations);
        }
        return this.GetPathFromSpecificName(controllerContext, name, key, ref searchedLocations);
    }

    private static bool IsSpecificPath(string name)
    {
        char ch = name[0];
        if (ch != '~')
        {
            return (ch == '/');
        }
        return true;
    }

    private string CreateCacheKey(string prefix, string name, string controllerName, string themeName)
    {
        return string.Format(CultureInfo.InvariantCulture,
            ":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}",
            new object[] { base.GetType().AssemblyQualifiedName, prefix, name, controllerName, themeName });
    }

    private string GetPathFromGeneralName(ControllerContext controllerContext, string[] locations, string name,
        string controllerName, string themeName, string cacheKey, ref string[] searchedLocations)
    {
        string virtualPath = string.Empty;
        searchedLocations = new string[locations.Length];
        for (int i = 0; i < locations.Length; i++)
        {
            string str2 = string.Format(CultureInfo.InvariantCulture, locations[i], new object[] { name, controllerName, themeName });

            if (this.FileExists(controllerContext, str2))
            {
                searchedLocations = _emptyLocations;
                virtualPath = str2;
                this.ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);
                return virtualPath;
            }
            searchedLocations[i] = str2;
        }
        return virtualPath;
    }

    private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, ref string[] searchedLocations)
    {
        string virtualPath = name;
        if (!this.FileExists(controllerContext, name))
        {
            virtualPath = string.Empty;
            searchedLocations = new string[] { name };
        }
        this.ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);
        return virtualPath;
    }
}

 

Create a ThemeControllerBase Class to Initially Set the Theme to Use

One additional step that’s necessary when using the above WebFormThemeViewEngine is to create a custom Controller Base Class, and inherit from it with all your Controllers. This ThemeControllerBase class needs to set the Theme to use within the “HttpContext.Items[“themeName”]”. If you miss or skip this step you will get an exception and will not be able to run the website.

The ThemeControllerBase class used in this example includes code that allows you to specify the Theme to use by passing it in using the QueryString like this: “http://localhost/Default.aspx?theme=Red”

Here’s the code for the ThemeControllerBase class:

public abstract class ThemeControllerBase : Controller
{
    protected override void Execute(System.Web.Routing.RequestContext requestContext)
    {
        // Add code here to set the Theme based on your database or some other storage
        requestContext.HttpContext.Items["themeName"] = "Red";


        
        // Allow the Theme to be overriden via the querystring
        // If a Theme Name is Passed in the querystring then use it and override the previously set Theme Name
        // http://localhost/Default.aspx?theme=Red
        var previewTheme = requestContext.HttpContext.Request.QueryString["theme"];
        if (!string.IsNullOrEmpty(previewTheme))
        {
            requestContext.HttpContext.Items["themeName"] = previewTheme;
        }

        base.Execute(requestContext);
    }
}

Then you need to have all your Controller’s inherit from the ThemeControllerBase like the following:

public class HomeController : ThemeControllerBase { }

 

Conclusion

I’m sure some kind of Themes support is on the list for Microsoft to eventually implement into ASP.NET MVC some time down the road, but I have yet to see anything mentioned. Also, since the Official v1.0 release has already been released, I don’t really expect anything to change until presumably after .NET 4.0 ships; this is assuming the ASP.NET MVC 1.0 bits are what make it into .NET 4.0 and not a newer version of ASP.NET MVC. So, for now if you want to implement Themes into your ASP.NET MVC Website, you’ll just have to use the code I post here, or some other custom implementation.



Virtual Earth Silverlight: Using MouseClick Event To Add "Pushpins"

clock March 25, 2009 22:38 by author Chris Pietschmann

In previous tutorials I covered the “Basics of Adding Polygons and Polylines” and “Adding Media (Images, Video, etc.)”, but what about handling user input via Map Events? Well, in this tutorial I’ll cover the basics of working with the Map.MouseClick Event (equivalent of the old “onclick” event in the Virtual Earth JavaScript Control).

Adding a MouseClick Event Handler

In order to wire things up to manipulate the Map using the MouseClick event, we must first attach an Event Handler to it. The Event Handler for the MouseClick event needs to be just like any other event handler in .NET; it must accept two parameters: 1) a “sender” object, and 2) an “EventArgs” object.

Before we create our event handler, it’s worth noting that to be more specific, the value that gets passed to the “EventArgs” parameter of the Map.MouseClick event is actually of Type MapMouseEventArgs.

The MapMouseEventArgs object has the following two properties:

  1. Handled – Gets or Sets a value indicating whether the Map event was handled. If you want to prevent the “default” event handling behavior from executing, then all you need to do is set this to True.
  2. ViewportPoint – Gets the viewport point where the mouse event occurred. This is a System.Windows.Point object that represents the X and Y coordinates where the mouse was clicked.

To declare the MouseClick event handler a method that matched the following signature: void(object sender, MapMouseEventArgs e)

Here’s an example Map.MouseClick event handler method:

private void myMap_MouseClick(object sender, MapMouseEventArgs e)
{
}

You can attach the above event handler from within code:

myMap.MouseClick += new EventHandler<MapMouseEventArgs>(myMap_MouseClick);

Or, from within XAML:

<m:Map Name="myMap" MouseClick="myMap_MouseClick"></m:Map>

 

Get the Location (Lat/Long Coordinate) the Mouse was Clicked

One thing that you may have noticed is the MapMouseEventArgs object that is passed to the MouseClick event handler doesn’t contain a “Location” or “LatLong” property of any kind. It’s seems odd at first that there’s no direct way of obtaining the Map Location (Lat/Long) of where the mouse was clicked, but with the help of a little helper method on the Map object itself we can pass in the ViewportPoint (X and Y Coordinates) value contained within the MapMouseEventArgs.ViewportPoint property and get back a Location object with Latitude/Longitude.

To obtain the Location (Lat/Long) where the mouse was clicked, simply invoke the Map.ViewportPointToLocation method by passing in the MapMouseEventArgs.ViewportPoint value.

The following line of code demonstrates how this can be done within the MouseClick Event Handler we defined above:

var location = clickedMap.ViewportPointToLocation(e.ViewportPoint);

Now that we have the  Location of where the mouse was clicked, we can directly access the Latitude and Longitude values.

var lat = location.Latitude;
var lng = location.Longitude;

 

Add a “Pushpin” at the Clicked Location

Now that we have a MouseClick Event Handler in place, and we have access to the Location (Latitude/Longitude) where the mouse was clicked; we can go ahead and add a “Pushpin” to the Map at that Location.

If you have any questions on how to add a Shape or “Pushpin” to the Map, I recommend reading the following two tutorials:

In this example to keep things simple, we’ll add a Square (via the System.Windows.Shapes.Rectangle object) to the Map at the clicked location that is 10x10 in size.

Here’s a full code example of the MouseClick event handler we created above, including code that gets the Location clicked and adds the Rectangle Shape to the Map within a new MapLayer:

private void myMap_MouseClick(object sender, MapMouseEventArgs e)
{
    // Set a variable equal to the Map that raised the event
    Map clickedMap = (Map)sender;
    
    // Convert the X/Y Coordinate that was Clicked to a Lat/Long Location
    var location = clickedMap.ViewportPointToLocation(e.ViewportPoint);

    // Create new MapLayer
    var myLayer = new MapLayer();

    // Create a Rectangle Shape to Show as the "Pushpin"
    var rectangle = new System.Windows.Shapes.Rectangle();
    // Set it's Size to 10x10
    rectangle.Width = 10;
    rectangle.Height = 10;
    // Color the Rectangle Red
    rectangle.Fill = new SolidColorBrush(Colors.Red);

    // Center the Rectangle around the location
    PositionMethod position = PositionMethod.Center;

    // Add Rectangle to MapLayer
    myLayer.AddChild(rectangle, location, position);

    // Add the MapLayer to the Map so the "Pushpin" gets displayed
    clickedMap.AddChild(myLayer);
}

One thing that you may want to do when implementing the above example is create only 1 MapLayer and add all the new “Pushpins” to that single MapLayer. This will help improve performance on the Map if you are adding MANY pushpins. For the simple example above this just wasn’t absolutely necessary.

Auto Expand “Pushpins” using ScaleTransform

The best part of the new Virtual Earth Silverlight Map Control is the fact that we can now leverage the full power of Silverlight within our mapping applications/implementations. One of the cool things that can be done are implementing Animations to spice up the user interface.

Here’s an example that takes the above MouseClick event handler code that adds a Rectangle “Pushpin” to the Map when the user clicks the Map, and adds a ScaleTransform to “Expand” the Rectangle when the user moves the mouse over it.

This is done by setting a ScaleTransform to the Rectangle.RenderTransform during the MouseEnter event, and then removing it during the MouseLeave event. The end result are Rectangle “Pushpins” that Expand when the user Moves the mouse over them.

Here’s the full code for Map.MouseClick, Rectangle.MouseEnter and Rectangle.MouseLeave Event Handlers for this “Expand” Animation example:

// Single MapLayer that will contain all "Pushpins"
private MapLayer myLayer = null;

private void myMap_MouseClick(object sender, MapMouseEventArgs e)
{
    // Set a variable equal to the Map that raised the event
    Map clickedMap = (Map)sender;

    // Convert the X/Y Coordinate that was Clicked to a Lat/Long Location
    var location = clickedMap.ViewportPointToLocation(e.ViewportPoint);

    // Check if MapLayer has already been created
    if (myLayer == null)
    {
        // Create new MapLayer
        myLayer = new MapLayer();

        // Add the MapLayer to the Map so the "Pushpin" gets displayed
        clickedMap.AddChild(myLayer);
    }

    // Create a Rectangle Shape to Show as the "Pushpin"
    var rectangle = new System.Windows.Shapes.Rectangle();
    // Set it's Size to 10x10
    rectangle.Width = 10;
    rectangle.Height = 10;
    // Color the Rectangle Red
    rectangle.Fill = new SolidColorBrush(Colors.Red);

    // Attach MouseEnter and MouseLeave Event Handler to the Rectangle
    // This will be used to "Expand" the Rectangle when the user hovers the mouse over it
    rectangle.MouseEnter += new MouseEventHandler(rectangle_MouseEnter);
    rectangle.MouseLeave += new MouseEventHandler(rectangle_MouseLeave);

    // Center the Rectangle around the location
    PositionMethod position = PositionMethod.Center;

    // Add Rectangle to MapLayer
    myLayer.AddChild(rectangle, location, position);
}

void rectangle_MouseEnter(object sender, MouseEventArgs e)
{
    // Get the Rectangle
    var rectangle = (Rectangle)sender;

    // Create new ScaleTransform
    var scaleTransform = new ScaleTransform();

    // Set the Transform to "Expand" the Rectangle
    // equally horizontally and vertically
    scaleTransform.ScaleX = 2.0;
    scaleTransform.ScaleY = 2.0;

    // Set the Scale Center to the Center of the Rectangle
    // This is so it doesn't move when "Expanded"
    scaleTransform.CenterX = rectangle.Width / 2;
    scaleTransform.CenterY = rectangle.Height / 2;

    // Apply the ScaleTransform to the Rectangle
    rectangle.RenderTransform = scaleTransform;
}

void rectangle_MouseLeave(object sender, MouseEventArgs e)
{
    // Get the Rectangle
    var rectangle = (Rectangle)sender;

    // Remove the ScaleTransform
    rectangle.RenderTransform = null;
}

The above code example also includes a modification from the previous examples in this tutorial; it adds all the new Rectangle “Pushpins” to a Single MapLayer instead of adding a new MapLayer for each Rectangle “Pushpin”.

Conclusion

The Map.MouseClick event is really simple to take advantage of, and it’s actually just as easy to add a “Pushpin” during the “onclick” event with the Virtual Earth Silverlight Map Control as it is with the Virtual Earth JavaScript Control.

There are a few other events that the Map object exposes, and I’ll be covering some of those in additional tutorials. So, keep an eye out for future posts.

Previous Tutorial/Article: Overlay OpenStreetMap, OpenAerialMap and Yahoo Map Imagery using Custom Tile Layers



MIX'09: Virtual Earth Silverlight CTP Session Online for Viewing

clock March 21, 2009 16:46 by author Chris Pietschmann

The "Introducing the Microsoft Virtual Earth Silverlight Map Control CTP" session from MIX'09 by Chris Pendlton is now available for your viewing pleasure online.

http://sessions.visitmix.com/MIX09/T34F

Also, here it is below:

Get Microsoft Silverlight



I'm starting a Virtual Earth Developer Resource list

clock March 21, 2009 16:36 by author Chris Pietschmann

I just started a page at the below link that I'm lising a bunch of Virtual Earth Developer Resources that I know of. The reason I'm doing this is because there just aren't any really good lists of resources out there, and there are a TON of resources that you probably don't even know about. This isn't going to be an all inclusive list (that would be impossible), but I'll try to make it the best list of resources I can. Hope it helps some of you discover great content.

http://pietschsoft.com/page/Virtual-Earth-Developer-Resources.aspx


About the author

I'm Chris Pietschmann, and this is my blog. Here you will find various posts relating to the software development process. I mostly focus on Microsoft technologies, but I do stray in to other stuff on occasion.

Please feel free to leave a comment or contact me directly.

E-mail Me
About Me

Post Archive

Chris Pietschmann | Microsoft MVP - Windows Live Platform


Simplovation Web.Maps.VE - ASP.NET AJAX Virtual Earth Server Control


Copyright Notice


This work is licensed under a Creative Commons Attribution 3.0 United States License, unless explicitly stated otherwise within the posted content.
© Copyright 2004 - 2009 Chris Pietschmann

Sign in