From 8564cd1e137102eb4edbf66bbbf4e9e8a23df2c8 Mon Sep 17 00:00:00 2001 From: markpollack Date: Wed, 16 Dec 2009 03:53:48 +0000 Subject: [PATCH] SPRNET-1298 - Copy edit the Spring reference documentation chapter on the web framework --- doc/reference/src/web.xml | 2229 +++++++++++++++++++------------------ 1 file changed, 1143 insertions(+), 1086 deletions(-) diff --git a/doc/reference/src/web.xml b/doc/reference/src/web.xml index 09747dec..eb682c01 100644 --- a/doc/reference/src/web.xml +++ b/doc/reference/src/web.xml @@ -25,199 +25,180 @@ Spring.NET Web Framework - Introduction + Introduction to Spring.NET Web Framework - Spring.NET's web application framework aims to increase your - productivity writing ASP.NET WebForms applications. It offers a unique - value proposition to creating ASP.NET applications not found in other .NET - web frameworks. + The Spring.NET Web Framework increases your productivity when you + write ASP.NET WebForms applications by offering capabilities not found in + other .NET web frameworks. - The goal of the framework is to make it easy to write 'thin and - clean' web applications. By thin, what is meant is that the WebForm's - responsibility is to act as adapter between the HTML based world of the - web and the oo world of your application. The application layer your web - form communicates with is where the business logic resides, not in the web - tier. By 'clean' what is meant that the web framework should have a good - separation of concerns, leading ideally to an event-handler that does not - contain any reference to UI elements. This makes it possible to test your - event handler code in integration style tests. Last but not least, - Spring's web framework reduces the incidental complexity of common tasks - in the web tier, for example the conversion of HTML control data to - objects and then vice-versa after the request has been processed by the - application layer. + The Spring.NET Web Framework makes it easy to write 'thin and clean' + web applications. "Thin" refers to WebForm's role as a small as possible + adapter between the HTML- based world of the web and the Object-oriented + world of your application. The business logic does not reside in the web + tier; it resides in the application layer with which your web form + communicates. "Clean" refers to the framework's appropriate separation of + concerns, separating web specific processing such as copying data out and + into from element from a data model from calling into a buiness tier and + redirecting to the next page. This results in an event-handler that does + not contain any reference to UI elements thereby making it possible to + test your event handler code in integration style tests. The Spring.NET + Web Framework reduces the incidental complexity of common tasks in the web + tier, for example, the conversion of HTML control data to objects and then + vice-versa after the request is processed by the application layer. - Highlights of Spring's Web framework are + Highlights of Spring's Web framework are: - Dependency Injection for all - ASP.NET artifacts. This includes pages and user controls but also - modules, providers and HTTP handlers. Your pages, controls, etc., do - not have any Spring dependencies in order to be configured via + Dependency Injection. Provided + for all ASP.NET artifacts, inlcuding pages and user controls, modules, + providers, and HTTP handlers. Your pages, controls, and so on do not + require any dependency on Spring in order to be configured via dependency injection. - Bi-directional data - binding. This allows you to declaratively define the data that - will be marshaled out of your html/user controls and into a data model + Bidirectional data + binding. Allows you to declaratively define the data that will + be marshaled out of your HTML and user controls and into a data model that in turn is generally submitted to the application layer. After the data model is updated in the application layer, those changes are - automatically reflected in the html/user controls on post back. This - removes large amounts of tedious, error prone boilerplate code. + automatically reflected in the HTML and user controls on post back. + This process removes large amounts of tedious, error-prone boilerplate + code. - Web object scopes. Object - definitions can be defined at the application, session or request - scope. This makes it easy to inject, say a session scoped shopping - cart, into your page without having to do any lower level - programming. + Web object scopes. Can be + defined at the application, session, or request scope. This capability + makes it easy to inject, for example, a session scoped shopping cart, + into your page without any lower level programming. - Data Model Management. - While ASP.NET managed the view state of your form, it does not offer - facilities to manage the data model that you build up to submit to the - application layer. Spring provides a mechanism similar to view state - to help manage your data model. + Data model management. + Provides a mechanism similar to view state to help manage your data + model. (While ASP.NET manages the view state of your form, it does not + offer facilities to manage the data model that you build up to submit + to the application layer.) - UI agnostic validation - framework. Declaratively define complex validation rules, for - example that take into account complex relationships in your data - model. Error controls are provided to easily render validation - failure. This allows you to centralize your validation logic and also - reuse it on the server side, for example using parameter validation - advice described in the aspect library chapter + UI-agnostic validation + framework. Enables you to declaratively define complex + validation rules, for example, that take into account complex + relationships in your data model. Spring's error controls easily + render validation failure. Thus you can centralize your validation + logic and also reuse it on the server side, for example, by using + parameter validation advice described in the aspect library + chapter. Externalized page navigation - through 'result mapping'. Instead of hard coding urls and data - to direct where a page should go next, result mappings are externally - defined and configured that associate logical names and a URL (+ - data). This also allows for the encryption of values that are sent via + through result mapping. Instead of hard-coding URLs and data to + direct where a page should go next and what data should be carried + along, you can define and configure result mappings externally that + associate logical names and a URL (+ data). This capability also + allows you to encrypt the values that are sent through Response.Redirect. Improved localization - and master page support - Advanced localization features (including - image localization) as well as declarative configuration of what mater - page to apply to different parts of your web application are easy to - perform. - - + and master page support. Provides advanced localization features + (including image localization) and make it easy to declaratively + configure which master page to apply to different parts of your web + application. - All you know about ASP.NET development still applies, Spring's - approach is to 'embrace and extend' the basic ASP.NET programming model so - you can be as productive as possible. + All you know about ASP.NET development still applies. Spring's + approach is to 'embrace and extend' the basic ASP.NET programming model to + make you as productive as possible. - Support for ASP.NET MVC is planned for Spring.NET 2.0 and previews - of our integration with the MVC framework will be mode available when - the final MVC framework ships. + Support for ASP.NET MVC is planned for Spring.NET 2.0. - What follows is a more detailed background and motivation of - features and most importantly the detailed reference manual for using - Spring's web framework. One of the great things about the framework is - that it is not an all or nothing solution. If you choose to use only - dependency injection and bi-directional data binding, that is just fine. - You can incrementally adopt the web framework, addressing problems areas - in your current web application with a specific feature. There is no need - to go 'whole hog' into using all parts of the framework everywhere in your - application. + This chapter describes the Spring.NET Web Framework in detail. The + framework is not an all-or-nothing solution. For example, you can choose + to use only dependency injection and bi-directional data binding. You can + adopt the web framework incrementally, addressing problems areas in your + current web application with a specific feature. - The Spring.NET distribution ships with a number of Web QuickStarts - and a complete reference application, SpringAir. Web QuickStarts are the - best way to learn each Spring.Web feature by following simple examples, - and the SpringAir reference application has a Spring.Web-enabled frontend - which uses many best practices for Spring.NET web applications, so please - do refer to it as you are reading this (reference) material (see The Spring.NET distribution ships with a Web Quick Start application + and a complete reference application, SpringAir. The Web QuickStart is the + best way to learn each Spring.NET Web Framework (also referred to in this + document as Spring.Web) feature, by following simple examples. The + SpringAir reference application has a Spring.Web-enabled frontend that + uses many best practices for Spring.NET web applications, so refer to it + as you are reading this (reference) material (see ). - Background + Comparing Spring.NET and ASP.NET - One of the objections many developers have to the ASP.NET - programming model is that it is not a "true MVC" (Model-View-Controller) - implementation, because controller-type logic within the page is too - tightly coupled to the view. A good example of this are event handlers - within the page class, which typically have references to view elements, - such as input controls, all over the place. Without getting into academic - discussion of what "true MVC" is, and whether it is even appropriate to - try to fit form-based technology such as ASP.NET into traditionally - request-based MVC pattern when MVP (Model-View-Presenter) or Presentation - Model might be more appropriate, we'd like to agree with the critics on - the most important point they are making: controller-type logic, such as - the code within page event handlers in ASP.NET, should not depend on the - view elements. + Many developers dislike the ASP.NET programming model because + currently it is not a "true MVC" (model-view-controller) implementation; + controller-type logic within the page is too tightly coupled to the view. + For example, event handlers within the page class typically have + references to view elements, such as input controls, in many code behind + locations, most typically the event handler. Controller-type logic, such + as the code within page event handlers in ASP.NET, should not depend on + the view elements. - Having said that, there are good things about - ASP.NET. Server-side forms and controls make developers significantly more - productive and allow us to significantly simplify page markup. They also - make cross-browser issues easier to deal with, as each control can make - sure that it renders correct markup based on the user's browser. The - ability to hook custom logic into the lifecycle of the page, as well as to - customize HTTP processing pipeline are also very powerful features. - Finally, being able to interact with the strongly typed server-side - controls instead of manipulating string-based HTTP request collections, - such as Form and QueryString, is a much needed layer of abstraction in web - development. + However, ASP.NET has its good points. Server-side forms and controls + make developers significantly more productive and allow you to + significantly simplify page markup. They also make cross-browser issues + easier to deal with, as each control can make sure that it renders correct + markup based on the user's browser. The ability to hook custom logic into + the lifecycle of the page, as well as to customize the HTTP processing + pipeline, are also very powerful features. The ability to interact with + the strongly typed server-side controls instead of manipulating + string-based HTTP request collections, such as Form and QueryString, is a + much needed layer of abstraction in web development. - For these reasons, we decided that instead of developing a new, - "pure and true MVC" web framework as part of Spring.NET, we should take a - more pragmatic approach and extend ASP.NET in such a way that most, if not - all of its shortcomings are eliminated. It should be noted that with the - introduction of a 'true MVC framework' being added to .NET, with extension - points for IoC containers such as Spring, Spring will continue to play a - role within a MVC based model once that functionality is available from - Microsoft. It is worth noting that Spring Java has a very popular MVC - framework and much of that experience and added value can be - transliterated to help developers be more productive when using the - upcoming ASP.NET MVC support. + Thus, instead of developing a new, pure and true MVC web framework + as part of Spring.NET, Spring decided to extend ASP.NET so that most of + its shortcomings are eliminated. With the introduction of a 'true MVC + framework' to .NET there are several opportunities for integration with + IoC containers such as Spring.NET. Furthermore, as Spring for Java has a + very popular MVC framework, much of that experience and added value can be + transliterated to help developers be more productive when using Spring's + future support for ASP.NET MVC. - Spring.Web also adds support for applying the dependency injection + Spring.Web also supports the application of the dependency injection principle to one's ASP.NET Pages and - Controls as well as http modules and custom provider - modules. This means that application developers can easily inject service + Controls as well as to HTTP modules and custom provider + modules. Thus application developers can easily inject service dependencies into web controllers by leveraging the power of the Spring.NET IoC container. See Dependency Injection - for ASP.NET Pages for more information. + for ASP.NET Pages. - As we said earlier, event handlers in code-behind classes really - should not have to deal with ASP.NET UI controls directly. Such event - handlers should rather work with the presentation model of the page, - represented either as a hierarchy of domain objects or an ADO.NET - DataSet. It is for that reason that the Spring.NET team - implemented bidirectional data binding framework to handle the mapping of - values to and from the controls on a page to the underlying data model. - The data binding framework also transparently takes care of data type + Event handlers in code-behind classes should not have to deal with + ASP.NET UI controls directly. Such event handlers should rather work with + the presentation model of the page, represented either as a hierarchy of + domain objects or an ADO.NET DataSet. Spring.NET + implemented a bidirectional data binding framework to handle the mapping + of values to and from the controls on a page to the underlying data model. + The data binding framework also transparently implements data type conversion and formatting, enabling application developers to work with fully typed data (domain) objects in the event handlers of code-behind files. See Bidirectional Data Binding and - Model Management for more information. + Model Management. - The flow of control through an application is another area of - concern that is addressed by Spring.NET Web Framework. Typical ASP.NET - applications will use Response.Redirect or - Server.Transfer calls within Page - logic to navigate to an appropriate page after an action is executed. This - typically leads to hard-coded target URLs in the Page, - which is never a good thing. Result mapping solves this problem by - allowing application developers to specify aliases for action results that - map to target URLs based on information in an external configuration file - that can easily be edited. Under consideration for future releases of - Spring.NET is a process management framework, which will take this - approach to another level, allowing you to control complex page flows in a - very simple way. See Result - Mapping for more information. + The Spring.NET Web Framework also addresses concerns about the flow + of control through an application. Typical ASP.NET applications use + Response.Redirect or Server.Transfer + calls within Page logic to navigate to an appropriate + page after an action is executed. This usage often leads to hard-coded + target URLs in the Page, which is never a good thing. + Result mapping solves this problem by allowing application developers to + specify aliases for action results that map to target URLs based on + information in an external configuration file that can easily be edited. + See Result Mapping. Standard localization support is also limited in versions of ASP.NET prior to ASP.NET 2.0. Even though Visual Studio 2003 generates a local @@ -225,35 +206,31 @@ those resources are never used by the ASP.NET infrastructure. This means that application developers have to deal directly with resource managers whenever they need access to localized resources, which in the opinion of - the Spring.NET team should not be the case. Spring.NET's Web Framework - (hereafter referred to as Spring.Web) adds comprehensive support for - localization using both local resource files and global resources that are - configured within and for a Spring.NET container. See Localization and Message Sources for - more information. + the Spring.NET team should not be the case. Spring.Web adds comprehensive + support for localization using both local resource files and global + resources that are configured within and for a Spring.NET container. See + Localization and Message + Sources. - In addition to the aforementioned features that can be considered to - be the 'core' features of the Spring.Web framework, - Spring.Web also ships with a number of other lesser features that might be - useful to a large number of application developers. Some of these - additional features include back-ports of ASP.NET 2.0 features that can be - used with ASP.NET 1.1, such as Master Page support. See Master Pages in ASP.NET 1.1 for more - information. + In addition to the aforementioned core features, Spring.Web ships + with lesser features that might be useful to many application developers. + Some of these additional features include back-ports of ASP.NET 2.0 + features that can be used with ASP.NET 1.1, such as Master Page support. + See Master Pages in ASP.NET + 1.1 . - In order to implement some of the above mentioned features the - Spring.NET team had to extend (as in the object-oriented sense) the - standard ASP.NET Page and - UserControl classes. This means that in order to take - advantage of the full feature stack of Spring.Web - (most notably bidirectional data binding, localization and result - mapping), your code-behind classes will have to extend Spring.Web specific - base classes such as Spring.Web.UI.Page; however, some - very powerful features such as dependency injection for ASP.NET Pages, - Controls, and providers can be leveraged without having to extend - Spring.Web-specific base classes. It is worth stating that by taking - advantage of some of the more useful features offered - by Spring.Web you will be coupling the presentation tier of your + To implement some features, the Spring.NET team had to extend (as in + the object-oriented sense) the standard ASP.NET Page + and UserControl classes. This means that in order to + take advantage of the full feature stack of + Spring.Web (most notably bidirectional data binding, localization and + result mapping), your code-behind classes must extend Spring.Web specific + base classes such as Spring.Web.UI.Page. However, + powerful features such as dependency injection for ASP.NET Pages, + controls, and providers can be leveraged without having to extend + Spring.Web-specific base classes. By taking advantage of + some of the more useful features offered by + Spring.Web, you will be coupling the presentation tier of your application(s) to Spring.Web. The choice of whether or not this is appropriate is, of course, left to you. @@ -262,33 +239,41 @@ Automatic context loading and hierarchical contexts - Configuration + Configuration of a web application - Unsurprisingly, Spring.Web builds on top of the Spring.NET IoC - container, and makes heavy use (internally) of the easy pluggability and - standardized configuration afforded by the IoC container. This also - means that all of the controllers (ASP.NET Pages) - that make up a typical Spring.Web enabled application will be configured - using the same standard Spring.NET XML configuration syntax. Spring.Web - uses a custom PageHandlerFactory implementation to - load and configure a Spring.NET IoC container, which is in turn used to - locate an appropriate Page to handle a HTTP request. - The WebSupportModule configures miscellaneous Spring - infrastructure classes for use in a web environment, for example setting - the storage strategy of LogicalThreadContext to be + Spring.Web builds on top of the Spring.NET IoC container, and + makes heavy use (internally) of the easy pluggability and standardized + configuration afforded by the IoC container. ASP.NET + Pages and UserControls that make up a typical + Spring.Web-enabled application are configured with the same standard + Spring.NET XML configuration syntax used for non web objects. To + integrate with the ASP.NET runtime you need to make a few modifications + to your Web.config file. + + Spring.Web uses a custom PageHandlerFactory + implementation to load and configure a Spring.NET IoC container, which + is in turn used to locate an appropriate Page to + handle a HTTP request. The WebSupportModule + configures miscellaneous Spring infrastructure classes for use in a web + environment, for example setting the storage strategy of + LogicalThreadContext to be HybridContextStorage. The instantiation and configuration of the Spring.NET IoC container by the Spring.Web infrastructure is wholly transparent to - application developers, who will typically never have to explicitly - instantiate and configure an IoC container manually (by for example - using the new operator in C#). In order to effect the - transparent bootstrapping of the IoC container, the Spring.Web - infrastructure requires the insertion of the following configuration - snippet into each and every Spring.Web-enabled web application's root - Web.config file (the verb and - path properties can of course be changed from the - values that are shown below): + application developers, who typically never have to explicitly + instantiate and configure an IoC container manually (by, for example, + using the new operator in C#). To effect the + transparent bootstrapping of the IoC container, you need to insert the + following configuration snippet into the root + Web.config file of every Spring.Web-enabled web + application. (You can of course change the verb and + path properties from the values that are shown.) + + If you are using the solution templates that ship with + Spring.NET this configuration will be done for you automatically + whent he solution is created. + <system.web> <httpHandlers> @@ -302,24 +287,24 @@ - Please note that this snippet of standard ASP.NET configuration is - only required to be present in the root directory - of each Spring.Web web application (i.e. in the - web.config file present in the top level virtual - directory of an ASP.NET web application). + This snippet of standard ASP.NET configuration is only required in + the root directory of each Spring.Web web + application (that is, in the Web.config file present + in the top level virtual directory of an ASP.NET web + application). - The above XML configuration snippet will direct the ASP.NET - infrastructure to use Spring.NET's page factory, which will in turn - create instances of the appropriate .aspx - Page, (possibly) inject dependencies into said - Page (as required), and then forward the handling of - the request to said Page. + The above XML configuration snippet directs the ASP.NET + infrastructure to use Spring.NET's page factory, which in turn creates + instances of the appropriate .aspx + Page, possibly injects dependencies into that + Page (as required), and then forwards the handling of + the request to the Page. - After the Spring.Web page factory is configured, you will also - need to define a root application context by adding a Spring.NET - configuration section to that same web.config file. - The final configuration file should look a little like this (your exact - configuration will no doubt vary in particulars)... + After the Spring.Web page factory is configured, you also need to + define a root application context by adding a Spring.NET configuration + section to that same Web.config file. The final + configuration file should resemble the following; your exact + configuration may vary in particulars. <?xml version="1.0" encoding="utf-8"?> <configuration> @@ -361,57 +346,58 @@ </configuration> - There are a few important points that need to be noted with regard - to the above configuration: + Notes about the preceding configuration: - + - You must define a custom configuration section handler for the - spring/context element. If you use Spring.NET for - many applications on the same web server, it might be easier to move - the whole definition of the Spring.NET section group to your - machine.config file. + Define a custom configuration section handler for the + <context> element. If you use + Spring.NET for many applications on the same web server, it might be + easier to move the whole definition of the Spring.NET section group + to your machine.config file. The custom configuration section handler is of the type - Spring.Context.Support.WebContextHandler which - will in turn instantiate an IoC container of the type + Spring.Context.Support.WebContextHandler which in + turn instantiates an IoC container of the type Spring.Context.Support.WebApplicationContext. - This will ensure that all of the features provided by Spring.Web are - handled properly (such as request and session-scoped object - definitions). + This ensures that all features provided by Spring.Web, such as + request and session-scoped object definitions, are handled + properly. - Within the <spring> element you need to define a root - context, and resource locations that contain the object definitions - that will be used within the web application (such as service or - business tier objects) then need to be specified as child elements - within the <context> element. Object definition resources can - be fully-qualified paths or URLs, or non-qualified, as in the - example above. Non-qualified resources will be loaded using the + Within the <spring> element, define a root + context element. Next, specify resource locations that contain the + object definitions that are used within the web application (such as + service or business tier objects) as child elements within the + <context> element. Object definition + resources can be fully-qualified paths or URLs, or non-qualified, as + in the example above. Non-qualified resources are loaded using the default resource type for the context, which for the WebApplicationContext is the WebResource type. - Please note that the object definition resources do not all - have to be the same resource type (e.g. all - file://, all http://, all - assembly://, etc). This means that you can load - some object definitions from resources embedded directly within - application assemblies (assembly://), while - continuing to load other object definitions from web resources that - can be more easily edited. + The object definition resources do not have to be the same + resource type (for example, all file://, all + http://, all assembly://, and + so on). This means that you can load some object definitions from + resources embedded directly within application assemblies + (assembly://) while continuing to load other + object definitions from web resources that can be more easily + edited. - + - Configuration for IIS7 + Configuration for IIS 7.0 on Windows Server 2008 and Windows + Vista - The configuration for IIS7 is shown below + There is some configuration that is specific to using IIS7, the + appropriate code snippit to place in web.config shown below. <system.webServer> <validation validateIntegratedModeConfiguration="false"/> @@ -427,38 +413,38 @@ - Context Hierarchy + Context hierarchy - ASP.NET provides a hierarchical configuration mechanism by - allowing application developers to override configuration settings - specified at a higher level in the web application directory hierarchy - with configuration settings specified at the lower level. + ASP.NET has a hierarchical configuration mechanism that enables + application developers to override configuration settings specified at a + higher level in the web application directory hierarchy with + configuration settings specified at the lower level. For example, a web application's root - Web.config file overrides settings from the (lower + Web.config file overrides settings from the (higher level) machine.config file. In the same fashion, settings specified within the web.config file within a subdirectory of a web application will override settings from the root - Web.config and so on. Lower level - Web.config files can also add settings of their own - that were not previously defined anywhere. + Web.config and so on. You can also add seettings to + lower level Web.config files that were not previously + defined anywhere. Spring.Web leverages this ASP.NET feature to provide support for a - context hierarchy. Your lower level Web.config files - can be used to add new object definitions or to override existing ones - per virtual directory. + context hierarchy. You can add new object definitions to lower level + Web.config files or override existing ones per + virtual directory. What this means to application developers is that one can easily componentize an application by creating a virtual directory per component and creating a custom context for each component that contains the necessary configuration info for that particular context. The - configuration for a lower level component will generally contain only - those definitions for the pages that the component consists of and - (possibly) overrides for some of the definitions from the root context - (for example, menus). + configuration for a lower level component generally contains only those + definitions for the pages that the component consists of and (possibly) + overrides for some definitions from the root context (for example, + menus). - Because each such lower level component will usually contain only - a few object definitions, application developers are encouraged to embed + Because each such lower level component usually contains only a + few object definitions, application developers are encouraged to embed those object definitions directly into the Web.config for the lower level context instead of relying on an external resource containing object definitions. This is easily accomplished by creating a @@ -496,49 +482,46 @@ (contained within the <spring/> element) simply tells the Spring.NET infrastructure code to load (its) object definitions from the spring/objects section of the - configuration file. + web.config configuration file. - You can (and should) avoid the need to specify - <configSections/> element by moving the - configuration handler definition for the + If Spring.NET is used for multiple applications on the same + server, you can avoid the need to specify the + <configSections/> element as shown in the + previous example, by moving the configuration handler definition for the <objects> element to a higher level (root) Web.config file, or even to the level of the - machine.config file if Spring.NET is to be used for - multiple applications on the same server. + machine.config file. - A very important point to be aware of is that this component-level - context can reference definitions from its parent context(s). Basically, - if a referenced object definition is not found in the current context, - Spring.NET will search all the ancestor contexts in the context - hierarchy until it finds said object definition (or ultimately fails and - throws an exception). + This component-level context can reference definitions from its + parent context(s). If a referenced object definition is not found in the + current context, Spring.NET searches all ancestor contexts in the + context hierarchy until it finds the object definition (or ultimately + fails and throws an exception). - Dependency Injection for ASP.NET Pages + Dependency injection for ASP.NET pages - Spring.Web builds on top of the feature set and capabilities of - ASP.NET; one example of this can be seen the way that Spring.Web has used - the code-behind class of the Page mechanism to satisfy - the Controller portion of the MVC architectural - pattern. In MVC-based (web) applications, the - Controller is typically a thin wrapper around one or - more service objects. In the specific case of Spring.Web, the Spring.NET - team realized that it was very important that service object dependencies - be easily injected into Page - Controllers. Accordingly, Spring.Web provides first - class support for dependency injection in ASP.NET - Pages. This allows application developers to inject any + An example of how Spring.Web builds on the capabilities of ASP.NET + is the way in which Spring.Web has used the code-behind class of the + Page mechanism to satisfy the + Controller portion of the MVC architectural pattern. In + MVC-based (web) applications, the Controller is + typically a thin wrapper around one or more service objects. It is + important that service object dependencies be easily injected into + Page Controllers. Accordingly, + Spring.Web provides first class support for dependency injection in + ASP.NET Pages. Application developers can inject any required service object dependencies (and indeed any other dependencies) - into their Pages using standard Spring.NET + into their Pages using the standard Spring.NET configuration instead of having to rely on custom service locators or manual object lookups in a Spring.NET application context. - Once an application developer has configured the Spring.NET web - application context, said developer can easily create object definitions - for the pages that compose that web application: + After an application developer configures the Spring.NET web + application context, the developer can easily create object definitions + for the pages that compose that web application. <objects xmlns="http://www.springframework.net"> @@ -554,55 +537,57 @@ </objects> - This example contains three definitions: + The preceding example contains three definitions: - + - The first definition is an abstract definition for the base page - that many other pages in the application will inherit from. In this - case, the definition simply specifies which page is to be referenced - as the master page, but it will typically also configure - localization-related dependencies and root folders for images, scripts - and CSS stylesheets. + An abstract definition for the base page from which many other + pages in the application will inherit. In this case, the definition + simply specifies which page is to be referenced as the master page, + but it typically also configures localization-related dependencies and + root folders for images, scripts, and CSS stylesheets. - The second definition defines a login page that neither inherits - from the base page nor references the master page. What it does show - is how to inject a service object dependency into a page instance (the + A login page that neither inherits from the base page nor + references the master page. This page shows how to inject a service + object dependency into a page instance (the authenticationService is defined elsewhere). - The final definition defines a default application page. In this - case it simply inherits from the base page in order to inherit the - master page dependency, but apart from that it doesn't need any - additional dependency injection configuration. + A default application page that, in this case, simply inherits + from the base page in order to inherit the master page dependency, but + apart from that it does not need any additional dependency injection + configuration. - + - One thing that slightly differentiates the configuration of ASP.NET - pages from the configuration of other .NET classes is in the value passed - to the type attribute. As can be seen in the above - configuration snippet the type name is actually the - path to the .aspx file for the Page, - relative to the directory context it is defined in. In the case of the - above example, those definitions are in the root context so - Login.aspx and Default.aspx also - must be in the root of the web application's virtual directory. The master - page is defined using an absolute path because it could conceivably be - referenced from child contexts that are defined within subdirectories of - the web application. + The configuration of ASP.NET pages differs from the configuration of + other .NET classes in the value passed to the type + attribute. As can be seen in the above configuration snippet, the + type name is actually the path to the + .aspx file for the Page, relative to + its directory context. When configuring other .NET classes one would + specify at minimum the fully qualified type name and the partial assembly + name. - The astute reader may have noticed that the definitions for the - Login and Default pages don't - specify either of the id and name - attributes. This is in marked contrast to typical object definitions in - Spring.NET, where the id or name - attributes are typically mandatory (although not always, as in the case of - inner object definitions). This is actually intentional, because in the - case of Spring.Web Page Controller - instances one typically wants to use the name of the + In the case of the above example, those definitions are in the root + context ,so Login.aspx and + Default.aspx files also must be located in the root of + the web application's virtual directory. The master page is defined using + an absolute path because it could conceivably be referenced from child + contexts that are defined within subdirectories of the web + application. + + The definitions for the Login and + Default pages do not specify either of the + id and name attributes, in marked + contrast to typical object definitions in Spring.NET, where the + id or name attributes are usually + mandatory (although not always, as in the case of inner object + definitions). In the case of Spring.Web manged Page + instances, one typically wants to use the name of the .aspx file name as the identifier. If an id is not specified, the Spring.Web infrastructure will simply use the name of the .aspx file as the object @@ -610,58 +595,64 @@ extension too). Nothing prevents an application developer from specifying an - id or name value explicitly; one use - case when the explicit naming might be useful is when one wants to expose - the same page multiple times using a slightly different configuration (Add - / Edit pages for example). If you would like to use abstract object - definitions and have your page inherit from them, the use of the name - attribute should be used instead of the id attribute on the abstract - object definition. + id or name value explicitly; + explicit naming can be useful when, for example, one wants to expose the + same page multiple times using a slightly different configuration, such as + Add / Edit pages. To use abstract object definitions and have your page + inherit from them, use the name attribute instead of + the id attribute on the abstract object + definition. - Injecting Dependencies into Controls + Injecting dependencies into controls Spring.Web also allows application developers to inject dependencies into controls (both user controls and standard controls) - that are contained within a page. This can be accomplished globally for + that are contained within a page. You can accomplish this globally for all controls of a particular Type by using the location of the .ascx as the object type identifier. - This is similar to injecting into .aspx pages shown - above. + This process is similar to injecting into .aspx + pages, shown above. <object type="~/controls/MyControl.ascx" abstract="true"> <!-- inject dependencies here... --> </object> - In either case, be sure to mark the object definition as - abstract (by adding - abstract="true" to the attribute list of the - <object/> element). + + In either case, be sure to mark the object definition as + abstract (by adding + abstract="true" to the attribute list of the + <object/> element). + Injecting dependencies into custom HTTP modules - You can perform dependency injection on custom HTTP modules - through the use of the class + You can inject dependencies into custom HTTP modules by using the + class Spring.Context.Support.HttpApplicationConfigurer. You - register your custom HTTP module as you would normally, for example a + register your custom HTTP module as you would normally; for example, a module of the type HtmlCommentAppenderModule, taken - from the Web Quickstart, appends additional comments into the http - response. It is registered as shown below + from the Web Quick Start, appends additional comments into the http + response. It is registered as follows: <httpModules> <add name="HtmlCommentAppender" type="HtmlCommentAppenderModule"/> </httpModules> - To configure this module, naming conventions are used to identify + To configure this module, you use naming conventions to identify the module name with configuration instructions in the Spring - configuration file. The ModuleTemplates property of - HttpApplicationConfigurer is a dictionary that takes as a key the name - of the HTTP module, HtmlCommentAppender, and as a value the - configuration instructions as you would normally use for configuring an - object with Spring. An example is shown below. - HttpApplicationConfigurer' ModuleTemplates property. + configuration file. The ModuleTemplates property of + HttpApplicationConfigurer is a dictionary that + takes as a key the name of the HTTP module, in this case + HtmlCommentAppender, and the Spring object + definition that describes how to perform dependency injection. The + object definition is in the standard <object/> style that you are + used to normally when configuring an object with Spring. An example is + shown below. HttpApplicationConfigurer' that configures the + HtmlCommentAppender's + AppendText property. <object name="HttpApplicationConfigurer" type="Spring.Context.Support.HttpApplicationConfigurer, Spring.Web"> <property name="ModuleTemplates"> @@ -676,25 +667,25 @@ </property> </object> - You can see this example in action in the Web Quickstart. + You can see this example in action in the Web Quick Start. Injecting dependencies into HTTP handlers and handler factories - You can perform dependency injection on - IHttpHandlers and - IHttpHandlerFactory. This effectively allows for a - fully Spring-managed <httpHandlers> - configuration section. To configure an IHttpHandler - or IHttpHandlerFactory you should register Spring's + Performing dependency injection on instances of + IHttpHandlers and IHttpHandlerFactory + allows for a fully Spring-managed + <httpHandlers> configuration section. To + perform dependency injection onan IHttpHandler or + IHttpHandlerFactory, register Spring's MappingHandlerFactory with a specific path or - wildcard string (i.e. *.aspx) using the standard configuration of an - <httpHandler> in web.config. This is shown - below + wildcard string (that is, *.aspx) using the standard configuration of an + <httpHandler> in web.config. For + example: - <system.web> + <system.web> <httpHandlers> <!-- the lines below map *any* request ending with *.ashx or *.whatever @@ -707,16 +698,19 @@ </httpHandlers> </system.web> - The specialization of which specific handler is mapped to the path - is done by configuration of Spring's - MappingHandlerFactoryConfigurer class. The - MappingHandlerFactoryConfigurer is configured by specifying a dictionary - of key value paris, the key value is a regular expression that will - match the request URL and the value is an instance of an IHttpHandler or - IHttpHandlerFactory configured via dependency injection. + Spring's MappingHandlerFactory serves a layer of indirection so + that you can configure multiple handler mappings with Spring. You do + this by configuring a IDictionary HandlerMap property on the class + MappingHandlerFactoryConfigurer. The dictionary + key is a regular expression that matches the request URL, and the value + is a reference to the name of a Spring managed instance of an + IHttpHandler or + IHttpHandlerFactory . The Spring managed instance + is configured via dependency injection using the standard + <object/> XML configuraiton schema. - the configuration of MappingHandlerFactoryConfigurer is shown - below + The configuration of + MappingHandlerFactoryConfigurer is shown: <objects xmlns="http://www.springframework.net"> @@ -749,27 +743,29 @@ </object> </objects> - Spring's DefaultHandlerFactory will use the - .NET class System.Web.UI.SimpleHandlerFactory to create handler instaces - and will configure each instances used an object definition whose name - matches the request url's filename. The abstract object definition of - DemoHandler.ashx is an example of this approach. You - may also configure standard classes that implment the IHttpHandler - interface as demonstrated in the example above for the class + Spring's DefaultHandlerFactory uses the .NET + class System.Web.UI.SimpleHandlerFactory to + create handler instances and configures each instance by using an object + definition whose name matches the request URL's filename. The abstract + object definition of DemoHandler.ashx is an example + of this approach. You can also configure standard classes that implement + the IHttpHandler interface as demonstrated in the + example above for the class MyCustomHttpHandler. - Please refer to the Web Quickstart application too see this in + Refer to the Web Quick Start application too see this in action. - Injecting dependencies into custom providers + Injecting dependencies in custom ASP.NET providers - Custom providers can be configured with Spring. The approach to - configuration is a family of adapters that correspond 1-to-1 with the - standard ASP.NET providers that are registered using the standard - ASP.NET mechanism. The adapters inherit from their correspondingly named - provider class in the BCL. + Custom providers can be configured via dependency injection with + Spring. The approach to configuration for providers is to use a family + of adapters that correspond 1-to-1 with the standard ASP.NET providers + that are registered via the standard ASP.NET mechanism. The adapters + inherit from their correspondingly named provider class in the .NET + class library. @@ -795,16 +791,16 @@ <membership defaultProvider="mySqlMembershipProvider"> <providers> <clear/> - <add connectionStringName="" name="mySqlMembershipProvider" type="Spring.Web.Providers.MembershipProviderAdapter, Spring.Web"/> + <add connectionStringName="" name="mySqlMembershipProvider" type="Spring.Web.Providers.MembershipProviderAdapter, Spring.Web"/> </providers> </membership> The name of the provider must match the name of the object in the - spring configuration that will serve as the actual provider - implementation. For convenience there are configurable versions of the - providers found in ASP.NET so that you can use the full functionality of - spring to configure these standard provider implementations, for example - using property place holders, etc. These are + Spring configuration that will serve as the actual provider + implementation. Configurable versions of the providers are found in + ASP.NET so that you can use the full functionality of Spring to + configure these standard provider implementations, by using property + placeholders, and so on. The providers are: @@ -828,10 +824,11 @@ - Here is an example configuration taken from the Web Quickstart - that simply sets the description property and connection string. + This example configuration taken from the Web Quick Start + application sets the description property and connection string. - <object id="mySqlMembershipProvider" type="Spring.Web.Providers.ConfigurableSqlMembershipProvider"> + <object id="mySqlMembershipProvider" type="Spring.Web.Providers.ConfigurableSqlMembershipProvider"> <property name="connectionStringName" value="MyLocalSQLServer" /> <property name="parameters"> <name-values> @@ -847,13 +844,18 @@ Customizing control dependency injection - There might be situations where it is necessary to customize - Spring.Web's dependency injection processing. In particular when using - GridViews, which create a large number of child controls, dependency - injection can slow down your page. To overcome this problem, you may - tell Spring to handle the dependency injection process yourself by - implementing the interface ISupportsWebDependencyInjection as shown - below: + You may need to customize Spring.Web's dependency injection + processing, such as when using GridViews or other complex 3rd party + custom controls. Often these controls are not configured using + dependency injection but Spring considers each control and its nested + child controls as candidates for DI. With very large (>1000) nested + controls that candidate evaluation process can unecessarily slow down + your page. To address this problem, you can tell Spring to not attempt + to configure via DI the sections of your page that contain these + controls or implementing the interface + ISupportsWebDependencyInjection and explicitly + ask Spring to inject dependencies on a particular contorol. These + approaches are shown below [C#] class MyControl : Control, ISupportsWebDependencyInjection @@ -875,9 +877,9 @@ class MyControl : Control, ISupportsWebDependencyInjection } } - There is a Spring server control, Panel, that provides an easier - way to turn of dependency injection for parts of your page. Example use - is shown below + A Spring server control, Panel, provides an + easier way to turn off dependency injection for parts of your + page: <spring:Panel runat="server" suppressDependencyInjection="true" @@ -887,11 +889,12 @@ class MyControl : Control, ISupportsWebDependencyInjection </spring:Panel> - By wrapping the performance sensitive parts of your page within - this panel, you can easily turn off DI using the attribute - suppressDependencyInjection. By default <spring:Panel/> won't - render a container tag (<div>, <span>, etc.). You can modify - this behavior by setting the attribute "renderContainerTag" + By wrapping the performance-sensitive parts of your page within + this panel, you can easily turn off DI by setting the attribute + suppressDependencyInjection to true. By default + <spring:Panel/> will not render a container + tag (<div>, <span>, and so on). You can modify this behavior + by setting the attribute renderContainerTag accordingly. @@ -901,47 +904,45 @@ class MyControl : Control, ISupportsWebDependencyInjection Spring.NET web applications support an additional attribute within object definition elements that allows you to control the scope of an - object: <object id="myObject" type="MyType, MyAssembly" scope="application | session | request"/>As - you can see, there are three possible values for the scope attribute -- - application, session or request. Application scope is the default, and - will be used for all objects that don't have scope attribute defined. As - its name says, it will result in a single instance of an object being - created for the duration of the application, so it works exactly like the - standard singleton objects in non-web applications. Session scope allows - you to define objects in such a way that an instance is created for each - HttpSession. This is the ideal scope for objects that you want bound to a - single user such as user profile, shopping cart, etc. Request scope will - result in a creation of an instance per HTTP request. + object: <object id="myObject" type="MyType, MyAssembly" scope="application | session | request"/>Possible + values for the scope attribute are application, + session, and request. Application + scope is the default, and is used for all objects with an undefined scope + attribute. This scope creates a single instance of an object for the + duration of the IIS application, so that the objects works exactly like + the standard singleton objects in non-web applications. Session scope + defines objects so that an instance is created for each HttpSession. This + scope is ideal for objects such as user profile, shopping cart, and so on + that you want bound to a single user. - Unlike with prototype objects, calls to - IApplicationContext.GetObject will return the same - instance of the request-scoped object during a single HTTP request. This - allows you, for example, to inject the same request-scoped object into - multiple pages and then use server-side transfer to move from one page to - another. As all the pages are executed within the single HTTP request in - this case, they will share the same instance of the injected - object. + Request scope creates one instance per HTTP request. Unlike calls to + prototype objects, calls to + IApplicationContext.GetObject return the same instance + of the request-scoped object during a single HTTP request. This allows + you, for example, to inject the same request-scoped object into multiple + pages and then use server-side transfer to move from one page to another. + As all the pages are executed within the single HTTP request in this case, + they share the same instance of the injected object. - One thing to keep in mind is that objects can only reference other - objects that are in the same or broader scope. This means that - application-scoped objects can only reference other application-scoped, - session-scoped objects can reference both session and application-scoped - objects, and finally, request-scoped objects can reference other request, - session or application-scoped objects. Also, prototype objects (and that - includes all ASP.NET web pages defined within Spring.NET context) can - reference singleton objects from any scope, as well as other prototype - objects. + Objects can only reference other objects that are in the same or + broader scope. This means that application-scoped objects can only + reference other application-scoped objects, session-scoped objects can + reference both session and application-scoped objects, and request-scoped + objects can reference other request-, session-, or application-scoped + objects. Also, prototype objects (including all ASP.NET web pages defined + within Spring.NET context) can reference singleton objects from any scope, + as well as other prototype objects. - Master Pages in ASP.NET 1.1 + Support for ASP.NET 1.1 master pages in Spring.Web Support for ASP.NET 1.1 master pages in Spring.Web is very similar to the support for master pages in ASP.NET 2.0. - The idea is that a web developer can define a layout template for - the site as a master page and specify content place holders that other - pages can then reference and populate. A sample master page + A web developer can define a layout template for the site as a + master page and specify content placeholders that other pages can then + reference and populate. A sample master page (MasterLayout.ascx) could look like this: <%@ Control language="c#" Codebehind="MasterLayout.ascx.cs" AutoEventWireup="false" Inherits="MyApp.Web.UI.MasterLyout" %> @@ -980,13 +981,13 @@ class MyControl : Control, ISupportsWebDependencyInjection </body> </html> - As you can see from the above code, the master page defines the - overall layout for the page, in addition to four content placeholders that - other pages can override. The master page can also include default content - within the placeholder that will be displayed if a derived page does not - override the placeholder. + In the preceding code, the master page defines the overall layout + for the page, in addition to four content placeholders that other pages + can override. The master page can also include default content within the + placeholder that will be displayed if a derived page does not override the + placeholder. - A page (Child.aspx) that uses this master page might look like + A page that uses this master page (Child.aspx) might look like this: <%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> @@ -1005,34 +1006,32 @@ class MyControl : Control, ISupportsWebDependencyInjection </body> </html> - The <spring:Content/> control in the above + The <spring:Content/> control in the example uses the contentPlaceholderId attribute (property) to specify exactly which placeholder from the master page is to be overridden. Because this particular page does not define content - elements for the head and title place holders, they will be displayed - using the default content supplied in the master page. + elements for the head and title placeholders, the content elements are + defined by the default content supplied in the master page. Both the ContentPlaceHolder and Content controls can contain any valid ASP.NET markup: - HTML, standard ASP.NET controls, user controls, etc. + HTML, standard ASP.NET controls, user controls, and so on. - VS.NET 2003 issue - Technically, the <html> and <body> tags from the previous example are not strictly necessary because they are already defined in the master page. - However, if these tags are omitted, then Visual Studio 2003 will - complain about a schema and intellisense won't work, so it's much easier - to work in the HTML view if those tags are included. They will be - ignored when the page is rendered. + However, if these tags are omitted, Visual Studio 2003 complains about a + schema, and IntelliSense does not work. So it is much easier to work in + the HTML view if those tags are included. They are ignored when the page + is rendered. - Linking child pages to their master + Linking child pages to their master page file The Spring.Web.UI.Page class exposes a property - called MasterPageFile, which can be used to specify + called MasterPageFile, which you can use to specify the master page. The recommended way to do this is by leveraging the Spring.NET IoC @@ -1052,51 +1051,48 @@ class MyControl : Control, ISupportsWebDependencyInjection </objects> This approach allows application developers to change the master - page being used for a number of pages within a web application. Of - course, the master page can still be overridden on a per context or per - page basis by creating a new abstract page definition within a child - context, or by specifying the MasterPageFile property - directly. + page for a number of pages within a web application. You can still + override the master page on a per context or per page basis by creating + a new abstract page definition within a child context, or by specifying + the MasterPageFile property directly. - Bidirectional Data Binding and Model Management + Bidirectional data binding and data model management - A problem with the existing data binding support in ASP.NET is that - it is one-way only. It allows application developers to bind page controls - to the data model and display information from said data model, but it - doesn't allow for the extraction of values from the controls when the form - is submitted. Spring.Web adds such bidirectional data binding to ASP.NET - by allowing developers to specify data binding rules for their page, and - by automatically evaluating configured data binding rules at the - appropriate time in the page's lifecycle. + The existing data binding support in ASP.NET is one-way only. It + allows application developers to bind page controls to the data model and + display information from the data model, but it does not permit the + extraction of values from the controls when the form is submitted. + Spring.Web adds bidirectional data binding to ASP.NET by allowing + developers to specify data binding rules for their page, and by + automatically evaluating configured data binding rules at the appropriate + time in the page's lifecycle. - ASP.NET also doesn't provide any support for model management within - the postbacks. Sure, it has a ViewState management, but that takes care of - the control state only and not of the state of any presentation model - objects these controls might be bound to. In order to manage model within - ASP.NET, developers will typically use HTTP Session object to store the - model between the postbacks. This results in a decent amount of - boilerplate code that can and should be eliminated, which is exactly what - Spring.Web does by providing a simple set of model management - methods. + ASP.NET does support model management within the postbacks. It has a + ViewState management, but that takes care of the control state only and + does not address the state of any presentation model objects to which + these controls are bound. To manage a model within ASP.NET, developers + typically use an HTTP session object to store the model between the + postbacks. This process results in boilerplate code that can and should be + eliminated, which is exactly what Spring.Web does by providing a simple + set of model management methods. - Please note that in order to take advantage of the bidirectional - data binding and model management support provided by Spring.Web, you - will have to couple your presentation layer to - Spring.Web; this is because features requires you to - extend a Spring.Web.UI.Page instead of the usual + To take advantage of the bidirectional data binding and model + management support provided by Spring.Web, you will + have to couple your presentation layer to Spring.Web; this is because + features require you to extend a + Spring.Web.UI.Page instead of the usual System.Web.UI.Page class. - Spring.Web data binding is very easy to use. Application developers - simply need to override the protected - InitializeDataBindings method and configure data - binding rules for the page. They also need to override three model + Spring.Web data binding is very easy to use. Simply override the + protected InitializeDataBindings method and configure + data binding rules for the page. You also need to override three model management methods: InitializeModel, - LoadModel and SaveModel. This is - perhaps best illustrated by an example from the SpringAir reference - application. First, let's take a look at the page markup:LoadModel and SaveModel. This + process is illustrated by an example from the SpringAir reference + application. First, take a look at the page markup:<%@ Page Language="c#" Inherits="TripForm" CodeFile="TripForm.aspx.cs" %> <asp:Content ID="body" ContentPlaceHolderID="body" runat="server"> @@ -1149,17 +1145,17 @@ class MyControl : Control, ISupportsWebDependencyInjection </asp:Content> Ignore for the moment the fact that none of the label - controls have text defined, which will be described later when we discuss - localization in Spring.NET. What is important for the purposes of our - current discussion, is that we have a number of input controls defined: - tripMode radio group, + controls have text defined; defining label controls is described later + when we discuss localization in Spring.NET. For the purposes of the + current discussion, a number of input controls are defined: + tripMode radio button group, leavingFromAirportCode and - goingToAirportCode dropdowns, as well as two Spring.NET - Calendar controls, departureDate and + goingToAirportCode dropdown lists, as well as two + Spring.NET Calendar controls, departureDate and returnDate. - Next, let's take a look at the model we will be binding this form - to:namespace SpringAir.Domain + Take a look at the model to which you bind the form:namespace SpringAir.Domain { [Serializable] public class Trip @@ -1249,7 +1245,7 @@ class MyControl : Control, ISupportsWebDependencyInjection way or return trip, which is exposed as Mode property. - Finally, let's see the code-behind class that ties everything + Here is the code-behind class that ties everything together:public class TripForm : Spring.Web.UI.Page { // model @@ -1306,89 +1302,90 @@ class MyControl : Control, ISupportsWebDependencyInjection // redirect to SuggestedFlights page } } -}There are quite a few things that are happening in this - relatively simple piece of code, so it's worth that we spend some time on - each one: +}Note the following about the three preceding pieces of + code: When the page is initially loaded (IsPostback == false), the InitializeModel() method is - called which initializes the trip object by creating a new instance + called, which initializes the trip object by creating a new instance and setting its properties to desired values. Right before the page - is rendered, the SaveModel() method will be - invoked and whatever the value it returns will be stored within the - HTTP Session. Finally, on each postback, the - LoadModel() method will be called and the value returned - by the previous call to SaveModel will be passed - to it as an argument. + is rendered, the SaveModel() method is invoked, + and the value it returns is stored within the HTTP session. On each + postback, the LoadModel() method is called, and + the value returned by the previous call to + SaveModel is passed to SaveModel as + an argument. In this particular case the implementation is very simple because our whole model is just the trip object. As such, SaveModel() simply returns the - trip object and LoadModel() - casts the savedModel() argument to + trip object, and LoadModel() + casts the SaveModel() argument to Trip and assigns it to the - trip field within the page. In the more complex - scenarios, you will typically return a dictionary containing your - model objects from the SaveModel() method, and - read the values from that dictionary within the - LoadModel(). + trip field within the page. In more complex + scenarios, the SaveModel() method will typically return a dictionary + that contains your model objects. Those values will be read from the + dictionary within the LoadModel() method. InitializeDataBindings method defines the - binding rules for all five input controls on our form. It does so by - invoking AddBinding method on the - BindingManager exposed by the page. - AddBinding method is heavily overloaded and it - allows you to specify a binding direction and a + binding rules for all of the five input controls on the form. The + controls are represented by the variables tripMode, + leavingFromAirportCode, goingToAirportCode, departueDate, and + returnDate. The binding rules are created by invoking the + AddBinding method on the + BindingManager exposed by the page. The + AddBinding method is heavily overloaded and it allows you + to specify a binding direction and a formatter to use in addition to the source and target binding expressions that are - used above. We'll discuss these optional parameters shortly, but for - now let's focus on the source and target expressions. + used above. These optional parameters are discussed later in this + chapter. For now, focus on the source and target expressions. - The Data Binding framework uses Spring.NET Expression Language - to define binding expressions. In most cases, like in the example - above, both source and target expression will evaluate to a property - or a field within one of the controls or a data model. This is - always the case when you are setting a bi-directional binding, as - both binding expressions need to be "settable". What is important to - remember about InitializeDataBindings method is - that it is executed only once per page type. - Basically, all of the binding expressions are parsed the first time - the page is instantiated, and are the cached and used by all - instances of that same page type that are created at a later time. - This is done for performance reasons, as data binding expression - parsing on every postback is unnecessary and would add a significant - overhead to the overall page processing time. + The Spring.NET data binding framework uses Spring.NET + Expression Language to define binding expressions. In most cases, as + in the example above, both source and target expression will + evaluate to a property or a field within one of the controls or a + data model. This is always the case when you are setting a + bidirectional binding, as both binding expressions need to be + "settable". The InitializeDataBindings method is + executed only once per page type. Basically, + all binding expressions are parsed the first time the page is + instantiated, and are then cached and used by all instances of that + same page type that are created at a later time. This is done for + performance reasons, as data binding expression parsing on every + postback is unnecessary and would add a significant overhead to the + overall page processing time. - If you look at the SearchForFlights event handler, you will - notice that it has no dependencies on the view elements. It simply - uses the injected bookingAgent service and a trip object that in - order to obtain a list of suggested flights. Furthermore, if you - make any modifications to the trip object within your event handler, - bound controls will be updated accordingly just before the page is + Notice that the SearchForFlights event handler + has no dependencies on the view elements. It simply uses the + injected bookingAgent service and a trip object in order to obtain a + list of suggested flights. Furthermore, if you make any + modifications to the trip object within your event handler, bound + controls are updated accordingly just before the page is rendered. - This accomplishes one of the major goals we set out to - achieve, allowing developers to remove view element references from - the page event handlers and decouple controller-type methods from - the view. + + The lack of view elements in the event handler + accomplishes one of the major goals we set out to achieve, + allowing developers to remove view element references from the + page event handlers and decouple controller-type methods from + the view. + - Now that you have a solid high-level picture of how Spring.NET data - binding and model management are typically used in web applications, let's - take a look at the details and see how data binding is actually - implemented under the hood, what the extension points are, and what are - some additional features that make data binding framework usable in - real-world applications. - - Data Binding Under the Hood + Data binding under the hood - Spring.NET Data Binding framework revolves around two main + This section describes how data binding is actually implemented, + the extension points, and additional features that make the data binding + framework usable in real-world applications. + + The Spring.NET data binding framework revolves around two main interfaces: IBinding and IBindingContainer. The IBinding interface is definitely the more important one of the two, as it has to @@ -1407,36 +1404,37 @@ class MyControl : Control, ISupportsWebDependencyInjection IDictionary variables); void SetErrorMessage(string messageId, params string[] errorProviders); -}As their names imply, BindSourceToTarget - method is used to extract and copy bound values from the source object - to the target object, while BindTargetToSource does - the opposite. Both method names and parameter types are very generic for - a good reason -- data binding framework can indeed be used to bind any - two objects. Using it to bind web forms to model objects is just one of - its possible uses, although a very common one and tightly integrated - into the Spring.NET Web Framework. +}The BindSourceToTarget method is used to + extract and copy bound values from the source object to the target + object, and BindTargetToSource does the opposite. + Both method names and parameter types are generic because the data + binding framework can be used to bind any two objects. Using it to bind + web forms to model objects is just one of its possible uses, although a + very common one and tightly integrated into the Spring.NET Web + Framework. - The validationErrors parameter requires further - explanation. While the data binding framework is not in any way coupled - to the data validation framework, they are in some ways related. For - example, while the data validation framework is best suited to validate - the populated model according to the business rules, the data binding - framework is in a better position to validate data types during the - binding process. However, regardless of where specific validation is + The ValidationErrors parameter requires further + explanation. Although the data binding framework is not in any way + coupled to the data validation framework, they are in some ways related. + For example, while the data validation framework is best suited to + validate the populated model according to the business rules, the data + binding framework is in a better position to validate data types during + the binding process. However, regardless of where specific validation is performed, all error messages should be presented to the user in a consistent manner. In order to accomplish this, Spring.NET Web Framework - passes the same ValidationErrors instance to binding methods and to any - validators that might be executed within your event handlers. This - ensures that all error messages are stored together and are displayed - consistently to the end user, using Spring.NET validation error - controls. + passes the same ValidationErrors instance to binding + methods and to any validators that might be executed within your event + handlers. This process ensures that all error messages are stored + together and are displayed consistently to the end user, using + Spring.NET validation error controls. The last method in the IBinding interface, - SetErrorMessage, enables this by allowing you to - specify the resource id of the error message to be displayed in the case - of binding error, as well as the list of error providers that messages - should be displayed in. We will see an example of the - SetErrorMessage usage shortly. + SetErrorMessage, enables you to specify the resource + id of the error message to be displayed in case of binding error, as + well as a list of strings, that server as identifiers to tag error + messages for the purposes of linking specific error messages to + locations in the page markup. We wil see an example of the + SetErrorMessage usage in a later section. The IBindingContainer interface extends the IBinding interface and adds the following @@ -1450,41 +1448,77 @@ class MyControl : Control, ISupportsWebDependencyInjection IBinding AddBinding(string sourceExpression, string targetExpression, IFormatter formatter); IBinding AddBinding(string sourceExpression, string targetExpression, BindingDirection direction, IFormatter formatter); -}As you can see, this interface has a number of overloaded - AddBinding methods. The first one, +}The IBindingContainer interface has several + overloaded AddBinding methods. AddBinding(IBinding binding) is the most generic one, as it can be used to add any binding type to the container. The other four are convenience methods that provide a simple way to add the most - commonly used binding type, SimpleExpressionBinding. - The SimpleExpressionBinding is what we used in the - example at the beginning of this section to bind our web form to a - Trip instance. It uses Spring.NET Expression Language - to extract and to set values within source and target objects. We - discussed sourceExpression and - targetExpression arguments earlier, so let's focus on - the remaining ones. + commonly used implementation of the + IBinding interface, + SimpleExpressionBinding. The + SimpleExpressionBinding was used under the covers in + the example at the beginning of this section to bind our web form to a + Trip instance when calling methods on the property + BindingManager. Note that the + BindingManager property is of the type + IBindingContainer. + SimpleExpressionBinding uses Spring.NET + Expression Language (SpEL) to extract and to set values within source + and target objects. + + In the TripForm example, the configuration of the BindingManager + shows the basic usage of how SpEL can be used to specify a + sourceExpression and + targetExpression arguments. This code section is + repeated below + + protected override void InitializeDataBindings() + { + BindingManager.AddBinding("tripMode.Value", "Trip.Mode"); + BindingManager.AddBinding("leavingFromAirportCode.SelectedValue", "Trip.StartingFrom.AirportCode"); + BindingManager.AddBinding("goingToAirportCode.SelectedValue", "Trip.ReturningFrom.AirportCode"); + BindingManager.AddBinding("departureDate.SelectedDate", "Trip.StartingFrom.Date"); + BindingManager.AddBinding("returnDate.SelectedDate", "Trip.ReturningFrom.Date"); + } + + In this case, the first argument is a + sourceExpression evaluated in the context of the page + itself. The sourceExpression + 'tripMode.Value' represents the value in the HTML control and + the targetExpression "Trip.Mode" represents the value + it will be mapped onto whent the page is rendered. When the post-back + happens values from in "Trip.Mode" get placed back + into the HTML control "tripMode.Value". This is a + common case in which bi-directional data mapping is symmetric in terms + of the sourceExpression and + targetExpression for both the initial rendering of + the page and when the post-back occurs. There other overloaded methods + that take BindingDirection and + IFormatter arguments are discussed in the next + section. - Binding Direction + Binding direction - The direction argument determines whether the binding is - bidirectional or unidirectional. By default, all data bindings are - bidirectional unless the direction argument is set to either - BindingDirection.SourceToTarget or + The direction argument determines whether the + binding is bidirectional or unidirectional. By default, all data + bindings are bidirectional unless the direction argument is set to + either BindingDirection.SourceToTarget or BindingDirection.TargetToSource. If one of these - two values is specified, binding will be evaluated only when the - appropriate BindDirection - method is invoked, and will be completely ignored in the other - direction. This is very useful when you want to bind some information - from the model into non-input controls, such as labels. + values is specified, binding is evaluated only when the appropriate + BindDirection + method is invoked, and is completely ignored in the other direction. + This configuration is very useful when you want to bind some + information from the model into non-input controls, such as + labels. However, unidirectional data bindings are also useful when your - form doesn't have a simple one-to-one mapping to presentation model. - In our earlier trip form example, the presentation model was + form does not have a simple one-to-one mapping to a presentation + model. In the earlier trip form example, the presentation model was intentionally designed to allow for simple one-to-one mappings. For the sake of discussion, let's add the Airport class - and modify our TripPoint class like - this:namespace SpringAir.Domain + and modify our TripPoint class as + follows:namespace SpringAir.Domain { [Serializable] public class TripPoint @@ -1537,40 +1571,45 @@ class MyControl : Control, ISupportsWebDependencyInjection set { this.name = value; } } } -}Instead of the string property - AirportCode, our TripPoint class - now exposes an Airport property of type - Airport, which is defined above. Now we have a - problem: what used to be a simple string to string binding, with the - airport code selected in a dropdown being copied directly into the - TripPoint.AirportCode property and vice versa, now becomes a not so - simple string to Airport binding, so let's see how - we can solve this mismatch problem. +}Instead of the string property AirportCode, our + TripPoint class now exposes an Airport + property of type Airport, which is defined in the + preceding example. What was formerly a simple string-to-string + binding, with the airport code selected in a dropdown being copied + directly into the TripPoint.AirportCode property and vice + versa, now becomes a not-so-simple string-to-Airport binding. So let's + see how we can solve this mismatch problem of converting a string to + an Airport instance and an Airport instance to a string. - First of all, binding from the model to the control is still - very straight forward. We just need to set up one-way bindings from - the model to controls:protected override void InitializeDataBindings() + Binding from the model to the control, namely the Airport to the + string, is still very straightforward. You set up one-way bindings + from the model to controls: The Model-To-Control is represented more + generally by the enumeration, BindingDirection.TargetToSource. + protected override void InitializeDataBindings() { BindingManager.AddBinding("leavingFromAirportCode.SelectedValue", "Trip.StartingFrom.Airport.Code", BindingDirection.TargetToSource); BindingManager.AddBinding("goingToAirportCode.SelectedValue", "Trip.ReturningFrom.Airport.Code", BindingDirection.TargetToSource); ... - }All we need to do is extract airport code value from the + }You extract the airport code value from the Trip.StartingFrom.Airport.Code instead of - Trip.StartingFrom.AirportCode. Unfortunately, - binding from the control to the model the same way won't work: we - might be able to set Code property of the - Airport object, but that will likely make the - Airport.Name property invalid. What we really want - do is find an instance of the Airport class based - on the airport code and set the TripPoint.Airport - property to it. Fortunately, this is very simple to do with Spring.NET - data binding, especially because we already have - airportDao object defined in the Spring context, - which has GetAirport(string airportCode) finder - method. All we need to do is set up data bindings from source to - target that will invoke this finder method when evaluating the source - expression. Our complete set of bindings for these two drop down lists - will then look like this:protected override void InitializeDataBindings() + Trip.StartingFrom.AirportCode since now the Code in + encapsulated inside the Airport class. Unfortunately, binding from the + control to the model the same way won't work, we need a way to create + an Airport instance from a string. Instead, you need to find an + instance of the Airport class based on the airport + code and set the TripPoint.Airport property to it. + Fortunately, Spring.NET data binding makes this simple, especially + because you already have airportDao object defined + in the Spring context (see SpringAir Spring context configuration file + for details.). The AirportDao has a GetAirport(string + airportCode) finder method. You set up data bindings from + source to target (control-to-model) that will invoke this finder + method when the page is submitted and the binding infrastructure maps + the sourceExpression onto the targetExpression.evaluating the source + expression. + + Our complete set of bindings for these two drop-down lists will + then look like this:protected override void InitializeDataBindings() { BindingManager.AddBinding("@(airportDao).GetAirport(leavingFromAirportCode.SelectedValue)", "Trip.StartingFrom.Airport", BindingDirection.SourceToTarget); BindingManager.AddBinding("leavingFromAirportCode.SelectedValue", "Trip.StartingFrom.Airport.Code", BindingDirection.TargetToSource); @@ -1578,27 +1617,27 @@ class MyControl : Control, ISupportsWebDependencyInjection BindingManager.AddBinding("@(airportDao).GetAirport(goingToAirportCode.SelectedValue)", "Trip.ReturningFrom.Airport", BindingDirection.SourceToTarget); BindingManager.AddBinding("goingToAirportCode.SelectedValue", "Trip.ReturningFrom.Airport.Code", BindingDirection.TargetToSource); ... - }That's it -- by using two unidirectional bindings with - different expressions and by leveraging the fact that expressions can - reference objects defined in the Spring context, we were able to solve - this non-trivial data binding problem. + }By using a pair of bindings for each control, one for + each direction and using SpEL's feature to reference objects defined + in the Spring context, you can resolve this data binding issue. - Formatters + formatter argument - The last argument to AddBinding method that - we need to discuss is a formatter argument. This - argument allows you to specify a formatter that should be used to - parse string value from the typical input control before it is bound - to the model, and to format strongly typed model value before it is - bound to the control. + The last overloaded methods of + IBindingContainer we need to discuss + are those that take a IFormatter argument. is an + argument to the AddBinding method. This argument + allows you to specify a formatter that you use to parse string value + from the input control before it is bound to the model, and to format + strongly typed model value before the model is bound to the + control. - You will typically use one of the formatters provided in the - Spring.Globalization.Formatters namespace, but if you have - requirements that cannot be satisfied by one of the standard - formatters it is easy enough to write your own -- all you need to do - is implement a very simple IFormatter interface:You typically use one of the formatters provided in the + Spring.Globalization.Formatters namespace, but if your requirements + cannot be satisfied by a standard formatter, you can write your own by + implementing a simple IFormatter interface:public interface IFormatter { string Format(object value); @@ -1611,58 +1650,59 @@ class MyControl : Control, ISupportsWebDependencyInjection FloatFormatter, IntegerFormatter, NumberFormatter and - PercentFormatter, which should be sufficient for - most usage scenarios. + PercentFormatter, which are sufficient for most + usage scenarios. - Type Conversion + Type conversion Because the data binding framework uses the same expression - evaluation engine as the Spring.NET IoC container, it will use any + evaluation engine as the Spring.NET IoC container, it uses any registered type converters to perform data binding. Many type converters are included with Spring.NET (take a look at the classes in - Spring.Objects.TypeConverters namespace) and automatically registered - for you, but you can implement your own custom converters and register - them using standard Spring.NET type converter registration - mechanisms. + Spring.Objects.TypeConverters namespace) and are automatically + registered for you, but you can implement your own custom converters + and register them by using standard Spring.NET type converter + registration mechanisms. - Data Binding Events + Data binding events Spring.Web's base Page class adds two events - to the standard .NET page lifecycle - DataBound and + to the standard .NET page lifecycle: DataBound and DataUnbound. - The DataUnbound event is fired after the data - model has been updated using values from the controls. It is fired - right after the Load event and only on postbacks, - because it doesn't make sense to update the data model using the + You can register for an DataUnbound event + which will be fired after the data model is updated with values from + the controls. Specifically, in terms of the Page lifecycle, it is + fired right after the Load event and only on + postbacks, because it not make sense to update the data model with the controls' initial values. - The DataBound is fired after controls have - been updated using values from the data model. This happens right + The DataBound event is fired after controls + are updated with values from the data model. This event occurs right before the PreRender event. - The fact that data model is updated immediately after the + The fact that the data model is updated immediately after the Load event and that controls are updated right before the PreRender event means that your event - handlers will be able to work with a correctly updated data model, as - they execute after the Load event, and that any - changes you make to the data model within event handlers will be - reflected in the controls immediately afterwards, as they (the - controls) are updated prior to the actual rendering. + handlers can work with a correctly updated data model, as they execute + after the Load event, and that any changes you make + to the data model within event handlers are reflected in the controls + immediately afterwards, as the controls are updated prior to the + actual rendering. - Rendering Binding Errors + Rendering binding errors - If there are errors in the databinding, for example, trying to - bind a string 'hello' to an integer property on the model, you can - specify how those fundamental binding errors should be rendered. An - example of this shown below taken from the WebQuickStart - 'RobustEmployeeInfo' example. + If errors occur in the databinding (for example, in trying to + bind a string 'hello' to an integer property on the model), you can + specify how those fundamental binding errors should be rendered. The + following snippet is from the Web Quick Start 'RobustEmployeeInfo' + example: [Default.aspx.cs] @@ -1680,23 +1720,37 @@ protected override void InitializeDataBindings() ... The SetErrorMessage specifies the message text or resource id of - the error message to be displayed followed by a variable length list - of strings that specify the collection of error providers message - where the message should be displayed. In the above case the error - provider will be rendered in Spring's ValidationError User Control. - See + the error message to be displayed. This is followed by a a variable + length list of strings that serve to as a means to assign a friendly + name to associate with this error should it occur. The same 'tag', or + error provider name, can be used across different calls to + 'AddBinding'. This is commonly the case if you want to present several + errors together in the page. In the preceding example, the 'tag' or + error provider name is "id.errors" will be rendered in Spring's + ValidationError User Control, for example as shown below in this + fragment of page markup. Validation controls are discussed more + extensively in this section. + + <td> + <asp:TextBox ID="txtId" runat="server" EnableViewState="false" /> + <spring:ValidationError ID="errId" Provider="id.errors" runat="server" /><!-- read msg from "id.error" provider --> + </td> HttpRequestListBindingContainer - HttpRequestListBindingContainer extracts posted raw values from - the request and populates the specified IList by creating objects of - the type specified and populating each of these objects according to - the requestBindings collection. + HttpRequestListBindingContainer + extracts posted raw values from the request and populates + the specified IList by creating objects of the type specified and + populating each object according to the requestBindings + collection. - Please checkout the WebQuickStart sample's demo of - HttpRequestListBindingContainer. Below + Please check out the Web Quick Start sample's demo of + HttpRequestListBindingContainer. Below is an + exerpt from that example showing how to use a + HttpRequestListBindingContainer. protected override void InitializeDataBindings() { @@ -1712,7 +1766,7 @@ protected override void InitializeDataBindings() } - Due to the fact, that browsers don't send the values of unchecked checkboxes, you can't use HttpRequestListBindingContainer with <input type="checkbox" > html controls. + Because browsers do not send the values of unchecked checkboxes, you cannot use HttpRequestListBindingContainer with <input type="checkbox" > html controls. @@ -1721,10 +1775,11 @@ protected override void InitializeDataBindings() Using DataBindingPanel To simplify use of Spring's Data Binding feature on web pages and - controls, Spring.Web provides a special DataBindingPanel container - control. A DataBindingPanel does not render any html code itself, but - allows for specifying additional, data binding related attributes to its - child controls: + controls, Spring.Web provides a special + DataBindingPanel container control. A + DataBindingPanel does not render any html code + itself, but allows you to define additional, data binding-related + attributes for its child controls. <%@ Page Language="C#" CodeFile="Default.aspx.cs" Inherits="DataBinding_EasyEmployeeInfo_Default" %> <%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> @@ -1735,7 +1790,7 @@ protected override void InitializeDataBindings() <tr> <td>Employee ID:</td> <td> - <asp:TextBox ID="txtId" runat="server" BindingTarget="Employee.Id" /> + <asp:TextBox ID="txtId" runat="server" BindingTarget="Employee.Id" /> </td> </tr> <tr> @@ -1747,48 +1802,39 @@ protected override void InitializeDataBindings() </body> </html> - Using DataBindingPanel the binding information can be specified + Using DataBindingPanel, you can specify the binding information directly on the control declaration. The following attributes are recognized by a DataBindingPanel: - BindingTarget - - corresponds to the target expression used in - IBindingContainer.AddBinding() + BindingTarget corresponds to the target + expression used in IBindingContainer.AddBinding(). - BindingSource - - corresponds to the source expression used in - IBindingContainer.AddBinding(). For standard controls you don't need - to specify the source expression. If you are binding to some custom - control, of course you must specific this attribute. + BindingSource corresponds to the source + expression used in IBindingContainer.AddBinding(). For standard + controls you don't need to specify the source expression. If you are + binding to some custom control, of course you must specific this + attribute. - BindingDirection - - one of the values of the BindingDirection enumeration + BindingDirection is one of the values of the + BindingDirection enumeration. - BindingFormatter - - if you need a custom formatter, you can specific the object - name of a formatter here. The formatter instance will be obtained by - a call to IApplicationContext.GetObject() each time it is - needed. + BindingFormatter is the object name of a custom + formatter. The formatter instance is obtained by a call to + IApplicationContext.GetObject() each time it is needed. - BindingType - - In case you need a completely customized binding, specify its - type here. Note that a custom binding type must implement the - following constructor signature: + BindingType is the type of a completely + customized binding. Note that a custom binding type must implement + the following constructor signature: ctor(string source,string target, BindingDirection, IFormatter) @@ -1796,30 +1842,30 @@ protected override void InitializeDataBindings() - The Visual Studio Web Form Editor will of course complain about binding attributes because it doesn't know them. You can safely ignore those warnings. + The Visual Studio Web Form Editor complains about binding attributes because it does not recognize them. You can safely ignore those warnings. - Customizing Model Persistence + Customizing model persistence - As was already mentioned in the introduction to this chapter, - model management needs an application developer to override + As mentioned in the chapter introduction, model management needs + an application developer to override InitializeModel(), SaveModel() and - LoadModel() for storing model information between - requests in the user's session. On web farms this of course storing - information in a user's session is not a good strategy. Thus it is - possible to choose another persistence strategy by setting a - Spring.Web.UI.Page's resp. Spring.Web.UI.UserControl's - ModelPersistenceMedium property: + LoadModel() in order to store model information + between requests in the user's session. On web farms, storing + information in a user's session is not a good strategy. You can choose + another persistence strategy by setting the + ModelPersistenceMedium property on Spring's base + Page or UserContorl class (e.g. + Spring.Web.UI.UserControl) <object id="modelPersister" type="Sample.DatabaseModelPersistenceMedium, MyCode"/> <object type="UserRegistration.aspx"> <property name="ModelPersistenceMedium" ref="modelPersister"/> </object>To implement any arbitrary persistence - strategy, one simply needs to implement the IModelPersistenceMedium - interface: + strategy, implement the IModelPersistenceMedium interface: public interface IModelPersistenceMedium { @@ -1833,46 +1879,110 @@ protected override void InitializeDataBindings() - Localization and Message Sources + Localization and message sources - While recognizing that the .NET framework has excellent support for - localization, the support within ASP.NET 1.x is somewhat - incomplete. + Although the .NET framework has excellent localization support, the + support within ASP.NET 1.x is incomplete. Spring provides support for + localization in ASP.NET 1.1 apps in the manner of ASP.NET 2.0. Despite the + initial focus on righer localization for ASP.NET 1.1 applications, using + Spring's localization features in ASP.NET 2.0 or higher applications does + provide some useful additional features with a similar programming model, + such as image localization, push mechansims, and built-in support for user + culture management via various mechansims. Every .aspx page in an ASP.NET project has a - resource file associated with it, but those resources are never used (by - the current ASP.NET infrastructure). ASP.NET 2.0 will change that and - allow application developers to use local resources for pages. In the - meantime, the Spring.NET team built support for using local pages - resources into Spring.Web thus allowing application developers to start - using ASP.NET 2.0-like page resources immediately. + resource file associated with it, but those resources are never used by + the current ASP.NET infrastructure). ASP.NET 2.0 changes this and allow + application developers to use local resources for pages. In the meantime, + the Spring.NET team built in to Spring.Web support for using local pages + resources, thus allowing ASP.NET 1.1 application developers to using + ASP.NET 2.0-like page resources. Spring.Web supports several different approaches to localization within a web application, which can be mixed and matched as appropriate. - Both push and pull mechanisms are supported, as well as the fallback to - globally defined resources when a local resource cannot be found. - Spring.Web also provides support for user culture management and image - localization, which are described in the later sections. + You can use push and pull mechanisms, as well as globally defined + resources when a local resource cannot be found. Spring.Web also supports + user culture management and image localization, which are described in + later sections. - Introductory material covering ASP.NET Globalization and - Localization can be found at the following URLs; For introductory information covering ASP.NET globalization and + localization, see Globalization Architecture for ASP.NET and Localization Practices for ASP.NET 2.0 by Michele Leroux Bustamante. - - Automatic Localization Using Localizers ("Push" - Localization) + + Working with localizers - The central idea behind 'push' localization is that an application - developer should be able to specify localization resources in the - resource file for the page and have those resources automatically - applied to the user controls on the page by the framework. For example, - an application developer could define a page such as - UserRegistration.aspx... + A localizer is an object that implements the + Spring.Globalization.ILocalizer interface. + Spring.Globalization.AbstractLocalizer is a + convenient base class for localization: this class has one abstract + method, LoadResources. This method must load and + return a list of all resources that must be automatically applied from + the resource store. + + To apply resources automatically, a localizer needs to be injected + into all pages that require automatic resource application. You + typically accomplish configuration using dependency injection of a page + base page definition that other page definitions will inherit from. The + injected localizer inspects the resource file when the page is first + requested, caches the resources that start with the + '$this' marker string value, and applies the values + to the controls that populate the page prior to the page being + rendered. + + Spring.NET ships with one concrete implementation of a localizer, + Spring.Globalization.Localizers.ResourceSetLocalizer, + that retrieves a list of resources to apply from the local resource + file. Future releases of Spring.NET may provide other localizers that + read resources from an XML file or even from a flat text file that + contains resource name-value pairs that allow application developers to + store resources within the files in a web application instead of as + embedded resources in an assembly. Of course, if an application + developer prefers to store such resources in a database, the developer + can write a custom ILocalizer implementation that + loads a list of resources to apply from a database. + + You typically configure the localizer to be used within an + abstract base definition for those pages that require + localization: + + <object id="localizer" type="Spring.Globalization.Localizers.ResourceSetLocalizer, Spring.Core"/> + +<object name="basePage" abstract="true"> + <description> + Pages that reference this definition as their parent + (see examples below) will automatically inherit following properties. + </description> + <property name="Localizer" ref="localizer"/> +</object> + + Of course, nothing prevents an application developer from defining + a different localizer for each page in the application; in any case, one + can always override the localizer defined in a base (page) definition. + Alternatively, if one does want any resources to be applied + automatically one can completely omit the localizer definition. + + One last thing to note is that Spring.NET + UserControl instances will (by default) inherit the + localizer and other localization settings from the page that they are + contained within, but one can similarly also override that behavior + using explicit dependency injection. + + + + Automatic localization with localizers ("push" + localization) + + With push localization, an application developer specifies + localization resources in the resource file for the page, and the + framework automatically applies those resources to the user controls on + the page. For example, an application developer could define a page such + as UserRegistration.aspx: <%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> <%@ Page language="c#" Codebehind="UserRegistration.aspx.cs" @@ -1914,12 +2024,11 @@ protected override void InitializeDataBindings() </body> </html> - A close inspection of the above .aspx code - reveals that none of the Label or - Button controls have had a value assigned to the - Text property. The values of the - Text property for these controls are stored in the - local resource file (of the page) using the following convention to + In the preceding .aspx code, none of the + Label or Button controls have had + a value assigned to the Text property. The values of + the Text property for these controls are stored in + the local resource file (of the page) using the following convention to identify the resource (string). $this.controlId.propertyName @@ -1946,7 +2055,7 @@ protected override void InitializeDataBindings() <data name="$this.countryLabel.Text"> <value>Country:</value> </data> - <data name="$this.saveButton.Text"> + <data name="$this.saveButton.Text"> <value>$messageSource.save</value> </data> <data name="$this.cancelButton.Text"> @@ -1955,31 +2064,36 @@ protected override void InitializeDataBindings() </root> - VS2003 + Viewing .resx file in Visual Studio 2003 To view the .resx file for a page, you may need to enable "Project/Show All Files" in Visual Studio. When "Show All Files" is enabled, the .resx file appears like a "child" of the code-behind page. - When Visual Studio creates the .resx file, it will include a + When Visual Studio creates the .resx file, it includes an xds:schema element and several reshead elements. Your data elements will follow the reshead elements. When working with the .resx files, you may want to choose "Open With" from the context menu and select the "Source Code" text editor. + + There is no way to visually edit resources in a RESX file. Lutz + Roeder has created a tool named Resourcer that you + can use to edit them - VS2005 + Creating a .resx file in Visual Studio 2005/8 - To create a resource file in VS2005, open your control or page + To create a resource file in VS 2005, open your control or page in design mode and select "Tools/Generate local resource" from the - menu + menu. - Finally a localizer must be configured for the page to enable - automatic localization: + You must create a localizer for the page to enable automatic + localization: <object id="localizer" type="Spring.Globalization.Localizers.ResourceSetLocalizer, Spring.Core"/> @@ -1992,10 +2106,10 @@ protected override void InitializeDataBindings() - Global Message Sources + Global message sources - The last two resource definitions from the previous section - require some additional explanation: + Two resource definitions from the previous section require some + additional explanation: <data name="$this.saveButton.Text"> <value>$messageSource.save</value> @@ -2017,19 +2131,20 @@ protected override void InitializeDataBindings() $messageSource. - Taking the case of the above example, this will tell the localizer - to use the save and cancel - portions of the resource key as lookup keys to retrieve the actual - values from a global message source. The important thing to remember is - that one need only define a resource redirect once, typically in the - invariant resource file – any lookup for a resource redirect will simply - fall back to the invariant culture, and result in a global message - source lookup using the correct culture. + In the preceding example, this string tells the localizer to use + the save and cancel portions of + the resource key as lookup keys to retrieve the actual values from a + global message source. You need to define a resource redirect only + once, typically in the invariant resource file. Any lookup for a + resource redirect falls back to the invariant culture, and results in a + global message source lookup using the correct culture. Global resources are (on a per-context basis) defined as a plain vanilla object definition using the reserved name of - 'messageSource', which one can add to one's - Spring.NET configuration file as shown below. + messageSource, which you can add to your Spring.NET + configuration file: <object id="messageSource" type="Spring.Context.Support.ResourceSetMessageSource, Spring.Core"> <property name="ResourceManagers"> @@ -2040,105 +2155,45 @@ protected override void InitializeDataBindings() </object> - NET 2.0 + for .NET 2.0 or higher To use resources from your App_GlobalResources folder, specify - App_GlobalResources as assembly name (see the - SpringAir example application for more): + App_GlobalResources as the assembly name: <value>Resources.Strings, App_GlobalResources</value> - The global resources are cached within the Spring.NET + See the SpringAir example application for more. The global + resources are cached within the Spring.NET IApplicationContext and are accessible through the Spring.NET IMessageSource interface. The Spring.Web Page and UserControl classes have a reference to their owning - IApplicationContext and it's associated - IMessageSource. As such, they will automatically - redirect resource lookups to a global message source if a local resource - cannot be found. + IApplicationContext and its associated + IMessageSource. As such, they automatically redirect + resource lookups to a global message source if a local resource cannot + be found. Currently, the ResourceSetMessageSource is the only message source implementation that ships with Spring.NET. - - Working with Localizers - - In order to apply resources automatically, a localizer needs to be - injected into all pages requiring this feature (typically accomplished - using a base page definition that other pages will inherit from). The - injected localizer will inspect the resource file when the page is first - requested, cache the resources that start with the - '$this' marker string value, and apply the values to - the controls that populate the page prior to the page being - rendered. - - A localizer is simply an object that implements the - Spring.Globalization.ILocalizer interface. - Spring.Globalization.AbstractLocalizer is provided as - a convenient base class for localization: this class has one abstract - method, LoadResources. This method must load and - return a list of all the resources that must be automatically applied - from the resource store. - - Spring.NET ships with one concrete implementation of a localizer, - Spring.Globalization.Localizers.ResourceSetLocalizer, - that retrieves a list of resources to apply from the local resource - file. Future releases of Spring.NET may provide other localizers that - read resources from an XML file or even a flat text file that contains - resource name-value pairs which will allow application developers to - store resources within the files in a web application instead of as - embedded resources in an assembly. Of course, if an application - developer would rather store such resources in a database, he or she can - write their own ILocalizer implementation that will - load a list of resources to apply from a database. - - As mentioned previously, one would typically configure the - localizer to be used within an abstract base definition for those pages - that require localization as shown below. - - <object id="localizer" type="Spring.Globalization.Localizers.ResourceSetLocalizer, Spring.Core"/> - -<object name="basePage" abstract="true"> - <description> - Pages that reference this definition as their parent - (see examples below) will automatically inherit following properties. - </description> - <property name="Localizer" ref="localizer"/> -</object> - - Of course, nothing prevents an application developer from defining - a different localizer for each page in the application; in any case, one - can always override the localizer defined in a base (page) definition. - Alternatively, if one does want any resources to be applied - automatically one can completely omit the localizer definition. - - One last thing to note is that Spring.NET - UserControl instances will (by default) inherit the - localizer and other localization settings from the page that they are - contained within, but one can similarly also override that behavior - using explicit dependency injection. - - - Applying Resources Manually ("Pull" Localization) + Applying resources manually ("pull" localization) - While automatic localization as described above works great for - many form-like pages, it doesn't work nearly as well for the controls - defined within any iterative controls because the IDs for such iterative - controls are not fixed. It also doesn't work well in those cases where - one needs to display the same resource multiple times within the same - page. For example, think of the header columns for outgoing and return - flights tables within the SpringAir application (see Although automatic localization as described above works well for + many form-like pages, it doesn't work nearly as well for controls + defined within any iterative controls, because the IDs for such + iterative controls are not fixed. Nor does automatic localization work + well if you need to display the same resource multiple times within the + same page. For example, think of the header columns for outgoing and + return flights tables within the SpringAir application (see ). - In these situations, one should use a pull-style mechanism for - localization, which boils down to a simple GetMessage - call as shown below. + These situations call for a pull-style mechanism for localization, + which is a simple GetMessage call: <asp:Repeater id="outboundFlightList" Runat="server"> <HeaderTemplate> @@ -2163,32 +2218,30 @@ protected override void InitializeDataBindings() The GetMessage method is available within both the Spring.Web.UI.Page and - Spring.Web.UI.UserControl classes, and it will - automatically fall back to a global message source lookup if a local - resource is not found. + Spring.Web.UI.UserControl classes, and it falls back + automatically to a global message source lookup if a local resource is + not found. - Localizing Images within a Web Application + Localizing images within a web application - Spring.Web provides an easy (and consistent) way to localize - images within a web application. Unlike text resources, which can be - stored within embedded resource files, XML files, or even a database, - images in a typical web application are usually stored as files on the - file system. Using a combination of directory naming conventions and a - custom ASP.NET control, Spring.Web allows application developers to - localize images within the page as easily as text resources. + Unlike text resources, which can be stored within embedded + resource files, XML files, or even a database, images in a typical web + application are usually stored as files on the file system. Using a + combination of directory naming conventions and a custom ASP.NET + control, Spring.Web allows you to localize images within the page as + easily as you do text resources. The Spring.Web Page class exposes the - ImagesRoot property, which is used to define the root - directory where images are stored. The default value is 'Images', which - means that the localizer expects to find an 'Images' directory within - the application root, but one can set it to any value in the definition - of the page. + ImagesRoot property, with which you define the root + directory where images are stored. The default value is Images, which + means that the localizer expects to find an Images directory within the + application root. But you can set the property to any value in the + definition of the page. - In order to localize images, one needs to create a directory for - each localized culture under the ImagesRoot directory - as shown below. + To localize images, you create a directory for each localized + culture under the ImagesRoot directory: /MyApp /Images @@ -2200,11 +2253,11 @@ protected override void InitializeDataBindings() /sr-SP-Latn ... - Once an appropriate folder hierarchy is in place all one need do - is put the localized images in the appropriate directories and make sure - that different translations of the same image are named the same. In - order to place a localized image on a page, one needs to use the - <spring:LocalizedImage> as shown below. + Once an appropriate folder hierarchy is in place, you put the + localized images in the appropriate directories and make sure that + different translations of the same image have the same image name within + the folders. To place a localized image on a page, you use the + <spring:LocalizedImage>: <%@ Page language="c#" Codebehind="StandardTemplate.aspx.cs" AutoEventWireup="false" Inherits="SpringAir.Web.StandardTemplate" %> @@ -2228,21 +2281,22 @@ protected override void InitializeDataBindings() - User Culture Management + User culture <!--GLOBAL: I see "culture" used in various places, before this chapter and here, but there's no explanation for what you mean by that.No explanation + +for culture resolver, etc.-->management In addition to global and local resource management, Spring.Web - also adds support for user culture management by exposing the current + supports user culture management by exposing the current CultureInfo through the UserCulture property on the Page and UserControl classes. - The UserCulture property will simply delegate - culture resolution to an implementation of - Spring.Globalization.ICultureResolver interface. One - can specify exactly which culture resolver to use by configuring the + The UserCulture property delegates culture + resolution to an implementation of the + Spring.Globalization.ICultureResolver interface. You + specify the culture resolver to use by configuring the CultureResolver property of the - Page class in the relevant object definition as shown - below. + Page class in the relevant object definition: <object name="BasePage" abstract="true"> <property name="CultureResolver"> @@ -2252,65 +2306,67 @@ protected override void InitializeDataBindings() Several useful implementations of ICultureResolver ship as part of Spring.Web, so it is - unlikely that application developers will have to implement their own - culture resolver. However, if one does have such a requirement, the - resulting implementation should be fairly straightforward as there are - only two methods that one need implement. The following sections discuss - each available implementation of the ICultureResolver + unlikely that application developers need to implement their own culture + resolver. However, you do need to implement your own culture resolver, + the resulting implementation should be fairly straightforward as you + need to implement only two methods. The following sections discuss each + available implementation of the ICultureResolver interface. DefaultWebCultureResolver - This is default culture resolver implementation. It will be used - if one does not specify a culture resolver for a page, or if one - explicitly injects a DefaultWebCultureResolver into - a page definition explicitly. The latter case (explicit injection) is - sometimes useful because it allows one to specify a culture that - should always be used by providing a value to the - DefaultCulture property on the resolver. + DefaultWebCultureResolver, the default + culture resolver implementation, is used if you do not specify a + culture resolver for a page, or if you inject a + DefaultWebCultureResolver into a page definition + explicitly. The latter case (explicit injection) is sometimes useful + because you can specify a culture that should always be used, by + defining the DefaultCulture property on the + resolver. - The DefaultWebCultureResolver will first look - at the DefaultCulture property and return its value - if said property value is not null. If it is null, the - DefaultWebCultureResolver will fall back to request - header inspection, and finally, if no 'Accept-Lang' - request headers are present it will return the UI culture of the + The DefaultWebCultureResolver looks first at + the DefaultCulture property and return its value if + said property value is not null. If it is null, the + DefaultWebCultureResolver falls back to request + header inspection. If no 'Accept-Lang' request + headers are present , the resolver returns the UI culture of the currently executing thread. RequestCultureResolver - This resolver works in a similar way to the - DefaultWebCultureResolver with the exception that - it always checks request headers first, and only - then falls back to the value of the DefaultCulture - property or the culture code of the current thread. + The RequestCultureResolver resolver operates similar to the + DefaultWebCultureResolver, except that it always + checks request headers first, and only then falls + back to the value of the DefaultCulture property or + the culture code of the current thread. SessionCultureResolver - This resolver will look for culture information in the user's - session and return it if it finds one. If not, it will fall back to - the behavior of the - DefaultWebCultureResolver. + The SessionCultureResolver resolver + looks for culture information in the user's session and returns the + information if it finds it. If not, + SessionCultureResolver falls back to the + behavior of the DefaultWebCultureResolver. CookieCultureResolver - This resolver will look for culture information in a cookie, and - return it if it finds one. If not, it will fall back to the behavior - of the DefaultWebCultureResolver. + This resolver looks for culture information in a cookie, and + return it if it finds one. If not, it falls back to the behavior of + the DefaultWebCultureResolver. - CookieCultureResolver will not work if your + CookieCultureResolver does not work if your application uses localhost as the server URL, which is a typical setting in a development environment. - In order to work around this limitation you should use + To work around this limitation, use SessionCultureResolver during development and switch to CookieCultureResolver before you deploy the application in a production. This is easily accomplished in @@ -2321,22 +2377,26 @@ protected override void InitializeDataBindings() - Changing Cultures + Changing cultures - In order to be able to change the culture application developers - will need to use one of the culture resolvers that support culture - changes, such as SessionCultureResolver or - CookieCultureResolver. One could also write a custom - ICultureResolver that will persist culture - information in a database, as part of a user's profile. + To change the culture, application developers need to define one + of the culture resolvers that support culture changes, such as + SessionCultureResolver or + CookieCultureResolver in the Spring application + context. For example, - Once that requirement is satisfied, all that one need do is to set - the UserCulture property to a new + You also can write a custom ICultureResolver + that persists culture information in a database, as part of a user's + profile. + + <object id="cultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web" /> + + Once that requirement is satisfied, you set the + UserCulture property to a new CultureInfo object before the page is rendered. In - the following .aspx example, there are two link - buttons that can be used to change the user's culture. In the - code-behind, this is all one need do to set the new culture. A code - snippet for the code-behind file + the following .aspx example, two link buttons can be + used to change the user's culture. In the code-behind, this is all one + need do to set the new culture. A code snippet for the code-behind file (UserRegistration.aspx.cs) is shown below. protected override void OnInit(EventArgs e) @@ -2357,30 +2417,30 @@ private void SetLanguage(object sender, CommandEventArgs e) - Result Mapping + Result mapping - One of the problems evident in many ASP.NET applications is that - there is no built-in way to externalize the flow of an application. The - most common way of defining application flow is by hardcoding calls to the - Response.Redirect and - Server.Transfer methods within event handlers. + In many ASP.NET applications, no built-in way exists to externalize + the flow of the application. The most common way of defining application + flow is by hardcoding calls to the Response.Redirect + and Server.Transfer methods within event + handlers. This approach is problematic because any changes to the flow of an application necessitates code changes (with the attendant recompilation, - testing, redeployment, etc). A much better way, and one that has been - proven to work successfully in many MVC ( Model-View-Controller) - web frameworks is to provide the means to externalize the mapping of - action results to target pages. + web frameworks, is to enable you to externalize the mapping of action + results to target pages. - Spring.Web adds this functionality to ASP.NET by allowing one to + Spring.Web adds this functionality to ASP.NET by allowing you to define result mappings within the definition of a page, and to then simply use logical result names within event handlers to control application flow. In Spring.Web, a logical result is encapsulated and defined by the - Result class; because of this one can configure results - just like any other object: + Result class; thus you can configure results like any + other object: <objects xmlns="http://www.springframework.net"> @@ -2415,66 +2475,66 @@ private void SetLanguage(object sender, CommandEventArgs e) </objects> - The only property that you must supply a value - for each and every result is the TargetPage property. - The value of the Mode property can be either + The only property for which you must supply a + value for each result is the TargetPage property. The + value of the Mode property can be Transfer, TransferNoPreserve, - Redirect, and defaults to Transfer - if none is specified. TransferNoPreserve issues a server-side transfer - with 'preserveForm=false', so that QueryString and Form data are not + or Redirect, and defaults to + Transfer if none is specified. + TransferNoPreserve issues a server-side transfer with + 'preserveForm=false', so that QueryString and Form data are not preserved. - If one's target page requires parameters, one can define them using - the Parameters dictionary property. One simply - specifies either literal values or object - navigation expressions for such parameter values; if one specifies - an expression, this expression will be evaluated in the context of the - page in which the result is being referenced... in the specific case of - the above example, this means that any page that uses the - homePageResult needs to expose a - UserInfo property on the page class itself. - In Spring 1.1.0 and before the prefix used to indicate an object + If your target page requires parameters, you can define them with + the Parameters dictionary property. You specify literal + values or object navigation expressions + for such parameter values. An expression is evaluated in the context of + the page in which the result is being referenced. In the preceding + example, any page that uses the homePageResult needs to + expose a UserInfo property on the page class + itself. + In Spring.NET 1.1.0 and earlier, the prefix indicated an object navigation expression in the Parameters dictionary - property was the dollar sign, i.e. - ${UserInfo.FullName}.This conflicted with the - prefix used to perform property replacement, the dollar sign, as - described in the section ${UserInfo.FullName}.This convention conflicted + with the prefix used to perform property replacement, the dollar sign, + as described in the section PropertyPlaceholderConfigurer. - As a workaround you can change the prefix and suffix used in - PropertyPlaceholderConfigurer to be different, for example prefix = - $${ and suffix = }. In Spring 1.1.1 a new prefix character, the - percent sign (i.e.%{UserInfo.FullName}.) can be + As a workaround you can differentiate the prefix and suffix used in + PropertyPlaceholderConfigurer, for example prefix + = $${ and suffix = }. In Spring. NET 1.1.1, a new prefix character, + the percent sign (i.e.%{UserInfo.FullName}.) can be used in the Parameters dictionary to avoid this - conflict so you can keep the familiar NAnt style + conflict , so you can keep the familiar NAnt style PropertyPlaceholderConfigurer defaults. - Parameters will be handled differently depending on the result mode. - For redirect results, every parameter will be converted to a string, then - URL encoded, and finally appended to a redirect query string. On the other - hand, parameters for transfer results will be added to the - HttpContext.Items collection before the request is - transferred to the target page. This means that transfers are more - flexible because any object can be passed as a parameter between pages. - They are also more efficient because they don't require a round-trip to - the client and back to the server, so transfer mode is recommended as the - preferred result mode (it is also the current default). + Parameters are handled differently depending on the result mode. For + redirect results, every parameter is converted to a string, then URL + encoded, and finally appended to a redirect query string. Parameters for + transfer results are added to the HttpContext.Items + collection before the request is transferred to the target page. Transfers + are more flexible because any object can be passed as a parameter between + pages. They are also more efficient because they don't require a + round-trip to the client and back to the server, so transfer mode is + recommended as the preferred result mode (it is also the current + default). - If you should need to customize how to a redirect request is - generate, for example to encrypt the request parameters, subclass the - Request object and override one or more protected methods, for example - string BuildUrl( string resolvedPath, IDictionary - resolvedParameters ). See the API documentation for additional + If you need to customize how a redirect request is generated, for + example, to encrypt the request parameters, subclass the Request object + and override one or more protected methods, for example string + BuildUrl( string resolvedPath, IDictionary resolvedParameters + ). See the API documentation for additional information. - The above example shows independent result object definitions, which - are useful for global results such as a home- and login- page. - Result definitions that are only going to be used by - one page should be simply embedded within the definition of a page, either - as inner object definitions or using a special shortcut notation for - defining a result definition: + The preceding example shows independent result object definitions, + which are useful for global results such as a home- and login- page. + Result definitions are only used by one page should be + simply embedded within the definition of a page, either as inner object + definitions or using a special shortcut notation for defining a result + definition: <object type="~/UI/Forms/UserRegistration.aspx" parent="basePage"> @@ -2496,37 +2556,41 @@ private void SetLanguage(object sender, CommandEventArgs e) [<mode>:]<targetPage>[?param1,param2,...,paramN] - There are three possible values for the mode - value referred to in the above notation snippet; they are: + Possible values for the mode value referred to in + the preceding notation snippet: - redirect: calls Response.Redirect(string) + redirect: calls + Response.Redirect(string) - redirectNoAbort: calls Response.Redirect(string, false) + redirectNoAbort: calls + Response.Redirect(string, false) - transfer: calls Server.Transfer(string) + transfer: calls + Server.Transfer(string) - TransferNoPreserve: calls Server.Transfer(string, false) + TransferNoPreserve: calls + Server.Transfer(string, false) - They correspond to the values of the ResultMode enumeration. One - thing to notice is that a comma is used instead of an ampersand to - separate parameters; this is done so as to avoid the need for laborious - ampersand escaping within an XML object definition. The use of the - ampersand character is still supported if required, but one will then have - to specify it using the well known & entity reference. + These values correspond to the values of the ResultMode enumeration. + A comma separates parameters instead of an ampersand; this avoids + laborious ampersand escaping within an XML object definition. The use of + the ampersand character is still supported if required, but you then have + to specify the ampersand character using the well known & entity + reference. - Once one has defined one's results, it is very simple to use them - within the event handlers of one's pages - (UserRegistration.apsx.cs)... + After you define your results, you can use them within the event + handlers of your pages + (UserRegistration.apsx.cs): private void SaveUser(object sender, EventArgs e) { @@ -2549,32 +2613,31 @@ protected override void OnInit(EventArgs e) base.OnInit(e); } - One could of course further refactor the above example and use - defined constants. This would be a good thing to do in the case of a - logical result name such as "home" that is likely to be referenced by many - pages. + You can further refactor the preceding example and use defined + constants, which is advisable when a logical result name such as "home" is + likely to be referenced by many pages. Registering user defined transfer modes - You may also register a custom interpreter that can parse the - short-hand string representation that creates a Result object.. The - string representation can be broken down into two parts shown - below + You can also register a custom interpreter that can parse the + shorthand string representation that creates a Result object. To do this + you should view the result mapping string representation as consisting + of two parts: <resultmode>:<textual result representation> The interface IResultFactory is responsible for - creating an IResult object from these two pieces, as shown below + creating an IResult object from these two pieces: public interface IResultFactory { IResult CreateResult( string resultMode, string resultText ); } - A ResultFactoryRegistry is used to associate a + You use a ResultFactoryRegistry to associate a given resultmode string with an IResultFactory - implementation. Here is an example + implementation: class MySpecialResultLogic : IResult { @@ -2592,8 +2655,8 @@ class MySpecialResultLogicFactory : IResultFactory ResultFactoryRegistry.RegisterResultFactory( "mySpecialMode", new MySpecialResultLogicFactory ); - You would then use the custom 'continue' mode in your page as - shown below + You then use the custom continue mode in your + page: <-- configure your Results --> <object type="mypage.aspx"> @@ -2605,26 +2668,22 @@ ResultFactoryRegistry.RegisterResultFactory( "mySpecialMode", new MySpecialResul </object> The result redirection is done as before, by calling - myPage.SetResult("continue"); - - + myPage.SetResult("cancel"); - Client-Side Scripting + Client-side scripting - ASP.NET has decent support for client-side scripting through the use - of the Page.RegisterClientScriptBlock and - Page.RegisterStartupScript methods. - - However, neither of these two methods allows you to output a - registered script markup within a <head> section - of a page, which is (in many cases) exactly what you would like to - do. + ASP.NET supports client-side scripting through the use of the + Page.RegisterClientScriptBlock and + Page.RegisterStartupScript methods. However, neither + method allows you to output a registered script markup within a + <head> section of a page, which in many cases is + exactly what you need to do. - Registering Scripts within the head HTML section + Registering scripts within the head HTML section Spring.Web adds several methods to enhance client-side scripting to the base Spring.Web.UI.Page class: @@ -2634,11 +2693,10 @@ ResultFactoryRegistry.RegisterResultFactory( "mySpecialMode", new MySpecialResul to register script blocks and script files that must be included in the <head> section of the final HTML page. - The only additional thing that is required to make this work is - that you use the <spring:Head> server-side - control to define your <head> section instead - of using the standard HTML <head> element. This - is shown below. + You must use the <spring:Head> + server-side control to define your <head> + section instead of using the standard HTML + <head> element. This is shown below. <%@ Page language="c#" Codebehind="StandardTemplate.aspx.cs" AutoEventWireup="false" Inherits="SpringAir.Web.StandardTemplate" %> @@ -2659,18 +2717,18 @@ ResultFactoryRegistry.RegisterResultFactory( "mySpecialMode", new MySpecialResul </body> </html> - The example above shows you how you would typically set-up a - <head> section within a master page template in - order to be able to change the title value and to add additional - elements to the <head> section from the child - pages using <spring:ContentPlaceholder> - controls. However, only the <spring:Head> - declaration is required in order for Spring.NET - Register* scripts to work properly. + The preceding example above shows how you typically set-up a + <head> section within a master page template to + be able to change the title value and to add additional elements to the + <head> section from the child pages using + <spring:ContentPlaceholder> controls. However, + only the <spring:Head> declaration is required + in order for Spring.NET Register* scripts to work + properly. - Adding CSS Definitions to the head Section + Adding CSS definitions to the head section In a similar fashion, you can add references to CSS files, or even specific styles, directly to the <head> HTML @@ -2685,26 +2743,25 @@ ResultFactoryRegistry.RegisterResultFactory( "mySpecialMode", new MySpecialResul - Well-Known Directories + Well-known directories - In order to make the manual inclusion of client-side scripts, CSS - files and images easier, the Spring.Web Page class - exposes several properties that help you reference such artifacts using - absolute paths. This affords web application developers a great deal of - convenience functionality straight out of the box if they stick to - common conventions such as a web application (directory) - structure.. + To make the manual inclusion of client-side scripts, CSS files and + images easier, the Spring.Web Page class exposes + several properties that help you reference such artifacts with absolute + paths. This capability gives web application developers convenience + functionality straight out of the box if they stick to common + conventions such as a web application (directory) structure. These properties are ScriptsRoot, CssRoot and ImagesRoot. They have default values of Scripts, CSS and - Images, which will work just fine if you create and - use these directories in your web application root. However, if you - prefer to place them somewhere else, you can always override default - values by injecting new values into your page definitions (you will - typically inject these values only in the base page definition, as they - are normally shared by all the pages in the application). An example of - such configuration is shown below: + Images, which work well if you create and use these + directories in your web application root. However, if you prefer to + place them somewhere else, you can always override default values by + injecting new values into your page definitions (you will typically + inject these values only in the base page definition, as they are + normally shared by all the pages in the application). An example of such + configuration is shown below: <object name="basePage" abstract="true"> <description> @@ -2721,23 +2778,23 @@ ResultFactoryRegistry.RegisterResultFactory( "mySpecialMode", new MySpecialResul - - Spring User Controls + + Spring user controls Spring provides several custom user controls that are located in the - Spring.Web.UI.Controls namespace. This section - primarily lists the controls and points to other documentation to provide - additional information. There are a few other controls not documented - here, please check the SDK docs for their descriptions. + Spring.Web.UI.Controls namespace. This section lists + the controls and points to other documentation to provide additional + information. Check the SDK docs for descriptions of controls that are not + mentioned here. - Validation Controls + Validation controls - The location in the web page where validation errors are to be - rendered can be specifies by using the + You can specify the location in the web page where validation + errors are to be rendered by using the ValidationSummary and - ValidationError controls. There are two controls - since they have different defaults for how errors are rendered. + ValidationError controls. Two controls exist because + they have different defaults for how errors are rendered. ValidationSummary is used to display potentially multiple errors identified by the validation framework. ValidationError is used to display field-level @@ -2748,19 +2805,19 @@ ResultFactoryRegistry.RegisterResultFactory( "mySpecialMode", new MySpecialResul - Databinding Controls + Databinding controls Some standard controls are not easy to use with Spring's databinding support. Examples are check boxes and ratio button groups. - In this case you should use the CheckBoxList and - RadioButtonGroup controls. Databinding itself can be - done using the CheckBoxList and + RadioButtonGroup controls. You can do databinding + itself by using the DataBindingPanel instead of the using the BindingManager API within the code behind page. - Calendar Control + Calendar control A pop-up DHTML calendar control is provided. It is a slightly modified version of the - Panel Control + Panel control You can suppress dependency injection for controls inside your - ASP.NET by using the Panel control. See the section Customizing control dependency - injection for more information. + injection .