Sign In
   Developer Productivity PKGs
Classic .NET Website Design
Accounting Suite for IBM i

Taking Advantage of Application State in ASP.NET



the Essence

This technical article provides a programming example for populating application level information in an ASP.NET web application.  From an application software developer's perspective, the essential technical feats involved are:

  • determining what multi-use data will be useful and accurate for application state
  • populating and storing the most applicable type of objects into application level memory space
  • accessing the database
  • pulling from application state memory at run time to re-populate the objects or transfer the information into its most useful format
  • initially populating the UI dropdown list box controls on the web page
Why are we here?

Good reasons for reading further into this TechCrystal...

  • To learn about application state and see an example of how it can be used

  • To see syntactical nuances and an example for instancing and populating

    System.Web.UI.WebControls.ListItemCollection


  • If you are new to OO syntax, ASP.NET or Web Forms, the introductory and tutorial-like explanations

  • To see an AJAX toolkit UpdatePanel example

  • To see how a web site project using more than one programming language is structured

  • To see the ease of using READE, Read Equal to Key, in Visual RPG for .NET programming language
the Enclosed Example

Our example for populating application state gathers a list of countries, states and provinces for US and Canada.  The clsAppViewState class generally runs just once for the application per website start.  It can be triggered to run during Application_Start most easily, but alternatively during the first Session_Start or the first session that needs the data (in order to reduce overhead during website start).

Our clsAppViewState class serializes the data using supplied framework feature(s).  The individual web pages access the data and de-serialize it later as needed.  As a general practice, Tegratecs also populates static properties of a namespace based on web.config settings at Application_Start time within our TegratecsWebBaseASPNET product, but that is going to have to wait for a separate TechCrystal.


Country and State Drop Downs

(populated via info from application state memory)

AVR.NET WebSite Example 1 Project


a little more about what data should get stored at the application level

If any of the lists of interest for application state had company specific permissions or nuances involved in presentation, then the data would not be quite as good of a candidate for storing at the application state level.  This is because, at a minimum, permissions programming would need to be coupled with the data, potentially bringing into play a number of other considerations (like where the programming is to be placed most properly and whether there is a potential permissions exposure).

A Bunch More Background

So it is probably a good idea for the senior developer to ascertain whether some business application data is best delivered using the features of the application level server memory (application state), as opposed to accessing the same data from the database over and over each time a web page is formulated (or once per session or once per page)

In addition to application state in ASP.NET, there are other environmental goodies provided such as view state and session state.  These are available to both web site and web application project structures.

View state information is specific to the web form and for the most part, travels between browser and server as hidden encrypted text within the web page content.  It holds not only information typed into text boxes but other status indications within the GUI such as drop down selections.  It is automatically populated by the ASP.NET web forms framework, however the developer can add viewstate variables.  View state data or an alternative method is pretty much necessary due the nature of the web browser client, that, in its base implementation, does not remain connected to the server (hence deemed "stateless") and thus only has an active memory space for view state when the web page is being formulated on the web server.

Session state is specific to the session and in general, not carried within the web page but stored on the server in memory (default) or on a server DB such as SQL Server (sessionState mode = "SQLServer" in web.config).  Session state is formed around a unique session ID (or method of identication for each particular user session), which is determined based on IP address, type of browser in use, start time and other facets.  Pretty much every web server has to be able to store session state info for each browser user and to be able to re-link incoming HTTP requests with that stored session info during each round trip between client (browser or mobile) and server. 

Application state information is also stored on the web server and is tied to the website boundaries as defined in IIS.  It is accessible to all user sessions.  We access it most typically during the formulation of certain user session web pages via the code-behind.

There are events triggered during the website application life cycle such as Application_Start and Session_Start.  Coupled with these events are various setup opportunities for the developer, essentially places for programming to be inserted that becomes tied to a previously independent action or event.  Application_Start will run the first time a page is requested subsequent to the time the web site was started.  The Application and Session level start and end event programming is located in a special code file called global.asax (and some of this is shown below). 

There are concurrency considerations involved when you make a decision regarding what should be saved in application state.  In addition to the conflicts that may occur in a multi-user environment, there may also be situations where the application state info needs to be refreshed/repopulated based on changes to it outside of the website.  One thing to build your thinking around first is the fact that manual changes to web.config (or even a simple update in place) by an administrater trigger a restart of the website (thus, in most cases, Application_Start).  Other conditions will also trigger a restart automatically (such as exceeding an error count threshold or as a last resort, an approximately 24 time period). 

Another way to trigger the re-populate of application state information is to build a secured admin-like web page within the website.  This web page could enable a way to trigger the restart or to change application state variables without requiring an admin to sign on to the web server at the OS level.  In the enclosed example, like our base code product, there is a boolean indicator at the application state level indicating whether application state is successfully populated.  So resetting that to false will enable the programming to detect that application state needs to be repopulated.  

the Programing Languages

In the enclosed examples are several different kinds of source code including c# and Visual RPG for .NET.

