Sunday, January 29, 2012

Using C# and VB.NET classes together in the App_Code folder


In ASP.NET 2.0 one can mix web forms coded in C# and VB.NET together in a single web site. This works great for web forms. However if you want to code classes from App_Code folder in different languages? Such mixing of classes coded in different languages is not allowed with default settings. You can, however,configure your web site to get this done. This article is going to explain you that how one can do that.
Creating sub folders and classes
First of all, Create a new webSite in VS.NET 2005. Add App_Code folder to it. You can do so easily by right clicking on the web site in the Solution Explorer and choosing Add ASP.NET Folder” option (see below).
Different Language
Once you add the App_Code folder add two class files – Class1.cs and Class2.vb. Note that one class file must be in C# where as the other must be in VB.NET. Add the following code in Class1.cs.
C# Code
Similarly, add the following code in Class2.vb.
VB.NET Code
Both of these classes contain a method called HelloWorld() that simply return a string to the caller.
Now try compiling the web site. What happens? You will get an error as shown below:
Error 1 The files ‘/VBandCSharptogether/App_Code/Class2.vb’  and ‘/VBandCSharptogether/App_Code/Class1.cs’ use a
different language, which is not allowed since they need to be compiled together.
The error message bluntly tells us that you can not use different coding languages for the classes in App_Code folder. Fortunately, there is a way to get out of this trap. Firstly you need to put C# and VB.NET classes in separate sub-folders under App_Code. Secondly you need to add some markup in the web.config file to tell ASP.NET compiler about your intention.

<codeSubDirectories> Section

Create two folders under App_Code filder named CSCode and VBCode. Move Class1.cs inside CSCode folder and Class2.vb inside VBCode folder.
Add a web.config file to your web site and add the following markup to it:
Web Config Code
Here, we added <compilation> section. The <codeSubDirectories> section defines a set of sub-directories relative to App_Code folder that are compiled at run time. The directoryName attribute points to the sub-folder of App_code. Each sub folder is compiled separately and hence each can have classes coded in different languages. We added our CSCode and VBCode folder in this section. This way the compiler will compile classes from CSCode and VBCode folders separately.
After configuring the web site try to compile it. This time it compiles successfully.

Saturday, January 14, 2012

HTTP POSTs and HTTP GETs with WebClient and C# and Faking a PostBack


A fellow emailed me wanting to screen scrape, er, ah, harvest a page that only displays the data he wants with a postback.
Remember what an HTTP GET looks like under the covers:
GET /whatever/page.aspx?param1=value&param2=value
Note that the GET includes no HTTP Body. That's important. With a POST the 'DATA' moves from the QueryString into the HTTP Body, but you can still have stuff in the QueryString.
POST /whatever/page.aspx?optionalotherparam1=value
Content-Type: application/x-www-form-urlencoded
Content-Length: 25
param1=value&param2=value
Note the Content-Type header and the Content-Length, those are important.
A POST is just the verb for when you have an HTTP document. A GET implies you got nothing.
So, in C#, here's a GET:
public static string HttpGet(string URI)
{
   System.Net.WebRequest req System.Net.WebRequest.Create(URI);
   req.Proxy = new System.Net.WebProxy(ProxyString, true); //true means no proxy
   System.Net.WebResponse resp = req.GetResponse();
   System.IO.StreamReader sr = new System.IO.StreamReader(resp.GetResponseStream());
   return sr.ReadToEnd().Trim();
}
Here's a POST:
public static string HttpPost(string URI, string Parameters)
{
   System.Net.WebRequest req = System.Net.WebRequest.Create(URI);
   req.Proxy = new System.Net.WebProxy(ProxyString, true);
   //Add these, as we're doing a POST
   req.ContentType = "application/x-www-form-urlencoded";
   req.Method = "POST";
   //We need to count how many bytes we're sending. Post'ed Faked Forms should be name=value&
   byte [] bytes = System.Text.Encoding.ASCII.GetBytes(Parameters);
   req.ContentLength = bytes.Length;
   System.IO.Stream os = req.GetRequestStream ();
   os.Write (bytes, 0, bytes.Length); //Push it out there
   os.Close ();
   System.Net.WebResponse resp = req.GetResponse();
   if (resp== nullreturn null;
   System.IO.StreamReader sr = new System.IO.StreamReader(resp.GetResponseStream());
   return sr.ReadToEnd().Trim();
}
I could and should have put in more 'using' statements, but you get the gist. And, there are other ways to have done this with the BCL, but this is one.
Now, how would you fake an HTTP PostBack? Use a tool like ieHttpHeaders to watch what a real postback looks like, and well, fake it. :) Just hope they don't require unique/encrypted ViewState (via ViewStateUserKey or EnableViewStateMac) for that page, or you're out of luck.

Monday, January 9, 2012

Converting Google Maps v2 to v3


