How to implement Visual WebGui custom control
Visual WebGui allows fast and simple development of Web/Cloud applications while providing flexibility and extensibility to leverage existing 3rd party components. This article shows how to implement a custom control by integration of Aurigma Image Uploader for further usage in Visual WebGui projects. The implementation will be done by demonstrating of key concept of new skin designer introduced in version 6.4.
Categories: Custom Controls
|
Tags: Architects, Developers, Events, 2. Intermediate, 3. Advanced, Customization, v6.4 and Later
Revision: 2
Posted: 01/Jan/2010
Updated: 08/Nov/2011
Status: Publish
Types: Walkthrough
|
Requirements
Install Visual WebGui SDK 6.4 Beta 1 or later. Select the installation in according to version of MS Visual Studio 2005/2008.
Introduction
In this "how to" article we are going to implement a custom control to integrate functionality of existing component of Aurigma - Image Uploader. The Aurigma's component intended for uploading images or binary files to the server. The purpose of the article to explain how to provide client resources required for proper functioning of integrated control and how to interact with client side functionality. We are going to use new skin designer introduced in version 6.4 of Visual WebGui and intended for simple and fast resource management of a custom control in drag and drop manner.
Quick start
Create a project with Visual WebGui application template:
Validate that property "Copy Local" has value True for the references:
- Gizmox.WebGUI.Common
- Gizmox.WebGUI.Forms
- Gizmox.WebGUI.Forms.Server
Add a custom control for internals implementation of rendering and interaction with the integrated control:
After adding ImageUploader custom control we can see that 3 files were generated by Visual WebGui integration module:
ImageUploader.bmp – a bitmap image for identification on the MS Visual Studio Toolbar
ImageUploader.cs – a class that will contain the control's implementation of server side part.
ImageUploaderSkin.cs – a class required for customization of the look and behavior of the custom control and serves as a container for all client required resources like images, HTML, XSLT, JS scripts and other binary resources intended to be exposed to the client side by Visual WebGui gateway handler. The Aurigma's Image Uploader is pure client side component that requires multiple resources to run. We will see how these additional resources of CAB archive, JAR package and JS scripts will be added through the skin designer, to allow smooth running of custom control on the client side.
Control Registration
Compile the project - we should not receive any error or warning messages. At this step we have an initial of custom control and next required step is registration - so that the Visual WebGui server side might expose the resources. There are two ways to accomplish the registration. The first - we will rely on Visual WebGui integration with MS Visual Studio. Go to the project properties:
Keep closed the project's web.config to avoid the warning:
Close the web.config and re-open the project's properties registration tab.
Press Add at the section of "Controls" (scroll the pane to the right if necessary) to get the dialog of adding the specific control or a namespace:
We are interested in registration of specific control of ImageUploaded, so it was selected. Pay attention to filter option to search faster according to a case insensitive prefix. The namespaces tab allows selecting the namespace for faster and collective registration of group of controls. The second way is control registration by editing web.config file, <Controls> section. As result we should have the following in the project's web.config.
XML : web.config
<Controls>
<Control Type="VWGSamples.Aurigma.ImageUploader, VWGSamples.Aurigma, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" ></ControlType>
</Controls>
The Type attribute stands for full qualified name of registered control's type.
The base class
For further implementation we need to adjust the base class of the custom control to inherit from. There are few ready to use alternatives available from Gizmox.WebGUI.Forms.Hosts namespace:
- ActiveXBox - the choice to integrate and extend behavior of an ActiveX based control
- AppletBox - the choice to integrate and extend behavior of a Java applet
- AspPageBox - the choice to integrate and interact with a 3rd party ASP.NET page
- FlashBox - the choice to integrate and extend behavior of an Adobe Flash animation
- XamlBox - the choice to integrate a Silverlight application
All these controls are derivatives of ObjectBox or FrameControl. The best candidate for our mission is FrameControl. The FrameControl class was added in version 6.4 Beta 1 to serve custom implementations requiring support for IFRAME. In accordance we also change the custom's control skin to inherit from: FrameControlSkin. As the result we have:
C# : ImageUploader.cs
public partial class ImageUploader : FrameControl { ...
C# : ImageUploaderSkin.cs
public class ImageUploaderSkin : FrameControlSkin { ...
Pay attention to attributes of ImageUploader class. The attribute MetadataTag should be set to "FC". The default value of the attribute - "VWGSamples.Aurigma.ImageUploader" should be used when you are creating the HTML markup of the custom control with XSLT transformation. The reason to change the attribute value or delete the attribute at all is implementation where we are relying on HTML markup produced by FrameControl base class.
ImageUploader implementation
To render properly the HTML markup of custom control and to interact with integrated control we rely on general mechanism by overriding the implementation of IGatewayComponent interface. See also the article describing Visual WebGui gateways. The interface is implemented by default in RegisteredComponent class, which is base class for most of the Visual WebGui components. Every Visual WebGui component can declare itself as a gateway, this allow declaring virtual URLs that are handled by the control by declaring actions. The IGatewayComponent contains the method ProcessGatewayRequest that receives an action name and should return a gateway handler. The gateway handler processes the request in the same way as an HTTP handler does, which means that we can also use HTTP handlers. The additional scenarios where we can use gateways:
- Providing HTML based content to IFRAMES
- Providing a printable version of the current view
- Interacting with Java applets, Flash, ActiveX and other content
- Downloading files
Our custom control should render Aurigma's Image Uploader in IFRAME. The source HTML for IFRAME will be handled by "Content" action. To provide well formed URL for source attribute of IFRAME we will reference gateway by overriding Source property of FrameControl:
C# : ImageUploader.cs
protected override string Source
{
get
{
return (new GatewayReference(this,"Content")).ToString()
}
}
GatewayReference class is standard way to reference the gateway in difference contexts. The call to ToString() returns unique URL reference in the scope of application. The URL is set to source attribute of IFRAME element of HTML markup of the custom control and browser requests the content from there. To see how to handle the request for "Content" action, we need to review the override of ProcessGatewayRequest and ProcessContentRequest function:
C# : ImageUploader.cs
protected override IGatewayHandler ProcessGatewayRequest(HttpContext objHttpContext, string strAction)
{
if (strAction == "Content")
{
ProcessContentRequest(objHttpContext);
}
else if (strAction == "Upload")
{
ProcessUploadRequest(objHttpContext);
}
return null;
}
///<summary>
/// Processes the content request.
///</summary>
///<param name="objHttpContext">The HTTP context.</param>
private void ProcessContentRequest(HttpContext objHttpContext)
{
objHttpContext.Response.ContentType = "text/html";
XmlTextWriter objWriter = new XmlTextWriter(objHttpContext.Response.OutputStream, Encoding.UTF8);
objWriter.Formatting = Formatting.Indented;
objWriter.WriteStartElement("html");
objWriter.WriteStartElement("head");
objWriter.WriteElementString("title", "ImageUploader");
objWriter.WriteStartElement("script");
objWriter.WriteAttributeString("language", "javascript");
objWriter.WriteAttributeString("src", GetSkinResource("iuembed.js"));
objWriter.WriteValue(" ");
objWriter.WriteEndElement();
objWriter.WriteStartElement("script");
objWriter.WriteAttributeString("language", "javascript");
objWriter.WriteAttributeString("src", GetSkinResource("iuintegration.js"));
objWriter.WriteValue(" ");
objWriter.WriteEndElement();
objWriter.WriteStartElement("script");
objWriter.WriteAttributeString("language", "javascript");
objWriter.WriteValue(string.Format("var mstrControlId={0}; var mstrSessionID='{1}';", this.ID,
this.Session.SessionID));
objWriter.WriteEndElement();
objWriter.WriteEndElement();
objWriter.WriteStartElement("body");
objWriter.WriteAttributeString("style", "margin:2px;background-color:#F0F0F0;");
objWriter.WriteStartElement("script");
objWriter.WriteAttributeString("language", "javascript");
objWriter.WriteValue(GetScript());
objWriter.WriteEndElement();//script
objWriter.WriteEndElement();//body
objWriter.WriteEndElement();//html
objWriter.Flush();
objWriter.Close();
}
///<summary>
/// Gets the script.
///</summary>
///<returns></returns>
private string GetScript()
{
StringBuilder objScript = new StringBuilder();
objScript.AppendLine("var iu = new ImageUploaderWriter('ImageUploader', '100%', '100%');");
objScript.AppendLine("iu.activeXControlEnabled = true;");
objScript.AppendLine("iu.javaAppletEnabled = true;");
objScript.AppendLine(string.Format("iu.activeXControlCodeBase = '{0}';", this.GetSkinResource("ImageUploader6.cab")));
objScript.AppendLine(string.Format("iu.javaAppletCodeBase = '{0}';", this.GetSkinResource("ImageUploader6.jar")));
objScript.AppendLine(string.Format("iu.addParam('LicenseKey', '{0}');", this.GetSafeScriptString(this.LicenseKey)));
objScript.AppendLine(string.Format("iu.addParam('PaneLayout', '{0}');", this.GetSafeScriptString(this.PaneLayout)));
objScript.AppendLine(string.Format("iu.addParam('Action', '{0}');", this.Action));
objScript.AppendLine("iu.addEventListener('AfterUpload', 'OnUploadComplete');");
objScript.AppendLine("iu.addEventListener('BeforeUpload', 'OnBeforeUpload');");
objScript.AppendLine("iu.addParam('BackgroundColor', '#F0F0F0');");
objScript.AppendLine("iu.writeHtml();");
return objScript.ToString();
///<summary>
/// Processes the upload request.
///</summary>
///<param name="objHttpContext">The HTTP context.</param>
private void ProcessUploadRequest(HttpContext objHttpContext)
{
// Check upload is valid
if ((!string.IsNullOrEmpty(objHttpContext.Request.Form["PackageGuid"]) && this.IsRequest ...
{
// Get the file count
int intFileCount = int.Parse(objHttpContext.Request.Form["FileCount"]);
// Create a files list for collecting posted files
List<ImageUploaderFile> objFiles = new List<ImageUploaderFile>();
// Clear the previous files value
this.RemoveValue<ImageUploaderFile[]>(ImageUploader.FilesProperty);
// Loop all files
for (int intFileIndex = 1; intFileIndex <= intFileCount; intFileIndex )
{
objFiles.Add(new ImageUploaderFile(objHttpContext, intFileIndex));
}
// Set the files collection
this.SetValue<ImageUploaderFile[]>(ImageUploader.FilesProperty,
objFiles.ToArray());
}
}
We need to review the few rows in the code above.
The response's content type should be properly set to "text/html" to make sure the content will be received by browser.
The XmlTextWriter used to format properly the response as well formed DHTML.
As I already mention above, the Aurigma's Image Uploader component requires a few resources to function properly on the client side. More specifically, we need to deliver CAB archive with implementation of ActiveX control for Internet Explorer clients, Java jar package for non MS browsers and a JS script containing client side logic. The code creates HTML markup as required by Aurigma's Image Uploader programmer's reference. We need to provide values for "src" attributes to reference the location of resources required for running: iuembed.js, iuintegration.js, ImageUploader6.cab and ImageUploader6.jar. The skin is also a container of binary resources. The call to GetSkinResource function with the name of the resource is nothing else than getting the gateway reference to the resource exposed by Visual WebGui skin engine.
For correct functioning of integrated Image Uploader we need to set 'Action' to a server side resource handling the HTTP post from the client side. To handle it we need to provide additional gateway handler with "Upload" action and implement HTTP post processing according to rules described in programmer's reference. See the code in ProcessUploadRequest function. The uploaded images are available in the request and are iterated one by one and converted to ImageUploaderClass for further processing.
The interaction between client side of the integrated control and server side is implemented by raising and handling the events. That is example of common practice for communication between client side and Visual WebGui server side. We need to add additional JS script and subscribe the function as a listener for 'AfterUpload' to raise a Visual WebGui event to inform the server side of custom control. Pay attention to mstrControlId rendered in according to ID property to provide correct identification of control raising the event:
JavaScript : ImageUploaderSkin.iuintegration.js
function OnUploadComplete(strHtml)
{
// Get the reference to the owner application
var objApp = window.parent.mobjApp;
// If found valid application
if(objApp)
{
// Create event and raise it
objApp.Events_CreateEvent(mstrControlId,"UploadComplete");
objApp.Events_RaiseEvents();
}
}
function OnBeforeUpload()
{
if (iu.getControlType() == 'Java') {
var strSessionID = 'ASP.NET_SessionId=' mstrSessionID;
getImageUploader('ImageUploader').AddCookie(strSessionID);
}
The raised event should be handled by overridden implementation of FireEvent function. The implementation recognizes the event and raises standard .NET Framework event for further processing of uploaded images and binaries. Don’t miss the call to the base class to handle all other kind of events.
C# : ImageUploader.cs
protected override void FireEvent(IEvent objEvent)
{
// If is upload complete event
if (objEvent.Type == "UploadComplete")
{
// Get the event handler if possible
EventHandler objFileUploadedHandler = this.FileUploadedHandler;
if (objFileUploadedHandler != null)
{
// Raise the uploaded event
objFileUploadedHandler(this, EventArgs.Empty);
}
}
else
{
base.FireEvent(objEvent);
}
}
Aurigma Image Uploader has dual implementation with ActiveX in IE and Java applet in non-IE browsers like FireFox and Chrome. The existing issue with Java applet is requirement to preserve session ID to guarantee that post with uploaded images will be handled on the server side properly. In according to recommendation here and only in case of Java applet OnBeforeUpload called to add cookie with value of session ID previously rendered in global variable mstrSessionID.
Visual WebGui Skin designer
We already saw how to use the resources to guarantee functioning of client side of integrated control. The skin designer is a new tool for managing the different resources required for a custom control. The examples of usage could be seen in the sources of Gizmox.WebGUI.Forms assembly. There is definition of a skin for each Visual WebGui control and could be good reference for further study. When you plan to implement the custom control you need to decide how to create a look and behavior. You can use an XSLT sheet as a resource in the skin to run the transformation on XML and dynamically create a HTML markup of the control or to use an HTML resource as a template. The behavior of a custom control based on script files. There are additional settings to limit the usage of a script in a target browser family, to reduce the size of the script by stripping the comments and white spaces and to obscure the code.
That is the time to see how to add the resources to complete the skin of our custom control, easily and in drag and drop manner. Right click the ImageUploaderSkin.cs item in project's solution explorer and click the "View Designer" menu item. There is a pane on the left side for adding different kinds of resources like images, scripts, XSLT stylesheets, HTML files and binary resources ("Other" section). We are going to add two JavaScript files and two binary files. Select "Scripts" menu item from drop down and drag the files iuembed.js, iuintegration.js to the pane or use the "Add Resourse" item. Both will be added to the ImageUploaderSkin. Repeat the adding of ImageUploader6.cab and ImageUploader6.jar in "Other" section. That is the only action required to expose these resources.
Switch the resource view pane to "Scripts" to set the settings of added script files. Click a resource and press F4 to see the properties window. To adjust target browser family set "Presentation" property to "Browser" and select the value in "Presentation Engine". To make the resource unaware to the browser – set "Presentation" to "Agnostic". The same set of settings available for CSS and XSLT resources.
PropertyStore
The version 6.4 of Visual WebGui contains implementation of "PropertyStore" - general purpose pattern for optimization of serialization and memory allocations. In other words - to save space when storing property values. In general instead of using instance fields, the dictionary-like PropertyStore is used. Imagine a control that inherits ~60 properties from Gizmox.WebGui.Forms.Control class. That means on instantiation of this control without PropertyStore - ~60 values will be allocated and default values will be assigned for each one, but the only few of them actually assigned by user to non-default values.
A typical application has multiple forms with tens controls on each form. For each active user the session object contain memory print for all opened forms. That means significant memory pressure on the server.
The PropertyStore implementation make possible to allocate the memory only when a property set to non default value or at least should be set or get. More than that! When a property assigned to a default value – actually the value will be deleted from PropertyStore. Such way when only few properties are changed in application – the memory allocated for values of only these few properties.
Same improvement was made for the events. In common practice the only few events of a control or a form are actually listened – that means that the memory will be allocated only for these that are actually listened.
While implementing the custom control and to take advantage from using the PropertyStore we need to declare and implement the properties in slight a different way.
The property declaration:
C# : ImageUploader.cs
///<summary>
/// The LicenseKey property registration.
///</summary>
private static readonly SerializableProperty LicenseKeyProperty =
SerializableProperty.Register("LicenseKey", typeof(string),
typeof(ImageUploader));
The property implementation:
C# : ImageUploader.cs
[DefaultValue("")]
public string LicenseKey
{
get
{
return this.GetValue<string>(ImageUploader.LicenseKeyProperty,
string.Empty);
}
set
{
if (this.LicenseKey != value)
{
this.SetValue<string>(ImageUploader.LicenseKeyProperty, value);
this.Update();
}
}
}
An event declaration and implementation example:
C# : ImageUploader.cs
///<summary>
/// The FileUploaded event registration.
///</summary>
private static readonly SerializableEvent FileUploadedEvent =
SerializableEvent.Register("FileUploaded", typeof(EventHandler),
typeof(ImageUploader));
///<summary>
/// Occurs when files where uploaded.
///</summary>
public event EventHandler FileUploaded
{
add
{
this.AddHandler(ImageUploader.FileUploadedEvent, value);
}
remove
{
this.RemoveHandler(ImageUploader.FileUploadedEvent, value);
}
}
Put it all together
The custom control integrates functionality of Aurigma's Image Uploader to be used in a Visaul WebGui application. The essential properties and events were exposed in interface of the custom control like LicenseKey, PaneLayout and Files. To build an application while using the developed general purpose custom control let's add a Visual WebGUI UserControl – PictureBrowser, to encapsulate the logic of the concrete application. The PictureBrowser contains the instance of ImageUploader custom control and an instance of TextBox. Each time the end user selects and uploads graphics or binaries to the server – the details of uploaded files will be shown in the text box.
C# : PictureBrowser.cs
public partial class PictureBrowser : UserControl
{
private ImageUploader mobjImageUploader;
private TextBox mobjTextOutput;
public PictureBrowser()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
mobjTextOutput = new TextBox();
mobjTextOutput.Multiline = true;
mobjTextOutput.Dock = DockStyle.Fill;
this.Controls.Add(mobjTextOutput);
mobjImageUploader = new ImageUploader();
mobjImageUploader.Dock = DockStyle.Top;
mobjImageUploader.Height = 600;
mobjImageUploader.FileUploaded = new EventHandler(OnImageUploaderFileUploaded);
this.Controls.Add(mobjImageUploader);
}
private void OnImageUploaderFileUploaded(object sender, EventArgs e)
{
StringBuilder objOutput = new StringBuilder();
foreach (ImageUploaderFile objUploadedFile in mobjImageUploader.Files)
{
if (objUploadedFile.PostedFile != null)
{
objOutput.AppendLine(objUploadedFile.PostedFile.FileName);
}
}
mobjTextOutput.Text = objOutput.ToString();
}
}
Pay attention to event handler implementation for FileUploaded event. The handler iterates the uploaded files after the upload completed. The concrete user control might encapsulate application business logic ignoring the complexity of underlying custom control. From point of view of user control developer – the ImageUploader is just an additional Visual WebGui control. Because the custom control developed on top of Visual WebGui infrastructure it ensures performance and cross browser support.
The example application screenshot:
Summary
The article's related code contains implementation of the custom control and an example application. The code highlights the important steps of integration a 3rd party component into a Visual WebGui control: registration, base class selection, rendering and client resource management with the skin designer. Review the code to see how to use properties and events with PropertyStore optimization. An example application demonstrates the usage of developed custom control in a user control.
See also
Visual WebGui SDK Help in the installation
Visual WebGui Wiki
Visual WebGui Official developers resources
Aurigma Image Uploader
Code using HostContext - 6.4.0 Release and later
Download
Code using HttpContext - 6.4 versions prior to 6.4.0 Release
About the author
Related Articles
Custom Controls |
|
|
I played a bit around with the new 6.4 ListView control, its quite amazing what you can do with it. It opens a lot of new ways to present data in a better and more userfriendly way.
Tags: Architects, Developers, Data Binding, C#, 2. Intermediate, 3. Advanced, Customization, Data Binding, v6.4 and Later
|
|
|
In this How to we are going to learn the basic usage of Visual WebGui RIA Platform Theme & Control designer.
Tags: Developers, Graphic Designers, Theme, 1. Beginner, 2. Intermediate, Customization, v6.4 and Later, 3. Advanced
|
|
|
Tags: Developers, Events, JavaScript, 1. Beginner, 2. Intermediate, Integration, Pre v6.3, v6.3, 3. Advanced
|
|
|
Tags: Developers, Events, JavaScript, 2. Intermediate, 3. Advanced, Integration, Pre v6.3, v6.3
|
|
|
Tags: Developers, Graphic Designers, Theme, 1. Beginner, 2. Intermediate, Customization, v6.4 and Later, 3. Advanced
|
|
|
Tags: Developers, Events, JavaScript, 3. Advanced, Customization, v6.3
|
|
|