Within the extended version of the Visual Studio 2015 IDE we are using, you can also develop c# and VB.NET if you prefer for some of the programming (in addition to Visual RPG).  Our example shows a little bit of the structuring needed when doing mixed language development in a single website.  Visual RPG for .NET provides a number of features for compatibility and ease of conversion from other RPG languages.

Visual RPG for .NET is a 3rd party .NET language developed by ASNA of San Antonio, TX.  It works with the Common Language Runtime (CLR) and the .NET Frameworks as do c# and VB.NET.  This means it has access to all of the (prebuilt by Microsoft) framework object types such as stacks and queues and arrays and the System.Web.UI.Page object. 


Code locations for application level events Global.asax contains sections for code to run based on start and end events for the application level and session level.  These events are a part of the ASP.NET lifecycle.


Global.asax high level summary

<%@ Application Language="C#" %>

<%@ Import Namespace="System.Web.Optimization" %>

<%@ Import Namespace="AjaxControlToolkit" %>

 

 

<script RunAt="server">

 

    // This file doesn't exist by default in a Web app. To add it,

    // right-click on the project in the Solution Explorer, select

    // Add New Item, and add a "Global Application Class" item.

 

    // Read more about the Global.asax file.

    // http://msdn2.microsoft.com/en-us/library/ms178473.aspx

    // http://samples.gotdotnet.com/quickstart/aspplus/doc/globalasax.aspx

 

+   void Application_Start(Object sender, EventArgs e)...

 

+   void Application_End(Object sender, EventArgs e)...

 

 

    //*******************************************

    // Fires for each error within each session

    //*******************************************

 

    void Application_Error(Object sender, EventArgs e)

    {

        // Code that runs when an unhandled error occurs

        // our current theory is that this code can be run multiple times as an error goes from having a session state to

        //   the point where there is virtually no context whatsoever anymore - i.e. multiple tap-out points :-)

    }

 

 

    //***********************************************

    // Code that runs when a new session is started

    //***********************************************

+   void Session_Start(Object sender, EventArgs e)...

 

+   void Session_End(Object sender, EventArgs e)...

 

 

</script>


Setting up application level memory It is usually easiest to set up memory spaces and objects at the application level within the Application_Start event area in source code file Global.asax.


Global.asax Application_Start custom code

    void Application_Start(Object sender, EventArgs e)

    {

        // Code that runs on application startup

        // Code that runs on application startup which is upon inz of first user's first page

        // Not session specific, not run at start of 2nd and subsequent user's coming to web site

        // Initialize and populate if appropriate static application variables in "Application" collection that will not change

        //  (or really don't need to be that dynamic)(refreshed much (if at all) during the life of the application)

 

        // one could wait to instanciate and populate application state variables

        // however, we are doing that here so we don't have to test the app state variables against null prior to

        //  instanciating and using them

 

        // application level events require use of HttpContext - cannot use *this.Page for example

        // HttpContext.Current.Response.Write("Application is Starting...<br />")

 

 

        //***********************************************

        // Instance and inz Application State variables

        //***********************************************

        HttpContext.Current.Application.Add("asvApplicationStartDateTime",DateTime.Now);

        HttpContext.Current.Application.Add("asvFirstSessionStartDateTime2",DateTime.MaxValue);

        HttpContext.Current.Application.Add("asvLastSessionEndDateTime2",DateTime.MaxValue);

 

        // server machine name

        Application.Add("asv_strMachineName", HttpContext.Current.Server.MachineName.ToString());

 

        // 404 counter

        Application.Add("asv_int404ErrorLogCount", 0);

 

        // populate "UserCount", prefix to application state not required

        System.Int32 UserCount = 0;

        Application.Add("asv_intUserCount", UserCount);

 

        // you can instance and populate application level, session and view state variables in one shot but that is

        //  not quite as OO then, because you can't tell whether the variable is being instanced by looking at the code

        // except of course in the application_start events because this is the first thing that is done

 

        //  set up Ajax Control Toolkit

        BundleTable.Bundles.Add(new ScriptBundle("~/bundles/MsAjaxJs").Include(

            "~/Scripts/WebForms/MsAjax/MicrosoftAjax.js",

            "~/Scripts/WebForms/MsAjax/MicrosoftAjaxApplicationServices.js",

            "~/Scripts/WebForms/MsAjax/MicrosoftAjaxTimer.js",

            "~/Scripts/WebForms/MsAjax/MicrosoftAjaxWebForms.js"));

 

        BundleTable.EnableOptimizations = true;

 

        // populate the application state event monitor

        Application["asv_strAppEventMonitor"] =

            "Web Site " + ConfigurationManager.AppSettings["apkBaseURL"].ToString() +

            " triggered Application_Start event" + " at " + DateTime.Now.TimeOfDay.ToString() +

            " on " + DateTime.Now.Date.ToShortDateString() +

            " on Web Server Machine Name: " + Environment.MachineName +

            " running OS Version: " + Environment.OSVersion.VersionString + "; " + Environment.NewLine;

 

        Application["asv_boolContinueAppEventMonitor"] = true;

 

        // these application state variables are based on application key settings (web.config),

        //   and aren't changed during the life of the application

 

        // can't store anything but string values in AppSettings, but application state stores a serialized object

        if (ConfigurationManager.AppSettings["apkWriteCustomEventLog"] == "T")

            Application["WriteCustomEventLog"] = true;

        else

            Application["WriteCustomEventLog"] = false;

 

        // Denote base site URL for application

        Application["BaseURL"] = ConfigurationManager.AppSettings["apkBaseURL"];

 

        // Denote application state initialization status

        Application["AppStateReady"] = false;

 

        // populate general purpose info at the application level into application state during Application_Start

        /***************************/

        /* ** example 1! timing ** */

        /***************************/

        // System.Text.StringBuilder strbldrEvtLogGlobalAsax = new System.Text.StringBuilder();

        // clsAppViewState clsPopulateAppViewState = new clsAppViewState("Application_Start", strbldrEvtLogGlobalAsax);

        // clsPopulateAppViewState.Populate("Global.asax");

 

    }

 


