|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThe ASP.NET development platform provides to easily design and publish Web forms. For certain applications however, it is necessary to keep those forms dynamically variable and allow to embed them in a dynamic fashion too. There are scenarios, where the creation of those forms is separated from the application development process. Such dynamic forms might have a life cycle as follows:
Each of these areas has its own requirements in regard to dynamic form management:
Solution Approach
The Solution Provider develops the Form Controls library, which serves for designing the Web forms. The library contains elements for structuring the forms as well as the available form fields. The library will be delivered to the Form Designer in combination with a design Web site. The actual design of the forms will be done in a Web-enabled version of Visual Studio, such as for example, the freely available Visual Web Developer 2008 Express Edition. After installation of the Form Controls library into a Visual Studio Toolbox, the form can be designed in an ASP.NET Once the design of the form is fixed, it will be deployed. Forms can be copied to the Web application in a conventional way, or might be provided from sources outside the actual Web application itself (FTP, database, etc.). Different mechanisms allow the integration of the forms into the Web Application while handling security relevant aspects. At runtime, the forms will be loaded dynamically into the Web Application. Depending on individual needs, runtime controls get invoked, lookups become initialized, form data gets loaded from Data Storage and runtime variables get expanded. Replacing a placeholder, the After the Web user has entered the form data and confirmed them, the collected form data gets transferred to the Data Storage. Form ModelsThere exist two representational models for the Web user form:
The UI model represents each part of a form as a control and is used at form design time as well as at runtime for user data entry. The data model abstracts and contains the field values of the form. The following overview compares the two models:
Form Controls LibraryThe Form Controls Library implements the UI model of the form. Based on the ASP.NET
The form header Form fields implement the
The accompanying project
The controls Forms can be supplemented with own or third party controls as necessary. Such controls however, can often not be delivered to the form designer, be it for deployment or licensing related issues. In such situations the design will use a placeholder control instead, which will be replaced by the actual own or third party control at runtime. The controls A form can execute commands through the Design Time SupportThe design mode of the form provides the designer with helpful information through coloring the form fields according to their state. Visual Studio allows to control the representation of controls at design time through inheriting from
The control
The control Side note: to allow debugging a
Form Controls Library DeploymentThe project The Form Controls library has to be installed at the form designer™s workstation. The control library can be installed into a Visual Studio Toolbox manually or with a tool such the Visual Studio Toolbox Manager.
For an easy start, the form designer should be provided with a Web site which shows the various aspects of form design. The included project Form DesignOnce the form controls library has been installed as a toolbox, the form designer can create a new form with the following steps:
Sub-forms can be integrated by inserting the child The Web User Forms Properties allow to configure the form elements. Depending on the type of control, differing properties are available:
The examples Form Business RulesBusiness rules can be integrated in a variety of ways:
Using any of the CLR programming languages such as C# or VB.NET is not recommended for security reasons. How to suppress the execution of such code in forms at runtime will be shown further below. The following sample demonstrates the usage of JavaScript in a form: <script language="javascript">
// --- init form field ---
function initFormField()
{
var field = document.getElementById( "<%# TextBox1.ClientID %>" );
if ( field != null )
{
field.value = Date();
}
}
// --- show form field ---
function showFormField()
{
var field = document.getElementById( " <%# TextBox1.ClientID %>" );
if ( field != null )
{
alert( field.value );
}
}
// --- register form loading function ---
function addLoadEventHandler( func )
{
var previousHandler = window.onload;
if ( typeof window.onload != "function" )
window.onload = func;
else
window.onload = function()
{
previousHandler();
func();
}
}
// --- add form init function ---
addLoadEventHandler( initFormField );
</script>
<button onclick="showFormField()">Show Form Field</button>
<cc1:TextBox ID="TextBox1" runat="server" FieldName="MyField" />
Notice the access to the form control through the binding directive Form Design GuidelinesThe following rules should be respected when designing forms:
Form DeploymentForms can be deployed either statically or dynamically. Static deployment consists of simply copying the *.ascx files into the directory of the Web application. The form repository in this case is represented by the file system of the Web application. With dynamic deployment, the Solution Provider offers an individual way to the Form Designer to store a form into a form repository. Such a repository can be a database, a remote or FTP folder, a Web service or any other storage means. Forms at RuntimeUsage of forms at application runtime consists of the following phases:
Loading of FormThe class In case the form contains .NET CLR code which is not allowed, the loading process will raise a <script runat="server" language="C#">
public void MyPublicMethod( object sender, EventArgs e )
{
System.IO.DirectoryInfo dir = new System.IO.DirectoryInfo("C:\\");
// do something with dir
} // MyPublicMethod
protected void MyProtectedMethod( object sender, EventArgs e )
{
System.Diagnostics.Process.Start( "C:\\AUTOEXEC.BAT" );
} // MyProtectedMethod
private void MyPrivateMethod( object sender, EventArgs e )
{
System.Threading.Thread.CurrentThread.Abort();
} // MyPrivateMethod
</script>
<%Response.Write( "Embedded Code generated output." ); %>
The method The forms get loaded from a virtual path, which normally represents a file in the local application directory. By inheriting from the class // ------------------------------------------------------------------------
public class MyUserFormProvider : VirtualUserFormProvider
{
// ----------------------------------------------------------------------
public MyUserFormProvider( string basePath )
: base( basePath )
{
} // MyUserFormProvider
// ----------------------------------------------------------------------
protected override Stream LoadUserForm( string virtualPath )
{
MemoryStream stream = null;
// load Web-form from you source: file system, database, Web-Service...
string formControlData = GetWebFormFromMySource( virtualPath );
if ( !string.IsNullOrEmpty( formControlData ) )
{
stream = new MemoryStream( Encoding.Default.GetBytes( formControlData ) );
}
return stream;
} // LoadUserForm
} // class MyUserFormProvider
The included Form Runtime ControlsIn case the form allows runtime controls, the The provided project Form ScriptingFor forms which access a controls // --------------------------------------------------------------------------
public partial class ClientScriptFormPage : System.Web.UI.Page
{
// ------------------------------------------------------------------------
protected override void OnLoad( EventArgs e )
{
base.OnLoad( e );
DataBind(); // resolve data-binding expressions
} // OnLoad
} // class ClientScriptFormPage
LookupsControl of the forms lookups happens with the
The // --------------------------------------------------------------------------
public partial class LookupsPage: System.Web.UI.Page, ILookupProvider
{
// ------------------------------------------------------------------------
protected override void OnLoad( EventArgs e )
{
// load form control
UserCotrol userForm = new UserFormLoader( "~/MyUserForm.ascx" ).Load();
// lookup
LookupAdapter.Apply( this, userForm );
FormPlaceHolder.Controls.Add( userForm );
base.OnLoad( e );
} // OnLoad
// ------------------------------------------------------------------------
ILookupValueCollection ILookupProvider.GetLookup( string lookupName, string formType )
{
LookupValueCollection lookup = null;
switch ( lookupName )
{
case "MaritialStatus":
lookup = new LookupValueCollection( lookupName );
lookup.Add( new LookupValue( "Single" ) );
lookup.Add( new LookupValue( "Married" ) );
lookup.Add( new LookupValue( "Separated" ) );
lookup.Add( new LookupValue( "Divorced" ) );
lookup.Add( new LookupValue( "Widowed" ) );
lookup.Add( new LookupValue( "Engaged" ) );
lookup.Add( new LookupValue( "Annulled" ) );
lookup.Add( new LookupValue( "Cohabitating" ) );
lookup.Add( new LookupValue( "Deceased" ) );
break;
}
return lookup;
} // ILookupProvider.GetLookup
} // class LookupsPage
Form VariablesExpressions and variables in a form will be handled by the class
The // --------------------------------------------------------------------------
public partial class VariablesPage: System.Web.UI.Page, IVariableProvider
{
// ------------------------------------------------------------------------
protected override void OnLoad( EventArgs e )
{
// load form control
UserCotrol userForm = new UserFormLoader( "~/MyUserForm.ascx" ).Load();
// expand variables
VariableAdapter.ExpandVariables( this, userForm );
FormPlaceHolder.Controls.Add( userForm );
base.OnLoad( e );
} // OnLoad
// ------------------------------------------------------------------------
IVariableSet IVariableProvider.GetVariables( string formType )
{
VariableSet variableSet = new VariableSet();
variableSet.MapContentToVariable( "CurrentDate", DateTime.Now.ToString() );
variableSet.MapContentToVariable( "CurrentUser", "John Doe" );
return variableSet;
} // IVariableProvider.GetVariables
} // class VariablesPage
Form PersistenceForm data can be loaded and saved (and thus exchanged) through the entity model. The class // --------------------------------------------------------------------------
public partial class PersistentPage : System.Web.UI.Page
{
// ------------------------------------------------------------------------
protected override void OnLoad( EventArgs e )
{
// load form control
this.userForm = new UserFormLoader( "~/UserForms/MyUserForm.ascx" ).Load();
FormPlaceHolder.Controls.Add( this.userForm );
if ( !Page.IsPostBack )
{
LoadFormData();
}
base.OnLoad( e );
} // OnLoad
// ------------------------------------------------------------------------
private void LoadFormData()
{
// file check
string fileName = MapPath( virtualFileName );
if ( !File.Exists( fileName ) )
{
return;
}
// load form data
using ( StreamReader streamReader = new StreamReader( fileName ) )
{
IForm form = FormXml.Instance.Load( streamReader );
UserFormAdapter.ApplyForm( this.userForm, form );
}
} // LoadFormData
// ------------------------------------------------------------------------
private void SaveFormData()
{
IForm form = UserFormAdapter.ExtractForm( this.userForm );
string fileName = MapPath( virtualFileName );
if ( File.Exists( fileName ) ) // update existing form
{
File.Delete( fileName );
form.MarkUpdated( DateTime.Now, "DemoUser" );
}
else // new form
{
form.FormId = "1";
form.SetCreated( DateTime.Now, "DemoUser" );
}
// ensure directory
string directory = new FileInfo( fileName ).DirectoryName;
if ( !Directory.Exists( directory ) )
{
Directory.CreateDirectory( directory );
}
// save data
using ( StreamWriter streamWriter = new StreamWriter( fileName ) )
{
FormXml.Instance.Save( form, streamWriter );
UserFormAdapter.ApplyForm( this.userForm, form );
}
} // SaveFormData
// ------------------------------------------------------------------------
protected void SaveButton_Click( object sender, EventArgs e )
{
SaveFormData();
} // SaveButton_Click
// ------------------------------------------------------------------------
protected void LoadButton_Click( object sender, EventArgs e )
{
LoadFormData();
} // LoadButton_Click
// ------------------------------------------------------------------------
// members
private UserControl userForm;
private const string virtualFileName = "~/Data/UserFormData.xml";
} // class PersistentPage
Form CommandsHandling of form commands is achieved through the class // --------------------------------------------------------------------------
public partial class CommandPage : System.Web.UI.Page
{
// ------------------------------------------------------------------------
protected override void OnLoad( EventArgs e )
{
// load form control
this.userForm = new UserFormLoader( "~/UserForms/MyUserForm.ascx" ).Load();
this.commandManager = new UserFormCommandManager( this.userForm );
this.commandManager.Command += new CommandEventHandler( FormCommand );
FormPlaceHolder.Controls.Add( this.userForm );
UpdateCommands( UserFormAdapter.ExtractForm( this.userForm ) );
base.OnLoad( e );
} // OnLoad
// ------------------------------------------------------------------------
private void UpdateCommands( IForm form )
{
this.commandManager.EnableCommand( commandFormLock, !form.IsLocked );
this.commandManager.EnableCommand( commandFormUnlock, form.IsLocked );
} // UpdateCommands
// ------------------------------------------------------------------------
private void ChangeFormLock( bool isLocked )
{
IForm form = UserFormAdapter.ExtractForm( this.userForm );
form.IsLocked = isLocked;
UpdateCommands( form );
} // ChangeFormLock
// ------------------------------------------------------------------------
private void FormCommand( object sender, CommandEventArgs e )
{
switch ( e.CommandName.ToLower() )
{
case commandFormLock:
ChangeFormLock( true );
break;
case commandFormUnlock:
ChangeFormLock( false );
break;
}
} // FormCommand
// ------------------------------------------------------------------------
// members
private UserControl userForm;
private UserFormCommandManager commandManager;
private const string commandFormLock = "formlock";
private const string commandFormUnlock = "formunlock";
} // class CommandPage
Form UtilitiesThere exist several helpers to control the editing status:
Future ExtensionsThe component introduced herein provides room for manifold extension possibilities which would go way beyond the scope of this article:
History
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||