Browse DevX
Sign up for e-mail newsletters from DevX


Using the ASP.NET Runtime to Extend Desktop Applications with HTML Scripts : Page 3

People often think of HTML as the sole domain for Web applications. But HTML's versatile display attributes are also very useful for handling data display of all sorts in desktop applications. The Visual Studio .NET start page is a good example. Coupled with a scripting/template mechanism you can build highly extendable applications that would be very difficult to build using standard Windows controls. In this article, Rick introduces how to host the ASP.NET runtime in desktop applications and utilize this technology in a completely client-side application using the Web Browser control.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Passing Parameters to the ASP.NET Page
So far I've shown you how to do basic scripting. However, when you build template-based applications it's not good enough to just process code in templates. You have to also be able to receive data from the calling application. In the previous examples you've been limited by small text parameters that you can pass via Query string. While you can probably use the Query string to pass around serialized data from objects and datasets, this is really messy and requires too much code on both ends of the script calling mechanism.

My idea of a desktop application that utilizes scripts dictates that the application performs the main processing while the scripts act as the HTML display mechanism. To do this I need to pass complex data to my script pages.

wwAspRuntimeProxy provides a ParameterData property that you can assign any value to and it will pass this value to the ASP.NET application as a Context item named "Content" which you can then retrieve on a form. To call a script page with a parameter you do this:

// *** Using the wwAspRuntime Class to execute // an ASP.Net page loHost = AspRuntimeProxy.Start(Directory. GetCurrentDirectory() + @"\WebDir\","/LocalScript"); loHost.cOutputFile = Directory.GetCurrentDirectory() + @"\WebDir\__Preview.htm"; ... cCustomer loCust = new cCustomer(); loCust.cCompany = "West Wind Technologies"; loHost.ParameterData = loCust; this.oHost.ProcessRequest("PassObject.aspx",null);

But, I first need to do a little more work and make a few changes. SimpleWorkerRequest doesn't provide a way to pass properties or content to the ASP.NET page directly. However, I can subclass it and implement one of its internal methods that hook into the HttpRuntime processing pipeline. Specifically, I can implement the SetEndOfSendNotification() method to receive a reference to the HTTP Context object that is accessible to my ASP.NET script pages and assign an object reference to it. Listing 5 shows an implementation of SimpleWorkerRequest that takes the ParameterData property and stores into the Context object.

I'll implement the constructor by simply forwarding the parameters to the base class. The SetEndOfSendNotification method gets fired just before processing is handed over to the ASP.NET page after any request data has been provided. The extraData parameter, at this point, contains an instance of the HttpContext object that you can access in your ASP.NET pages with:

object loData = this.Context.Items["Content"];

And voilà! You can now access object data. This subclass passes a single object, which for most purposes should be enough. If you need to pass more than one object you can simply create a composite object and hand multiple object references off to the composite object to pass multiple items. Of course, you can also create a more complex class and add as many properties as you need to pass into the Context object. Note that you must mark any objects and sub-objects passed in this fashion as Serializable.

[Serializable()] public class cCustomer { public string cCompany = "West Wind Technologies"; public string cName = "Rick Strahl"; public string cAddress = "32 Kaiea Place"; public string cCity = "Paia"; public string cState = "HI"; public string cZip = "96779"; public string cEmail = "rstrahl@west-wind.com"; public cPhones oPhones = null; public cCustomer() { this.oPhones = new cPhones(); } }

Alternately, you can derive a class from MarshalByRefObject to make it accessible over the wire:

public class cPhones : MarshalByRefObject { public string Phone = "808 579-8342"; public string Fax = "808 579-8342"; }

Before you can utilize this functionality you need to change a couple of things in the wwAspRuntimeProxy class. First, you need to add a parameter called ParameterData that will hold the data you want to pass to the ASP.NET application. Next, you need to change the code in the ProcessRequest method to handle a customer worker request class to use the wwWorkerRequest class instead of SimpleWorkerRequest.

wwWorkerRequest Request = new wwWorkerRequest(Page, QueryString, loOutput); Request.ParameterData = this.ParameterData;

You should also pass the ParameterData property forward. To execute a script with the object contained within it, check out the PassObject.aspx script page shown in Listing 6.

Please note a few important points here. Notice that you need to import the assembly and namespace of any classes that you want to use in the script. Since I declared the assembly in my main application (AspNetHosting.exe with a default namespace of AspNetHosting), I have to include the Exe file as an assembly reference. If you require any other non-System namespaces or assemblies you will have to reference those as well.

You should omit the .EXE or .DLL extensions of any included assemblies. If you try to run with the extension you will get an error as the runtime tries to append the extensions as it searches for the file.

Since you imported the namespace and assembly, you can reference your value by its proper type and add it to a property that I added to the script page (oCust). To assign the value you must cast it to the proper cCustomer type.

<% this.oCust = (cCustomer) this.Context.Items["Content"]; %>

Once I've done this, you can access this object as needed by using its property values. To embed it into the page you can use syntax like this.

Customer Name: <%= this.oCust.cName %>

You can also call methods this way. For example, if you add this method to the Customer object:

public string CityZipStateString() { return this.cCity + ", " + this.cState + " " + this.cZip; }

You can then call it from the script page like this:

City: <%= this.oCust.CityZipStateString() %>

You can easily execute business logic right within a script page! However, I recommend that you try to minimize the amount of code you run within a script page, rather than rely on it to provide the dynamic and customizable interface for the application. So, rather than passing an ID via the query string then using the object to load the data to display, instead use the application to perform the load operation and simply pass the object to the page that you want to display. You must make the object you want to pass in some way serializable to pass over the AppDomain boundaries.

You'll find it easy to set up ASP.NET scripting. You can configure the application handling even more by using a web.config file as shown in Listing 7.

I suggest two extremely useful settings that you can make. First, you should set debug to True to allow you to debug your scripts. If you have this setting in your application you can debug your scripts right along with your application. Simply open the script in the Visual Studio environment, set a breakpoint in the script, then run the application. You'll hit the script and voilà, you can debug your script code with all of the Visual Studio debugging features.

Once you've loaded a program or script into an AppDomain you cannot unload it unless you unload the AppDomain.
If you don't have an existing Visual Studio project you can still use the debugger against the Executable.

<VS Path>\devenv /debugexe AspNetHosting.exe

Let Visual Studio create a new solution for you! Open the page to debug, set a breakpoint, and off you go. This is a very cool feature that you can offer to your customers as well, so they can more easily debug their scripts. Along the same lines, you can use the Visual Studio editor to edit your scripts as well, although you should try and stay away from all of the Web Forms-related stuff because that's meant for server side development. You can implement this, but frankly I think you'll be much better off dealing with these issues in your regular application code.

Second, when you build template-based applications you might prefer to use extensions other than ASPX for your scripts. You can do this by adding httpHandlers into the Config.Web file as shown above. Set each extension to the same System.Web.UI.PageHandlerFactory as ASPX files (set in machine.config) are set, then you can process those files with those extensions through the scripting runtime. Unlike ASP.NET, you don't need script maps to make this work because you're in control of the HttpRuntime locally.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date