Setting up
session level namespaces using  Global.asax
This is code that runs just once whenever a new session is started.  Each new session is assigned a unique session ID unless a timed-out session is reactivated without closing the browser.  In this case, Session_Start runs with the Session ID previously used.



Global.asax Session_Start custom code

    //***********************************************

    // Code that runs when a new session is started

    //***********************************************

    void Session_Start(Object sender, EventArgs e)

    {

        // Store session start date and time in a 1) string of yyyyMMddHHmmss format and in a 2) datetime Object form

        System.DateTime dattimSessionStart = DateTime.Now;

        Session["ssvSessionStartDateTime"] = dattimSessionStart.ToString("yyyyMMddHHmmssffff");

        Session["ssvSessionStartDateTime2"] = dattimSessionStart;

        Session["ssv_strLogInDateTime14MostRecent"] = "00000000000000";

 

        // populate first Session start date at application level if null

        if (Convert.ToDateTime(Application["asvFirstSessionStartDateTime2"]) == System.DateTime.MaxValue)

            Application["asvFirstSessionStartDateTime2"] = Session["ssvSessionStartDateTime2"];

 

        Int32 intTotalUserCount = Convert.ToInt32(Application["asv_intUserCount"].ToString());

 

        Session["SessionEventMonitor"] = "Session_Start Event: with total of all User Counts already = " + intTotalUserCount.ToString() + " at " + dattimSessionStart.ToString() + "; " + Environment.NewLine;

 

        // here is probably the most proper syntax for instancing and populating a session state variable

        Session.Add("ssvFirstURLRequest", HttpContext.Current.Request.Url.ToString());

 

        // there have been some problems with a blank and/or unknown and/or null User Host Address, so don't perform

        //  Session.Add("ssv_strUserHostAddress", HttpContext.Current.Request.UserHostAddress.ToString());

        //  until later after editing

 

        // also insure address provided will parse out into the IPAddress object

        // any address we know is valid can be substituted for apkInternalNetworkWANAddress

 

        string strUserHostAddress = null;

        System.Net.IPAddress ipaddressUserHostAddress = System.Net.IPAddress.Parse("0.0.0.0");

 

        try

        {

            strUserHostAddress = HttpContext.Current.Request.UserHostAddress.ToString();

            if (strUserHostAddress == "::1")

                strUserHostAddress = "127.0.0.1";

            ipaddressUserHostAddress = System.Net.IPAddress.Parse(strUserHostAddress);

        }

        catch (Exception)

        { }

 

        if (strUserHostAddress == null)

            strUserHostAddress = "*nothing";

 

        if (strUserHostAddress == "")

            strUserHostAddress = "*blank";

 

        Session.Add("ssv_strUserHostAddress", strUserHostAddress);

 

 

        // application level custom event log is pretty lean and should be saved to a file (at some point I would think)

        Application["asv_strAppEventMonitor"] = Application["asv_strAppEventMonitor"].ToString() +

                    "IP address " + Session["ssv_strUserHostAddress"].ToString() + " requested " +

                    Session["ssvFirstURLRequest"].ToString() + " " + "Session_Start event" +

                    " at " + dattimSessionStart.TimeOfDay.ToString() +

                    " on " + DateTime.Now.Date.ToShortDateString() +

                    " with SessionID: " + HttpContext.Current.Session.SessionID +

                    "; " + Environment.NewLine;

 

        // It appears as if VS2005 using fw 2.0 does not simulate application start and end very well

        // VS2008 and fw 3.5 simulate much better)

 

        // Increment application level user count

        //   lock the application state variable to insure only 1 session updates at a time    

        // System.Int32 is the same thing as type(*integer4)

 

        Application.Lock();

        System.Int32 UserCount = Convert.ToInt32(Application["asv_intUserCount"]);

        UserCount += 1;

        Application["asv_intUserCount"] = UserCount;

        Application.UnLock();

 

        // Instance the Pages Visited session variables so they will always be defined

        //   don't put any entries in it yet because this is not a page (it has a count of 0)

        //   Note: the object type of a session variable must be serializable (able to be turned into a byte stream) (i.e. .ArrayList is allowed because it is serializable)

 

        Session["ssvarrlstPagesVisited"] = new System.Collections.ArrayList();

        Session["ssvarrlstPagesVisitedLastControl"] = new System.Collections.ArrayList();

 

        // capture customer's browser description

        // note:  evidently some hackers are eliminating this data or corrupting it

        try { HttpContext.Current.Session["ssv_strRequestUserAgent"] = HttpContext.Current.Request.UserAgent.ToString(); }

        catch { HttpContext.Current.Session["ssv_strRequestUserAgent"] = "*unknown"; }

 

    }