Problem:

I was given a project where the client switched from using http to https.  Everything worked fine except for their Google Maps page.  The page  had a warning in IE about unsecure mixed content.  This was because Google Maps was pulling from an http (unsecured) address.  It turns out that the Map was using version 2 of Google Maps API which does not support https.  Only version 3 does.

Solution:

Don’t be fooled into thinking this is a straight forward, simple upgrade.  It is not.  This solution is just a log of what I did to get the site from v2 up to v3.  It is in no way  a complete guide.  The site used EWindow instead of the infoWindow because of its customized look.  So I guess the following could be a rough step-by-step guide to converting the Ewindow script to v3.

In general you’ll need to replace all “G” prefixes like “GSize” with “google.maps” like “google.maps.Size”.
Replace GSize with google.maps.Size
Replace GLatLng with google.maps.LatLng
Replace GPoint with google.maps.Point
Replace GOverlay with google.maps.Overlay
Replace “new google.maps.Overlay” with “new google.maps.OverlayView()”
Take out or replace GBrowserIsCompatible. There is no equivalent function in v3.
Replace
map = new GMap2(document.getElementById("map_canvas"))
with something like:
    var myOptions = {
      zoom: 4,
      center: myLatlng,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    }
    map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
Replace a custom icon marker using the icon-complex example:
http://code.google.com/apis/maps/documentation/javascript/examples/icon-complex.html
Before the change:
var tinyIcon = new GIcon();
tinyIcon.image = "images/gmap/marker.gif";
tinyIcon.iconSize = new google.maps.Size(22, 22);
tinyIcon.iconAnchor = new google.maps.Point(11, 11);
tinyIcon.infoWindowAnchor = new google.maps.Point(0, 0);
var myLatLng = new google.maps.LatLng(loc['lat'], loc['lng']);
var marker = new GMarker(myLatLng, { icon:tinyIcon });
GEvent.addListener(marker, showWindow, mouseover);
map.addOverlay(marker);
After:
var image = new google.maps.MarkerImage('images/gmap/marker.gif',
  // This marker is 20 pixels wide by 32 pixels tall.
  new google.maps.Size(22, 22),
  // The origin for this image is 0,0.
  new google.maps.Point(0,0),
  // The anchor for this image is the base of the flagpole at 0,32.
  new google.maps.Point(11, 11));
 
var myLatLng = new google.maps.LatLng(loc['lat'], loc['lng']);
var marker = new google.maps.Marker({
    position: myLatLng,
    map: map,
    shadow: shadow,
    icon: image
});
google.maps.event.addDomListener(marker,'mouseover', showWindow);
Replace getPoint to getPosition
var vx = marker.getIcon().iconAnchor.x - marker.getIcon().infoWindowAnchor.x;
var vy = marker.getIcon().iconAnchor.y - marker.getIcon().infoWindowAnchor.y;
this.openOnMap(marker.getPoint(), html, new google.maps.Point(vx,vy));
For infoWindowAnchor I could not find an equivalent so I scratched the whole calculation and just put in a hard coded value.
var vx = 10;
var vy = 10;
this.openOnMap(marker.getPosition(), html, new google.maps.Point(vx,vy));
Replace initialize:
EWindow.prototype.initialize = function(map) {
With onAdd:
EWindow.prototype.onAdd = function() {
Replace map.addOverlay(object) by moving a similar call to within the object.
map.addOverlay(ewindow)
To this within the object:
this.setMap(map)
Replace map.getPane()
map.getPane(G_MAP_FLOAT_SHADOW_PANE)
With this:
this.getPanes().mapPane
Replace this.redraw
this.redraw
with this.draw
this.draw
Replace Overlay.getZindex
var z = google.maps.Overlay.getZIndex(this.point.lat());
I could not find an equivalent to getZIndex so, again, it’s somewhat hard coded.
// you may need to work on this "hack" to replace V2 getZindex
// GOverlay.getZIndex(this.point.lat());
var z = 1000*(90-this.point.lat());
this.div_.style.zIndex = parseInt(z);
Replace this.map.fromLatLngToDivPixel
var p = this.map.fromLatLngToDivPixel(this.point);
With this:
var proj = this.getProjection();
var p = proj.fromLatLngToDivPixel(this.point);
Replace map.panBy(Size) to map.panBy(x:number, y:number)
map.panBy(new google.maps.Size(pan_right, pan_up));
With this:
map.panBy(-pan_right, -pan_up);
A very helpful site was one that made google maps compatible for both versions:
http://notebook.kulchenko.com/maps/google-maps-using-multiple-api-versions
And of course the Google Maps v3 Examples and API docs helped a great deal.  It may also help to look at version 2 of the API so that you can try and find something equivalent.
I uploaded a converted EWindow.js for whoever needs a google maps v3 version of EWindow.