Finishing a session
This code runs when a session times out.  All active sessions are also ended during the process of shutting down the application in a controlled manner.



Global.asax Session_End custom code

    void Session_End(Object sender, EventArgs e)

    {

        // Code that runs when a session ends.

        // Note: The Session_End event is raised only when the sessionstate mode

        // is set to InProc in the Web.config file. If session mode is set to StateServer

        // or SQLServer, the event is not raised.

        // define session variables that will be populated based on session variables and passed to App_Code classes

 

        // Store session end date and time in a 1) string of yyyyMMddHHmmss format and in a 2) datetime Object form

        System.DateTime dattimSessionEnd = DateTime.Now;

        Session["ssvSessionEndDateTime"] = dattimSessionEnd.ToString("yyyyMMddHHmmssffff");

        Session["ssvSessionEndDateTime2"] = dattimSessionEnd;

        // populate last Session end date at application level, assumes chronilogical

        Application["asvLastSessionEndDateTime2"] = dattimSessionEnd;

 

        try

        {

            //**********************

            // Decrement user count

            //**********************

 

            // retain user count beforehand

            string strUserCount = "";

 

            Application.Lock();

            System.Int32 UserCount = Convert.ToInt32(Application["asv_intUserCount"]);

            strUserCount = UserCount.ToString();

            UserCount -= 1;

            Application["asv_intUserCount"] = UserCount;

            Application.UnLock();

 

            //*******************

            // Write audit logs

            //*******************

 

            Application["asv_strAppEventMonitor"] = Application["asv_strAppEventMonitor"].ToString() +

                        "Session_End Event: with User Count before decrement = " + strUserCount +

                        " and SessionID: " + Session.SessionID.ToString() +

                        " at " + dattimSessionEnd.TimeOfDay.ToString() + "; " + Environment.NewLine;

        }

 

        catch (System.Exception Excepciones)

        {

            // send email indicating Session_End has croaked using static email method

            nsFdnAnyCtxCS10.clsFdnAnyCtxCS9.stcSendErrorDiagnosticEmailV4(

                "*dft", "*dft", "*dft",

                Session["ssv_strUserHostAddress"].ToString() + " - ", "Session_End Croakola - ", "Session_End - ", "*dft", "*dft",

                Excepciones.ToString());

        }

    }


Shutting down the application
This code runs when the last active session ends, also when web.config is changed and/or various other triggers including a request from IIS Manager.



Global.asax Application_End custom code

    void Application_End(Object sender, EventArgs e)

    {

        string strAppEndEvent = "Web Site " + ConfigurationManager.AppSettings["apkBaseURL"].ToString() +

                                " triggered Application_End event" + " at " + DateTime.Now.TimeOfDay.ToString() +

                                " on " + DateTime.Now.Date.ToShortDateString() +

                                " on Web Server Machine Name: " + Environment.MachineName +

                                " running OS Version: " + Environment.OSVersion.VersionString +

                                " based on shutdown reason: " +

                                System.Web.Hosting.HostingEnvironment.ShutdownReason +

                                "; " + Environment.NewLine;

 

        Application["asv_strAppEventMonitor"] =

                    Application["asv_strAppEventMonitor"].ToString() + strAppEndEvent;

 

        // send email containing the AppEventMonitor string

        // the easiest way is to run the static email method

        nsFdnAnyCtxCS10.clsFdnAnyCtxCS9.stcSendErrorDiagnosticEmailV4(

            "*dft", "*dft", "*dft",

            "", "Application_End Details - ", "", "*dft", "Status Email",

            Application["asv_strAppEventMonitor"].ToString());

 

    }


class AppViewState (accesses database to populate application state) Here is the class that populates various application level memory spaces / objects (or variables to use non-OO terminology) dependent upon a database.


clsAppViewState.vr
programming


AppCode VR class:
Populate Application Viewstate


Visual RPG for .NET source code


















































Example Code








































Countries list item collection























US States list item collection









Canada Provinces list item collection



 

 

 

Using System

// Using System.Data

// Using System.Configuration

Using System.Web

// Using System.Web.Security

// Using System.Web.UI

Using System.Web.UI.WebControls

// Using System.Web.UI.WebControls.WebParts

// Using System.Web.UI.HtmlControls

 

 

BegClass clsAppViewState Access(*Public)

 

DclDB     AppDB                 dbname("*Public/FP3 Database FP")

DclFld    strbldrEvtLogLocal    Type(System.Text.StringBuilder)        Access(*protected)                   

DclFld    strCallerStack           Type(*string)

 

dcldiskfile name(CountryCodesByCountryName)     +

        type(*input)                            +

        file("*libl/XACTR1L1")                  +

        org(*indexed)                           +

        impopen(*no)                            +

        db(AppDB)

           

dcldiskfile name(StatesByCountryCode2)          +

        type(*input)                            +

        file("*libl/XASTE1")                    +

        org(*indexed)                           +  

        impopen(*no)                            +

        db(AppDB)

           

dcldiskfile name(CustomerContactClasses)        +

        type(*input)                            +

        file("*libl/ARCCC1L1")                  +

        org(*indexed)                           +  

        impopen(*no)                            +

        db(AppDB)

           

dcldiskfile name(CustomerContactClassGroups)    +

        type(*input)                            +

        file("*libl/ARCCG1")                    +

        org(*indexed)                           +  

        impopen(*no)                            +

        db(AppDB)

           

 

 

    BegConstructor Access(*Public)

 

        DclSrParm strCallerStack    type(*string)

        DclSrParm strbldrEvtLog        type(System.Text.StringBuilder)                       

 

        *this.strCallerStack = strCallerStack +  "." + "clsAppViewStateBasedOnAppDB"

        *this.strbldrEvtLogLocal = strbldrEvtLog

 

    EndConstructor

 

 

    // Provide a way to Disconnect here so that the calling method can request it

    BegSr CloseAppDB Access(*Public) 

 

        try

            if (AppDB *ne *nothing)

                Close *all

                Disconnect AppDB

            endif

        catch

        endtry

                   

    EndSr           

 

 

    //********************************************************************************************************

    // instance all application state variables based on AppDB Db2 database access, and populate if possible     

    //********************************************************************************************************

   

    // this helps to separate AVR type I/O file declarations from global.asax

    // also to reduce footprint size of global.asax

 

    BegSr Populate Access( *Public ) 

       

        DclSrParm    strThisPageURL                         Type(*string)

 

        DclFld       lstitmcolCountries                     Type(System.Web.UI.WebControls.ListItemCollection) New()

        DclFld       lstitmcolUSStates                      Type(System.Web.UI.WebControls.ListItemCollection) New()

        DclFld       lstitmcolCAProvinces                   Type(System.Web.UI.WebControls.ListItemCollection) New()

        DclFld       lstitmcolCustomerContactClasses        Type(System.Web.UI.WebControls.ListItemCollection) New()

        DclFld       lstitmcolCustomerContactClassGroups    Type(System.Web.UI.WebControls.ListItemCollection) New()

    

        // this code must be run upon application startup

        // it has been separated from Application_Start within global.asax

        //    now when the code resides here, an application state variable that indicates whether population has occurred, triggers execution

 

        // as a part of Application_Start, it is not session specific, not run at start of 2nd and subsequent user's coming to web site

 

        // Initialize and populate if appropriate static application variables in "Application" collection that will not change

        //  (or really don't need to be that dynamic)(refreshed much (if at all) during the life of the application)

               

        // application level events require use of HttpContext - cannot use *this.Page for example

 

        // Reset status of application state populated indicator

        HttpContext.Current.Application["AppStateReady"] = *false

 

 

        //*********************************************************************************************

        // Populate 4 list item collections at the app level, storing in memory in application state, 

        //  to be used to bound to the Country drop down list box during address entry

        //*********************************************************************************************

 

        // log the start of this populate routine (in our custom logging processes)

 

        // Follow rule - But let's track population of the application state variable from DB based on this appstate variable...

        if (HttpContext.Current.Application["asv_boolContinueAppEventMonitor"] *as *boolean = *true)

            clsFdnWebCS.stcAddToAsttEventLog(strThisPageURL, "AppViewStateViaAppDB.Start")

        endif

 

        // Follow rule - test for whether to display custom event log before populating or displaying variable
              // nsFdnAnyCtxCS10.clsFdnAnyCtxCS10.boolpropTrackCustomEventLog is a static app level property populated at Application_Start time (not shown)

        if (nsFdnAnyCtxCS10.clsFdnAnyCtxCS10.boolpropTrackCustomEventLog)

            clsFdnWebCS.stcAddToSsttEvnLog2(strThisPageURL, "AppViewStateViaAppDB.Start", *ByRef strbldrEvtLogLocal)

        endif

 

        // define class to connect AppDB

        DclFld clsOpenAppDB   type(clsOpenAppDBcnnWeb)

        clsOpenAppDB = *new clsOpenAppDBcnnWeb(AppDB)

        clsOpenAppDB.srOpenAppDBcnnWeb()

 

        //*****************************************************************************

        // access country codes and populate list item collection, lstitmcolCountries

        //*****************************************************************************

        if (AppDB.IsOpen)

            open CountryCodesByCountryName       

            read CountryCodesByCountryName

            if (NOT CountryCodesByCountryName.IsEof)

                dountil (CountryCodesByCountryName.IsEof)

                    lstitmcolCountries.Add(*new ListItem(ctrnam,ctrcd))

                    read CountryCodesByCountryName

                enddo

            endif

       

            close CountryCodesByCountryName

 

        endif

        // create application state namespace even if based on empty list item collection

        // use implied capability/function to serialize object

        HttpContext.Current.Application["asv_lstitmcolCountries"] = lstitmcolCountries

        // end population of application state list item collection, lstitmcolCountries

 

        //******************************************************

        // access state / province lists for certain countries   

        //******************************************************

        if (AppDB.IsOpen)

 

            open StatesbyCountryCode2       

 

            // access US states and populate list item collection, lstitmcolUSStates

            // add first element indicating a selection has not yet been made

            lstitmcolUSStates.Add(*new ListItem("not selected","**"))

 

            ctrcd = "US"

            chain StatesbyCountryCode2 key(ctrcd)

            if (StatesbyCountryCode2.IsFound)

               

                dountil (StatesbyCountryCode2.IsEof)

                    lstitmcolUSStates.Add(*new ListItem(tstenm,state))

                    reade StatesbyCountryCode2 key(ctrcd)

                enddo

           

            endif

       

            // access Canada provinces tates and populate list item collection, lstitmcolCAProvinces       

            lstitmcolCAProvinces.Add(*new ListItem("not selected","**"))

 

            ctrcd = "CA"

            chain StatesbyCountryCode2 key(ctrcd)

            if (StatesbyCountryCode2.IsFound)

               

                dountil (StatesbyCountryCode2.IsEof)

                    lstitmcolCAProvinces.Add(*new ListItem(tstenm,state))

                    reade StatesbyCountryCode2 key(ctrcd)

                enddo

           

            endif

       

            close StatesbyCountryCode2

 

        endif

           

        // create application state namespace for US states

        HttpContext.Current.Application["asv_lstitmcolUSStates"] = lstitmcolUSStates

 

        // create application state namespace for Canadian provinces

        HttpContext.Current.Application["asv_lstitmcolCAProvinces"] = lstitmcolCAProvinces

 

 

        // log the conclusion of this populate routine (in our custom logging processes)  

        if (AppDB.IsOpen)

 

            // Follow rule - But let's track population of the application view state variables from DB based on this appstate variable...

            if (HttpContext.Current.Application["asv_boolContinueAppEventMonitor"] *as *boolean = *true)

                clsFdnWebCS.stcAddToAsttEventLog( strThisPageURL, "AppViewStateViaAppDB.Complete" )

            endif

 

 

        endif

 

        // end populate of application state list item collections

        // Set status of application state populated indicator to happy!

        if (AppDB.IsOpen)

            HttpContext.Current.Application["AppStateReady"] = *true

        endif

 

        clsOpenAppDB.CloseAppDB()

 

       

    Endsr

 

                     

EndClass

 


Specifying the UI controls
UI controls for the web page can be specified in XHTML and stored in the .aspx source file.  We are using the XHTML 1.1 Target Schema for Validation.


.aspx (view) source subset

        <asp:UpdatePanel ID="UpdatePanel1" runat="server">

        <ContentTemplate>

 

        <table id="tblDataEntry1" runat="server" style="text-align: left;  vertical-align: middle;">

            <tr>

                <td id="Td30">

                    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

                    <asp:DropDownList ID="ddlCountries" runat="server" Style="z-index: 112;" Width="254px"

                     CssClass="InputFieldStdTHRelativeWithinTable" AutoPostBack="True"></asp:DropDownList>

                    &nbsp;&nbsp;

                    <asp:DropDownList ID="ddlStates" runat="server" Style="z-index: 112;"

                     CssClass="InputFieldStdTHRelativeWithinTable" Width="162px"></asp:DropDownList>

                    <asp:TextBox ID="tbState" runat="server" Style="z-index: 106;"

                     CssClass="InputFieldStdTHRelativeWithinTable" MaxLength="2" Width="28"></asp:TextBox>

                </td>

            </tr>

 

        </table>

 

        </ContentTemplate>

        </asp:UpdatePanel>


code-behind the web page handling the events and drop down lists
Here is the code involved with the countries and states drop down lists.  A more complete set of the page code is available on request (register and indicate your interest (at the bottom of the registration page).  Here is a link to a functioning version of the page.


web page code subset written in Visual RPG for .NET

       //  ddlCountries_SelectedIndexChanged routine resets the list of states or changes the state input control to a text box

       //  The only way this code triggers the change right away (in a drop down list) is if AutoPostBack is *true (in the list

       //    of properties for the drop down list box)

       //  Ajax update panel allows refresh of just a portion of the browser window/display

      

       BegSr ddlCountries_SelectedIndexChanged Access(*Private) Event(*This.ddlCountries.SelectedIndexChanged)

 

              DclSrParm sender Type(*Object)

              DclSrParm e Type(System.EventArgs)

 

              clsFdnWebCS.stcAddToSsttEvnLog2( strThisPageURL, clsFdnWebCS.stcstrfctGetCtrlID(sender *as Control) + "_Click", *ByRef *base.strbldrEvtLogBase )

 

              if (ddlCountries.SelectedValue() *eq "CA")

                     ddlStates.DataSource = Application["asv_lstitmcolCAProvinces"] *as ListItemCollection

                     ddlStates.DataTextField  = "text"

                     ddlStates.DataValueField = "value"

                     ddlStates.DataBind()

                     ddlStates.Visible = *true

                     tbState.Visible = *false

              else

                     if (ddlCountries.SelectedValue() *eq "US")

                           ddlStates.DataSource = Application["asv_lstitmcolUSStates"] *as ListItemCollection

                           ddlStates.DataTextField  = "text"

                           ddlStates.DataValueField = "value"

                           ddlStates.DataBind()

                           ddlStates.Visible = *true

                           tbState.Visible = *false

                     else

                           ddlStates.Visible = *false

                           tbState.Visible = *true

                     endif

              endif

      

              // country was changed, so if states is visible it still needs to be selected

              if (ddlStates.Visible)

                     ddlStates.Focus()

              else

                     tbState.text = *blank

                     tbZip.Text = *blank

                     tbState.Focus()

              endif

 

       EndSr

 

 

       //**************************************************************************************************

       //  Populate RegisterTCAdvAppState Page from existing data or initialize for initial data creation 

       //**************************************************************************************************

 

       BegSr srPopulateOrInzPageUI  

      

        // populate application state data coming from database if not already done

              if (Application["AppStateReady"] *as *boolean <> *true)

                     // event log tracking contained within class

                     enterlock (nsFdnAnyCtxCS10.clsFdnAnyCtxCS10.objSingleThreadEnforcer)

                           DclFld clsAppViewStateFromDB  type(clsAppViewState)

                           clsAppViewStateFromDB = *new clsAppViewState("RegisterTCAdvAppState.aspx", *base.strbldrEvtLogBase)

                           clsAppViewStateFromDB.Populate(strThisPageURL)

                           // this one automatically closes at the end of the populate routine

                     exitlock

              endif

 

              //****************************************************************************

              // populate country (ddlCoutries) and state (ddlStates) drop down list boxes

              //****************************************************************************

 

              // population of ddlStates shows the most efficient way of using a static list that is based on information

              //  in a database (in a web app).  Initial creation of the static list is done once for all users in

              //  Application_Start in global.asax

              // For the drop down list of countries, we need to take advantage of method .FindByValue, thus we need to add one

              //  line of code that translates the application state variable into a ListItemCollection object (in this class)

              //  (apvlstitmcolCountries is a ListItemCollection that has been serialized (translated into a byte stream)

              //  (she ain't alive :-) / can't run methods on it / list of properties that work is very limited))

              //  So we populate ddlCountries control with a/the ListItemCollection object, lstitmcolCountries, that is instanced

             

              // please note that this example takes advantage of the fact that the drop down lists are kept around by use of

              //  the page level viewstate

 

                     // retrieve the list item collection application state variable as a list item collection object type

                     // this session variable is created during the application event Application_Start in global.asax

                     // if we did not need the .IndexOf method from the ListItemCollection then we could have bound the drop down

                     //  list box directly to the application state data as a ListItemCollection as is done (below) for ddlStates

                     //  We also/still could have eliminated declaration of the ListItemCollection by storing the index of the "US"

                     //   in a 2nd application viewstate collection member

                    

              // retrieve the list item collection application state variable as a list item collection object type

              //  and point to it as the data source

              // this session variable is created during the application event Application_Start in global.asax               

              DclFld lstitmcolCountries  Type(System.Web.UI.WebControls.ListItemCollection)

              lstitmcolCountries = Application["asv_lstitmcolCountries"] *as ListItemCollection

              ddlCountries.DataSource = lstitmcolCountries

 

              // we need some magic to get both the country code (2,A) and country name (30,A) to be usable as separate entities

              // evidentally the default column? names for a list item collection are "text" and "value"?                  

              ddlCountries.DataTextField = "text"

              ddlCountries.DataValueField = "value"

              // ok, this is required to finish the populating the data for the drop down list box

              //  and then pointing? to it

              ddlCountries.DataBind()

             

              // show UNITED STATES (as default selection)

              ddlCountries.SelectedIndex = lstitmcolCountries.IndexOf(lstitmcolCountries.FindByValue("US"))

             

              // populate the states drop down list in a similar manner knowing we defaulted the country selection to "US"

              // we have 2 state list item collections, one for US and one for Canada, that's it so far

              // the two lists have been hard-coded in terms of access and data binding but not in the database

              // if we get more states lists, it will make sense to fill them in at run time

              // if another state or territory is added to the "CA" or "US" lists, nothing else needs to be done

              //  (although the web application has to go down and come back up for it to show in the list)

              ddlStates.DataSource = Application["asv_lstitmcolUSStates"] *as ListItemCollection

              ddlStates.DataTextField = "text"

              ddlStates.DataValueField = "value"

              ddlStates.DataBind()

              ddlStates.Visible = *true

              tbState.Visible  = *false

 

              if (*base.clsCustCtcActs.boolpropSignInStatus *eq *false)

                    ...

 

       EndSr

 

 

       //*********************************************

       // change user interface based on data values

       //*********************************************

 

       BegSr SetUIProperties

 

              if (ViewState["vsv_ProcessingMode"].ToString() = "Update")

                     *this.Page.Title = "Update My Web User Profile"

                     btnAccept.Text = "Update Record"

              else

                     *this.Page.Title = "Create My Account Information"

                     btnAccept.Text = "Create Record"

              endif

                    

            //**********************************************************************************************

              // basically you can make a html table row invisible through the .visible property for the row

              //  the cells can still be set to visible as well as the imbedded .webcontrols (yea!)

            //**********************************************************************************************

             

              //     if (cbContactMe.Checked = *true *or tbPhoneNumber.Text <> *blank)

                     //     tr6.Visible = *true

                           //     tr6.Cells[0].Visible = *true

                           //     tr6.Cells[1].Visible = *true

              //     else

                     //     tr6.Visible = *false

                           //     tr6.Cells[0].Visible = *false

                           //     tr6.Cells[1].Visible = *false

              //     endif

 

              // let's softcode access of the row, and base the row object on what houses the text box we want to make invisible

              // 1) create a row object instanced to *nothing

              DclFld htmltblrowObject type(System.Web.UI.HtmlControls.HtmlTableRow) new()

              DclFld boolWork type(*boolean)

             

              // let's make the address invisible so it looks like it's easy and fast to sign up

              if (cbSendPromoLit.Checked *eq *true *or +

                     ddlCountries.SelectedValue() *ne "US" *or +

                     ddlStates.SelectedValue() *ne "**" *or +

                     tbState.Text *ne *blank *or +

                     tbAddressLine1.Text *ne *blank *or +

                     tbAddressLine2.Text *ne *blank *or +

                     tbCity.Text *ne *blank *or +

                     tbZip.Text *ne *blank)

                           boolWork = *true                        

              else

                           boolWork = *false

              endif

             

              *base.SetHtmlTableRowReference("ddlCountries", *ByRef htmltblrowObject)

              htmltblrowObject.Visible = boolWork     

              *base.SetHtmlTableRowReference("tbAddressLine1", *ByRef htmltblrowObject)

              htmltblrowObject.Visible = boolWork     

              *base.SetHtmlTableRowReference("tbAddressLine2", *ByRef htmltblrowObject)

              htmltblrowObject.Visible = boolWork     

              *base.SetHtmlTableRowReference("tbCity", *ByRef htmltblrowObject)

              htmltblrowObject.Visible = boolWork     

 

                                               

       Endsr

 


Setting up usage of multiple languages in web.config
To use more than one .NET language in a web site, you must use the web site project model.  If you want to mix languages in App_Code, you need to denote a directory for each language.  Contrary to some online and book information, you do not need to store source code on the server with the web site project model, it can be published in advance to a run time only version.


web.config file contents

showing mixed language directory names, compile order and establishment of  dependency

(in this order in App_Code, VB can call c# but not vice-versa)

 

  <appSettings>

    <add key="apkWriteCustomEventLog" value="T"/>

    <add key="apkBaseURL" value="www.tegratecs.com"/>

    <add key="apkCtcRegPagePrimaryInterestDft" value="RESOURCES"/>

    <add key="apkCtcRegPageJoinText" value="Create your Member ID now to gain greater access to professional technology reports!"/>

    <add key="apkCtcRegPageUpdateText" value="Thank you for keeping your member profile current."/>

  </appSettings>

 

<system.web>

    <!-- disable for production... <compilation debug="false"> --> 

    <!-- re-enable for testing...  <compilation debug="true"/> -->

    <compilation debug="true" defaultLanguage="c#" numRecompilesBeforeAppRestart="1500" strict="false" explicit="true" targetFramework="4.0">

      <!-- start section for mixing multiple languages in web site project -->

      <codeSubDirectories>

        <add directoryName="CS"/>

        <add directoryName="VB"/>

        <add directoryName="VR"/>

      </codeSubDirectories>

      <!-- end section for mixing multiple languages in web site project -->






        go to the home page Top of TechCrystals Articles     go to next series page Next in TechCrystals Articles     Site Map     Switch to
Mobile View

You are at the web site of Tegratecs Development Corp.  Click here to go to the home page of this site...
Offering Innovation & Providing ROI
without building Islands of Automation
Our contact information:
Tegratecs Development Corp.®
1320 Tower Road
Schaumburg, IL 60173
847-397-0088
800-739-FPSS
( please contact us or create a Web User ID or sign in )
© 2012-2025