2838 lines
134 KiB
XML
2838 lines
134 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!--
|
|
/*
|
|
* Copyright 2002-2010 the original author or authors.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
-->
|
|
<chapter version="5" xml:id="web" xmlns="http://docbook.org/ns/docbook"
|
|
xmlns:ns6="http://www.w3.org/1999/xlink"
|
|
xmlns:ns5="http://www.w3.org/1998/Math/MathML"
|
|
xmlns:ns4="http://www.w3.org/1999/xhtml"
|
|
xmlns:ns3="http://www.w3.org/2000/svg"
|
|
xmlns:ns="http://docbook.org/ns/docbook">
|
|
<title>Spring.NET Web Framework</title>
|
|
|
|
<sect1 xml:id="web-introduction">
|
|
<title>Introduction to Spring.NET Web Framework</title>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>Highlights of Spring's Web framework are:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><link linkend="web-di">Dependency Injection</link>. <!--GLOBAL: In pdf, these links are not highlighted in any way, so the reader does not know to click on them. Can we fix this?-->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.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><link linkend="web-databinding">Bidirectional data
|
|
binding</link>. 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 and user controls on post back.
|
|
This process removes large amounts of tedious, error-prone boilerplate
|
|
code.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><link linkend="web-objectscope">Web object scopes</link>. 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.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><link linkend="web-databinding">Data model management</link>.
|
|
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.)</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><link linkend="web-validation-controls">UI-agnostic validation
|
|
framework</link>. 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.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><link linkend="web-resultmapping">Externalized page navigation
|
|
through result mapping</link>. 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.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Improved <link linkend="web-localization">localization</link>
|
|
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.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>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.</para>
|
|
|
|
<note>
|
|
<para>Support for ASP.NET MVC is planned for Spring.NET 2.0.</para>
|
|
</note>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>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 <xref
|
|
linkend="springair" />).</para>
|
|
</sect1>
|
|
|
|
<sect1>
|
|
<title>Comparing Spring.NET and ASP.NET</title>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>Spring.Web also supports the application of the dependency injection
|
|
principle to one's ASP.NET <literal>Pages</literal> and
|
|
<literal>Controls</literal> 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 <link linkend="web-di">Dependency Injection
|
|
for ASP.NET Pages</link>.</para>
|
|
|
|
<para>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 <literal>DataSet</literal>. 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 <link linkend="web-databinding">Bidirectional Data Binding and
|
|
Model Management</link>.</para>
|
|
|
|
<para>The Spring.NET Web Framework also addresses concerns about the flow
|
|
of control through an application. Typical ASP.NET applications use
|
|
<literal>Response.Redirect</literal> or <literal>Server.Transfer</literal>
|
|
calls within <literal>Page</literal> logic to navigate to an appropriate
|
|
page after an action is executed. This usage often leads to hard-coded
|
|
target URLs in the <literal>Page</literal>, 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 <link linkend="web-resultmapping">Result Mapping</link>.</para>
|
|
|
|
<para>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
|
|
resource file for each ASP.NET <literal>Page</literal> and user control,
|
|
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.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
|
|
<link linkend="web-localization">Localization and Message
|
|
Sources</link>.</para>
|
|
|
|
<para>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 <link lang="" linkend="web-masterpages">Master Pages in ASP.NET
|
|
1.1</link> .</para>
|
|
|
|
<para>To implement some features, the Spring.NET team had to extend (as in
|
|
the object-oriented sense) the standard ASP.NET <literal>Page</literal>
|
|
and <literal>UserControl</literal> classes. This means that in order to
|
|
take advantage of the <emphasis>full</emphasis> 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 <literal>Spring.Web.UI.Page</literal>. 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
|
|
<emphasis>some</emphasis> 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.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="web-contexts">
|
|
<title>Automatic context loading and hierarchical contexts</title>
|
|
|
|
<sect2 xml:id="web-configuration">
|
|
<title>Configuration of a web application</title>
|
|
|
|
<para>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
|
|
<literal>Page</literal>s 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.</para>
|
|
|
|
<para>Spring.Web uses a custom <literal>PageHandlerFactory</literal>
|
|
implementation to load and configure a Spring.NET IoC container, which
|
|
is in turn used to locate an appropriate <literal>Page</literal> to
|
|
handle a HTTP request. The <literal>WebSupportModule</literal>
|
|
configures miscellaneous Spring infrastructure classes for use in a web
|
|
environment, for example setting the storage strategy of
|
|
<literal>LogicalThreadContext</literal> to be
|
|
<literal>HybridContextStorage</literal>.</para>
|
|
|
|
<para>The instantiation and configuration of the Spring.NET IoC
|
|
container by the Spring.Web infrastructure is wholly transparent to
|
|
application developers, who typically never have to explicitly
|
|
instantiate and configure an IoC container manually (by, for example,
|
|
using the <literal>new</literal> operator in C#). To effect the
|
|
transparent bootstrapping of the IoC container, you need to insert the
|
|
following configuration snippet into the root
|
|
<literal>Web.config</literal> file of every Spring.Web-enabled web
|
|
application. (You can of course change the <literal>verb</literal> and
|
|
<literal>path</literal> properties from the values that are shown.)
|
|
<note>
|
|
<para>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.</para>
|
|
</note></para>
|
|
|
|
<programlisting language="myxml"><system.web>
|
|
<httpHandlers>
|
|
<add verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web"/>
|
|
</httpHandlers>
|
|
<httpModules>
|
|
<add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
|
|
</httpModules>
|
|
...
|
|
</system.web>
|
|
|
|
</programlisting>
|
|
|
|
<para>This snippet of standard ASP.NET configuration is only required in
|
|
the <emphasis>root</emphasis> directory of each Spring.Web web
|
|
application (that is, in the <literal>Web.config</literal> file present
|
|
in the top level virtual directory of an ASP.NET web
|
|
application).</para>
|
|
|
|
<para>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 <literal>.aspx</literal>
|
|
<literal>Page</literal>, possibly injects dependencies into that
|
|
<literal>Page</literal> (as required), and then forwards the handling of
|
|
the request to the <literal>Page</literal>.</para>
|
|
|
|
<para>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 <literal>Web.config</literal> file. The final
|
|
configuration file should resemble the following; your exact
|
|
configuration may vary in particulars.</para>
|
|
|
|
<programlisting language="myxml"><?xml version="1.0" encoding="utf-8"?>
|
|
<configuration>
|
|
|
|
<configSections>
|
|
<sectionGroup name="spring">
|
|
<section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web"/>
|
|
</sectionGroup>
|
|
</configSections>
|
|
|
|
<spring>
|
|
<context>
|
|
<resource uri="~/Config/CommonObjects.xml"/>
|
|
<resource uri="~/Config/CommonPages.xml"/>
|
|
|
|
<!-- TEST CONFIGURATION -->
|
|
<!--
|
|
<resource uri="~/Config/Test/Services.xml"/>
|
|
<resource uri="~/Config/Test/Dao.xml"/>
|
|
-->
|
|
|
|
<!-- PRODUCTION CONFIGURATION -->
|
|
|
|
<resource uri="~/Config/Production/Services.xml"/>
|
|
<resource uri="~/Config/Production/Dao.xml"/>
|
|
|
|
</context>
|
|
</spring>
|
|
|
|
<system.web>
|
|
<httpHandlers>
|
|
<add verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web"/>
|
|
</httpHandlers>
|
|
<httpModules>
|
|
<add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
|
|
</httpModules>
|
|
</system.web>
|
|
|
|
</configuration>
|
|
</programlisting>
|
|
|
|
<para>Notes about the preceding configuration:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>Define a custom configuration section handler for the
|
|
<literal><context</literal><code>></code> 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 <literal>machine.config</literal> file.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>The custom configuration section handler is of the type
|
|
<literal>Spring.Context.Support.WebContextHandler</literal> which in
|
|
turn instantiates an IoC container of the type
|
|
<literal>Spring.Context.Support.WebApplicationContext</literal>.
|
|
This ensures that all features provided by Spring.Web, such as
|
|
request and session-scoped object definitions, are handled
|
|
properly.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Within the <code><spring></code> 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
|
|
<classname><context></classname> 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
|
|
<literal>WebApplicationContext</literal> is the
|
|
<literal>WebResource</literal> type.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>The object definition resources do not have to be the same
|
|
resource type (for example, all <literal>file://</literal>, all
|
|
<literal>http://</literal>, all <literal>assembly://</literal>, and
|
|
so on). This means that you can load some object definitions from
|
|
resources embedded directly within application assemblies
|
|
(<literal>assembly://</literal>) while continuing to load other
|
|
object definitions from web resources that can be more easily
|
|
edited.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<sect3>
|
|
<title>Configuration for IIS 7.0 on Windows Server 2008 and Windows
|
|
Vista</title>
|
|
|
|
<para>There is some configuration that is specific to using IIS7, the
|
|
appropriate code snippit to place in web.config shown below.</para>
|
|
|
|
<programlisting language="myxml"><system.webServer>
|
|
<validation validateIntegratedModeConfiguration="false"/>
|
|
<modules>
|
|
<add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
|
|
</modules>
|
|
<handlers>
|
|
<add name="SpringPageHandler" verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web"/>
|
|
<add name="SpringContextMonitor" verb="*" path="ContextMonitor.ashx" type="Spring.Web.Support.ContextMonitor, Spring.Web"/>
|
|
</handlers>
|
|
</system.webServer></programlisting>
|
|
</sect3>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="web-context-hierarchy">
|
|
<title>Context hierarchy</title>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>For example, a web application's root
|
|
<literal>Web.config</literal> file overrides settings from the (higher
|
|
level) <literal>machine.config</literal> file. In the same fashion,
|
|
settings specified within the <literal>web.config</literal> file within
|
|
a subdirectory of a web application will override settings from the root
|
|
<literal>Web.config</literal> and so on. You can also add seettings to
|
|
lower level <literal>Web.config</literal> files that were not previously
|
|
defined anywhere.</para>
|
|
|
|
<para>Spring.Web leverages this ASP.NET feature to provide support for a
|
|
context hierarchy. You can add new object definitions to lower level
|
|
<literal>Web.config</literal> files or override existing ones per
|
|
virtual directory.</para>
|
|
|
|
<para>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 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).</para>
|
|
|
|
<para>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 <literal>Web.config</literal>
|
|
for the lower level context instead of relying on an external resource
|
|
containing object definitions. This is easily accomplished by creating a
|
|
component <literal>Web.config</literal> similar to the following
|
|
one:</para>
|
|
|
|
<programlisting language="myxml"><?xml version="1.0" encoding="utf-8"?>
|
|
<configuration>
|
|
|
|
<configSections>
|
|
<sectionGroup name="spring">
|
|
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/>
|
|
</sectionGroup>
|
|
</configSections>
|
|
|
|
<spring>
|
|
<context type="Spring.Context.Support.WebApplicationContext, Spring.Web">
|
|
<resource uri="config://spring/objects"/>
|
|
</context>
|
|
|
|
<objects xmlns="http://www.springframework.net">
|
|
<object type="MyPage.aspx" parent="basePage">
|
|
<property name="MyRootService" ref="myServiceDefinedInRootContext"/>
|
|
<property name="MyLocalService" ref="myServiceDefinedLocally"/>
|
|
<property name="Results">
|
|
<!-- ... -->
|
|
</property>
|
|
</object>
|
|
<object id="myServiceDefinedLocally" type="MyCompany.MyProject.Services.MyServiceImpl, MyAssembly"/>
|
|
</objects>
|
|
</spring>
|
|
</configuration></programlisting>
|
|
|
|
<para>The <literal><context/></literal> element seen above
|
|
(contained within the <literal><spring/></literal> element) simply
|
|
tells the Spring.NET infrastructure code to load (its) object
|
|
definitions from the <literal>spring/objects</literal> section of the
|
|
web.config configuration file.</para>
|
|
|
|
<para>If Spring.NET is used for multiple applications on the same
|
|
server, you can avoid the need to specify the
|
|
<literal><configSections/></literal> element as shown in the
|
|
previous example, by moving the configuration handler definition for the
|
|
<literal><objects></literal> element to a higher level (root)
|
|
<literal>Web.config</literal> file, or even to the level of the
|
|
<literal>machine.config</literal> file.</para>
|
|
|
|
<para>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).</para>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="web-di">
|
|
<title>Dependency injection for ASP.NET pages</title>
|
|
|
|
<para>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
|
|
<literal>Page</literal> mechanism to satisfy the
|
|
<literal>Controller</literal> portion of the MVC architectural pattern. In
|
|
MVC-based (web) applications, the <literal>Controller</literal> is
|
|
typically a thin wrapper around one or more service objects. It is
|
|
important that service object dependencies be easily injected into
|
|
<literal>Page</literal> <literal>Controller</literal>s. Accordingly,
|
|
Spring.Web provides first class support for dependency injection in
|
|
ASP.NET <literal>Page</literal>s. Application developers can inject any
|
|
required service object dependencies (and indeed any other dependencies)
|
|
into their <literal>Page</literal>s 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.</para>
|
|
|
|
<para>After an application developer <link
|
|
linkend="web-configuration">configures</link> the Spring.NET web
|
|
application context, the developer can easily create object definitions
|
|
for the pages that compose that web application.</para>
|
|
|
|
<programlisting language="myxml"><objects xmlns="http://www.springframework.net">
|
|
|
|
<object name="basePage" abstract="true">
|
|
<property name="MasterPageFile" value="~/Web/StandardTemplate.master"/>
|
|
</object>
|
|
|
|
<object type="Login.aspx">
|
|
<property name="Authenticator" ref="authenticationService"/>
|
|
</object>
|
|
|
|
<object type="Default.aspx" parent="basePage"/>
|
|
|
|
</objects></programlisting>
|
|
|
|
<para>The preceding example contains three definitions:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>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.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>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
|
|
<literal>authenticationService</literal> is defined elsewhere).</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>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.</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>The configuration of ASP.NET pages differs from the configuration of
|
|
other .NET classes in the value passed to the <literal>type</literal>
|
|
attribute. As can be seen in the above configuration snippet, the
|
|
<literal>type</literal> name is actually the path to the
|
|
<literal>.aspx</literal> file for the <literal>Page</literal>, 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.</para>
|
|
|
|
<para>In the case of the above example, those definitions are in the root
|
|
context ,so <literal>Login.aspx</literal> and
|
|
<literal>Default.aspx</literal> 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.</para>
|
|
|
|
<para>The definitions for the <literal>Login</literal> and
|
|
<literal>Default</literal> pages do not specify either of the
|
|
<literal>id</literal> and <literal>name</literal> attributes, in marked
|
|
contrast to typical object definitions in Spring.NET, where the
|
|
<literal>id</literal> or <literal>name</literal> attributes are usually
|
|
mandatory (although not always, as in the case of inner object
|
|
definitions). In the case of Spring.Web manged <literal>Page</literal>
|
|
instances, one typically wants to use the name of the
|
|
<literal>.aspx</literal> file name as the identifier. If an
|
|
<literal>id</literal> is not specified, the Spring.Web infrastructure will
|
|
simply use the name of the <literal>.aspx</literal> file as the object
|
|
identifier (minus any leading path information, and minus the file
|
|
extension too).</para>
|
|
|
|
<para>Nothing prevents an application developer from specifying an
|
|
<literal>id</literal> or <literal>name</literal> 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 <literal>name</literal> attribute instead of
|
|
the <literal>id</literal> attribute on the abstract object
|
|
definition.</para>
|
|
|
|
<sect2 xml:id="web-di-controls">
|
|
<title>Injecting dependencies into controls</title>
|
|
|
|
<para>Spring.Web also allows application developers to inject
|
|
dependencies into controls (both user controls and standard controls)
|
|
that are contained within a page. You can accomplish this globally for
|
|
all controls of a particular <literal>Type</literal> by using the
|
|
location of the <literal>.ascx</literal> as the object type identifier.
|
|
This process is similar to injecting into <literal>.aspx
|
|
</literal>pages, shown above.</para>
|
|
|
|
<programlisting language="myxml"><object type="~/controls/MyControl.ascx" abstract="true">
|
|
<!-- inject dependencies here... -->
|
|
</object></programlisting>
|
|
|
|
<para><note>
|
|
<para>In either case, be sure to mark the object definition as
|
|
<literal>abstract</literal> (by adding
|
|
<literal>abstract="true"</literal> to the attribute list of the
|
|
<literal><object/></literal> element).</para>
|
|
</note></para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="web-di-modules">
|
|
<title>Injecting dependencies into custom HTTP modules</title>
|
|
|
|
<para>You can inject dependencies into custom HTTP modules by using the
|
|
class
|
|
<literal>Spring.Context.Support.HttpApplicationConfigurer</literal>. You
|
|
register your custom HTTP module as you would normally; for example, a
|
|
module of the type <literal>HtmlCommentAppenderModule</literal>, taken
|
|
from the Web Quick Start, appends additional comments into the http
|
|
response. It is registered as follows:</para>
|
|
|
|
<programlisting language="myxml"><httpModules>
|
|
<add name="HtmlCommentAppender" type="HtmlCommentAppenderModule"/>
|
|
</httpModules></programlisting>
|
|
|
|
<para>To configure this module, you use naming conventions to identify
|
|
the module name with configuration instructions in the Spring
|
|
configuration file. The <literal>ModuleTemplates</literal> property of
|
|
<classname>HttpApplicationConfigurer</classname> is a dictionary that
|
|
takes as a key the name of the HTTP module, in this case
|
|
<classname>HtmlCommentAppender</classname>, 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
|
|
<classname>HtmlCommentAppender's</classname>
|
|
<property>AppendText</property> property.</para>
|
|
|
|
<programlisting language="myxml"><object name="HttpApplicationConfigurer" type="Spring.Context.Support.HttpApplicationConfigurer, Spring.Web">
|
|
<property name="ModuleTemplates">
|
|
<dictionary>
|
|
<entry key="HtmlCommentAppender"> <!-- this name must match the module name -->
|
|
<object>
|
|
<!-- select "view source" in your browser on any page to see the appended html comment -->
|
|
<property name="AppendText" value="My configured comment!" />
|
|
</object>
|
|
</entry>
|
|
</dictionary>
|
|
</property>
|
|
</object></programlisting>
|
|
|
|
<para>You can see this example in action in the Web Quick Start.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="web-di-handler">
|
|
<title>Injecting dependencies into HTTP handlers and handler
|
|
factories</title>
|
|
|
|
<para>Performing dependency injection on instances of
|
|
<literal>IHttpHandlers</literal> and <literal>IHttpHandlerFactory
|
|
</literal>allows for a fully Spring-managed
|
|
<literal><httpHandlers></literal> configuration section. To
|
|
perform dependency injection onan <literal>IHttpHandler</literal> or
|
|
<literal>IHttpHandlerFactory,</literal> register Spring's
|
|
<literal>MappingHandlerFactory</literal> with a specific path or
|
|
wildcard string (that is, *.aspx) using the standard configuration of an
|
|
<literal><httpHandler></literal> in web.config. For
|
|
example:</para>
|
|
|
|
<programlisting language="myxml"><system.web>
|
|
<httpHandlers>
|
|
<!--
|
|
the lines below map *any* request ending with *.ashx or *.whatever
|
|
to the global(!) MappingHandlerFactory. Further "specialication"
|
|
of which handler to map to is done within MappingHandlerFactory's configuration -
|
|
use MappingHandlerFactoryConfigurer for this (see below)
|
|
-->
|
|
<add verb="*" path="*.ashx" type="Spring.Web.Support.MappingHandlerFactory, Spring.Web" validate="true"/>
|
|
<add verb="*" path="*.whatever" type="Spring.Web.Support.MappingHandlerFactory, Spring.Web" validate="false"/>
|
|
</httpHandlers>
|
|
</system.web></programlisting>
|
|
|
|
<para>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
|
|
<classname>MappingHandlerFactoryConfigurer</classname>. 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
|
|
<classname>IHttpHandler</classname> or
|
|
<classname>IHttpHandlerFactory</classname> . The Spring managed instance
|
|
is configured via dependency injection using the standard
|
|
<object/> XML configuraiton schema.</para>
|
|
|
|
<para>The configuration of
|
|
<classname>MappingHandlerFactoryConfigurer</classname> is shown:</para>
|
|
|
|
<programlisting language="myxml"><objects xmlns="http://www.springframework.net">
|
|
|
|
<!-- configures the global GenericHandlerFactory instance -->
|
|
<object name="mappingHandlerFactoryConfigurer" type="Spring.Web.Support.MappingHandlerFactoryConfigurer, Spring.Web">
|
|
<property name="HandlerMap">
|
|
<dictionary>
|
|
<!-- map any request ending with *.whatever to NoOpHandler -->
|
|
<entry key="\.whatever$" value="myCustomHandler" />
|
|
<entry key="\.ashx$" value="standardHandlerFactory" />
|
|
</dictionary>
|
|
</property>
|
|
</object>
|
|
|
|
<object name="standardHandlerFactory" type="Spring.Web.Support.DefaultHandlerFactory, Spring.Web" />
|
|
|
|
<!-- defines a standard singleton that will handle *.whatever requests -->
|
|
<object name="myCustomHandler" type="MyCustomHttpHandler, App_Code">
|
|
<property name="MessageText" value="This text is injected via Spring" />
|
|
</object>
|
|
|
|
<!--
|
|
used for configuring ~/DemoHandler.ashx custom handler
|
|
note, that this is an abstract definition because 'type' is not specified
|
|
-->
|
|
<object name="DemoHandler.ashx">
|
|
<property name="OutputText">
|
|
<value>This text is injected via Spring</value>
|
|
</property>
|
|
</object>
|
|
</objects></programlisting>
|
|
|
|
<para>Spring's <literal>DefaultHandlerFactory</literal> uses the .NET
|
|
class <classname>System.Web.UI.SimpleHandlerFactory</classname> 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 <literal>DemoHandler.ashx</literal> is an example
|
|
of this approach. You can also configure standard classes that implement
|
|
the <classname>IHttpHandler </classname>interface as demonstrated in the
|
|
example above for the class
|
|
<literal>MyCustomHttpHandler</literal>.</para>
|
|
|
|
<para>Refer to the Web Quick Start application too see this in
|
|
action.</para>
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>Injecting dependencies in custom ASP.NET providers</title>
|
|
|
|
<para>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.</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>MembershipProviderAdapter</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>ProfileProviderAdapter</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>RoleProviderAdapter</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>SiteMapProviderAdapter</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>Here is an example of how to register the adapter for membership
|
|
providers.</para>
|
|
|
|
<programlisting language="myxml"> <membership defaultProvider="mySqlMembershipProvider">
|
|
<providers>
|
|
<clear/>
|
|
<add connectionStringName="" name="<emphasis role="bold">mySqlMembershipProvider</emphasis>" type="Spring.Web.Providers.MembershipProviderAdapter, Spring.Web"/>
|
|
</providers>
|
|
</membership></programlisting>
|
|
|
|
<para>The name of the provider must match the name of the object in the
|
|
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:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>ConfigurableActiveDirectoryMembershipProvider</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>ConfigurableSqlMembershipProvider</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>ConfigurableSqlProfileProvider</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>ConfigurableSqlRoleProvider</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>ConfigurableXmlSiteMapProvider</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>This example configuration taken from the Web Quick Start
|
|
application sets the description property and connection string.</para>
|
|
|
|
<programlisting language="myxml"> <object id="<emphasis
|
|
role="bold">mySqlMembershipProvider</emphasis>" type="Spring.Web.Providers.ConfigurableSqlMembershipProvider">
|
|
<property name="connectionStringName" value="MyLocalSQLServer" />
|
|
<property name="parameters">
|
|
<name-values>
|
|
<add key="description" value="membershipprovider description" />
|
|
</name-values>
|
|
</property>
|
|
</object></programlisting>
|
|
|
|
<para>Your own custom providers of course will contain additional
|
|
configuration specific to your implementation.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="web-controlling-di">
|
|
<title>Customizing control dependency injection</title>
|
|
|
|
<para>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
|
|
<classname>ISupportsWebDependencyInjection</classname> and explicitly
|
|
ask Spring to inject dependencies on a particular contorol. These
|
|
approaches are shown below</para>
|
|
|
|
<programlisting language="csharp">[C#]
|
|
class MyControl : Control, ISupportsWebDependencyInjection
|
|
{
|
|
private IApplicationContext _defaultApplicationContext;
|
|
|
|
public IApplicationContext DefaultApplicationContext
|
|
{
|
|
get { return _defaultApplicationContext; }
|
|
set { _defaultApplicationContext = value; }
|
|
}
|
|
|
|
override protected AddedControl( Control control, int index )
|
|
{
|
|
// handle DI for children ourselves -
|
|
// defaults to a call to InjectDependenciesRecursive
|
|
WebUtils.InjectDependenciesRecursive( _defaultApplicationContext, control );
|
|
base.AddedControl( control, index );
|
|
}
|
|
}</programlisting>
|
|
|
|
<para>A Spring server control, <classname>Panel</classname>, provides an
|
|
easier way to turn off dependency injection for parts of your
|
|
page:</para>
|
|
|
|
<programlisting language="myxml"><spring:Panel runat="server"
|
|
suppressDependencyInjection="true"
|
|
renderContainerTag="false">
|
|
|
|
.. put your heavy controls here - they won't be touched by DI
|
|
|
|
</spring:Panel></programlisting>
|
|
|
|
<para>By wrapping the performance-sensitive parts of your page within
|
|
this panel, you can easily turn off DI by setting the attribute
|
|
<classname>suppressDependencyInjection</classname> to true. By default
|
|
<classname><spring:Panel/></classname> will not render a container
|
|
tag (<div>, <span>, and so on). You can modify this behavior
|
|
by setting the <classname>attribute renderContainerTag</classname>
|
|
accordingly.</para>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="web-objectscope">
|
|
<title>Web object scopes</title>
|
|
|
|
<para>Spring.NET web applications support an additional attribute within
|
|
object definition elements that allows you to control the scope of an
|
|
object: <programlisting language="myxml"><object id="myObject" type="MyType, MyAssembly" scope="application | session | request"/></programlisting>Possible
|
|
values for the scope attribute are <classname>application</classname>,
|
|
<classname>session,</classname> and <code>request</code>. 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.</para>
|
|
|
|
<para>Request scope creates one instance per HTTP request. Unlike calls to
|
|
prototype objects, calls to
|
|
<literal>IApplicationContext.GetObject</literal> 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.</para>
|
|
|
|
<para>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.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="web-masterpages">
|
|
<title>Support for ASP.NET 1.1 master pages in Spring.Web</title>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>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
|
|
(<literal>MasterLayout.ascx</literal>) could look like this:</para>
|
|
|
|
<programlisting language="myxml"><%@ Control language="c#" Codebehind="MasterLayout.ascx.cs" AutoEventWireup="false" Inherits="MyApp.Web.UI.MasterLyout" %>
|
|
<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %>
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
|
|
<html>
|
|
<head>
|
|
<title>Master Page</title>
|
|
<link rel="stylesheet" type="text/css" href="<%= Context.Request.ApplicationPath %>/css/styles.css">
|
|
<spring:ContentPlaceHolder id="head" runat="server"/>
|
|
</head>
|
|
<body>
|
|
<form runat="server">
|
|
<table cellPadding="3" width="100%" border="1">
|
|
<tr>
|
|
<td colspan="2">
|
|
<spring:ContentPlaceHolder id="title" runat="server">
|
|
<!-- default title content -->
|
|
</spring:ContentPlaceHolder>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<spring:ContentPlaceHolder id="leftSidebar" runat="server">
|
|
<!-- default left side content -->
|
|
</spring:ContentPlaceHolder>
|
|
</td>
|
|
<td>
|
|
<spring:ContentPlaceHolder id="main" runat="server">
|
|
<!-- default main area content -->
|
|
</spring:ContentPlaceHolder>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</form>
|
|
</body>
|
|
</html></programlisting>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>A page that uses this master page (Child.aspx) might look like
|
|
this:</para>
|
|
|
|
<programlisting language="myxml"><%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %>
|
|
<%@ Page language="c#" Codebehind="Child.aspx.cs" AutoEventWireup="false" Inherits="ArtFair.Web.UI.Forms.Child" %>
|
|
<html>
|
|
<body>
|
|
|
|
<spring:Content id="leftSidebarContent" contentPlaceholderId="leftSidebar" runat="server">
|
|
<!-- left sidebar content -->
|
|
</spring:Content>
|
|
|
|
<spring:Content id="mainContent" contentPlaceholderId="main" runat="server">
|
|
<!-- main area content -->
|
|
</spring:Content>
|
|
|
|
</body>
|
|
</html></programlisting>
|
|
|
|
<para>The <literal><spring:Content/></literal> control in the
|
|
example uses the <literal>contentPlaceholderId</literal> 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 placeholders, the content elements are
|
|
defined by the default content supplied in the master page.</para>
|
|
|
|
<para>Both the <literal>ContentPlaceHolder</literal> and
|
|
<literal>Content</literal> controls can contain any valid ASP.NET markup:
|
|
HTML, standard ASP.NET controls, user controls, and so on.</para>
|
|
|
|
<tip>
|
|
<para>Technically, the <literal><html></literal> and
|
|
<literal><body></literal> tags from the previous example are not
|
|
strictly necessary because they are already defined in the master page.
|
|
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.</para>
|
|
</tip>
|
|
|
|
<sect2 xml:id="web-childpage-linking">
|
|
<title>Linking child pages to their master page file</title>
|
|
|
|
<para>The <literal>Spring.Web.UI.Page</literal> class exposes a property
|
|
called <literal>MasterPageFile</literal>, which you can use to specify
|
|
the master page.</para>
|
|
|
|
<para>The recommended way to do this is by leveraging the Spring.NET IoC
|
|
container and creating definitions similar to the following:</para>
|
|
|
|
<programlisting language="myxml"><?xml version="1.0" encoding="utf-8" ?>
|
|
<objects xmlns="http://www.springframework.net">
|
|
|
|
<object name="basePage" abstract="true">
|
|
<property name="MasterPageFile" value="~/MasterLayout.ascx"/>
|
|
</object>
|
|
|
|
<object type="Child.aspx" parent="basePage">
|
|
<!-- inject other objects that page needs -->
|
|
</object>
|
|
|
|
</objects></programlisting>
|
|
|
|
<para>This approach allows application developers to change the master
|
|
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 <literal>MasterPageFile</literal> property directly.</para>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="web-databinding">
|
|
<title>Bidirectional data binding and data model management</title>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>To take advantage of the bidirectional data binding and model
|
|
management support provided by Spring.Web, you <emphasis>will</emphasis>
|
|
have to couple your presentation layer to Spring.Web; this is because
|
|
features <emphasis>require </emphasis>you to extend a
|
|
<literal>Spring.Web.UI.Page</literal> instead of the usual
|
|
<literal>System.Web.UI.Page</literal> class.</para>
|
|
|
|
<para>Spring.Web data binding is very easy to use. Simply override the
|
|
protected <literal>InitializeDataBindings</literal> method and configure
|
|
data binding rules for the page. You also need to override three model
|
|
management methods: <literal>InitializeModel</literal>,
|
|
<literal>LoadModel</literal> and <literal>SaveModel</literal>. This
|
|
process is illustrated by an example from the SpringAir reference
|
|
application. First, take a look at the page markup:<programlisting
|
|
language="myxml"><%@ Page Language="c#" Inherits="TripForm" CodeFile="TripForm.aspx.cs" %>
|
|
|
|
<asp:Content ID="body" ContentPlaceHolderID="body" runat="server">
|
|
<div style="text-align: center">
|
|
<h4><asp:Label ID="caption" runat="server"></asp:Label></h4>
|
|
<table>
|
|
<tr class="formLabel">
|
|
<td>&nbsp;</td>
|
|
<td colspan="3">
|
|
<spring:RadioButtonGroup ID="tripMode" runat="server">
|
|
<asp:RadioButton ID="OneWay" runat="server" />
|
|
<asp:RadioButton ID="RoundTrip" runat="server" />
|
|
</spring:RadioButtonGroup>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="formLabel" align="right">
|
|
<asp:Label ID="leavingFrom" runat="server" /></td>
|
|
<td nowrap="nowrap">
|
|
<asp:DropDownList ID="leavingFromAirportCode" runat="server" />
|
|
</td>
|
|
<td class="formLabel" align="right">
|
|
<asp:Label ID="goingTo" runat="server" /></td>
|
|
<td nowrap="nowrap">
|
|
<asp:DropDownList ID="goingToAirportCode" runat="server" />
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="formLabel" align="right">
|
|
<asp:Label ID="leavingOn" runat="server" /></td>
|
|
<td nowrap="nowrap">
|
|
<spring:Calendar ID="departureDate" runat="server" Width="75px" AllowEditing="true" Skin="system" />
|
|
</td>
|
|
<td class="formLabel" align="right">
|
|
<asp:Label ID="returningOn" runat="server" /></td>
|
|
<td nowrap="nowrap">
|
|
<div id="returningOnCalendar">
|
|
<spring:Calendar ID="returnDate" runat="server" Width="75px" AllowEditing="true" Skin="system" />
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td class="buttonBar" colspan="4">
|
|
<br/>
|
|
<asp:Button ID="findFlights" runat="server"/></td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
|
|
</asp:Content>
|
|
|
|
</programlisting>Ignore for the moment the fact that none of the label
|
|
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:
|
|
<literal>tripMode</literal> radio button group,
|
|
<literal>leavingFromAirportCode</literal> and
|
|
<literal>goingToAirportCode</literal> dropdown lists, as well as two
|
|
Spring.NET Calendar controls, <literal>departureDate</literal> and
|
|
<literal>returnDate</literal>.</para>
|
|
|
|
<para>Take a look at the model to which you bind the form:<programlisting
|
|
language="csharp">namespace SpringAir.Domain
|
|
{
|
|
[Serializable]
|
|
public class Trip
|
|
{
|
|
// fields
|
|
private TripMode mode;
|
|
private TripPoint startingFrom;
|
|
private TripPoint returningFrom;
|
|
|
|
// constructors
|
|
public Trip()
|
|
{
|
|
this.mode = TripMode.RoundTrip;
|
|
this.startingFrom = new TripPoint();
|
|
this.returningFrom = new TripPoint();
|
|
}
|
|
|
|
public Trip(TripMode mode, TripPoint startingFrom, TripPoint returningFrom)
|
|
{
|
|
this.mode = mode;
|
|
this.startingFrom = startingFrom;
|
|
this.returningFrom = returningFrom;
|
|
}
|
|
|
|
// properties
|
|
public TripMode Mode
|
|
{
|
|
get { return this.mode; }
|
|
set { this.mode = value; }
|
|
}
|
|
|
|
public TripPoint StartingFrom
|
|
{
|
|
get { return this.startingFrom; }
|
|
set { this.startingFrom = value; }
|
|
}
|
|
|
|
public TripPoint ReturningFrom
|
|
{
|
|
get { return this.returningFrom; }
|
|
set { this.returningFrom = value; }
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public class TripPoint
|
|
{
|
|
// fields
|
|
private string airportCode;
|
|
private DateTime date;
|
|
|
|
// constructors
|
|
public TripPoint()
|
|
{}
|
|
|
|
public TripPoint(string airportCode, DateTime date)
|
|
{
|
|
this.airportCode = airportCode;
|
|
this.date = date;
|
|
}
|
|
|
|
// properties
|
|
public string AirportCode
|
|
{
|
|
get { return this.airportCode; }
|
|
set { this.airportCode = value; }
|
|
}
|
|
|
|
public DateTime Date
|
|
{
|
|
get { return this.date; }
|
|
set { this.date = value; }
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public enum TripMode
|
|
{
|
|
OneWay,
|
|
RoundTrip
|
|
}
|
|
}</programlisting>As you can see, <literal>Trip</literal> class uses the
|
|
<literal>TripPoint</literal> class to represent departure and return,
|
|
which are exposed as <literal>StartingFrom</literal> and
|
|
<literal>ReturningFrom</literal> properties. It also uses
|
|
<literal>TripMode</literal> enumeration to specify whether the trip is one
|
|
way or return trip, which is exposed as <literal>Mode</literal>
|
|
property.</para>
|
|
|
|
<para>Here is the code-behind class that ties everything
|
|
together:<programlisting language="csharp">public class TripForm : Spring.Web.UI.Page
|
|
{
|
|
// model
|
|
private Trip trip;
|
|
public Trip Trip
|
|
{
|
|
get { return trip; }
|
|
set { trip = value; }
|
|
}
|
|
|
|
// service dependency, injected by Spring IoC container
|
|
private IBookingAgent bookingAgent;
|
|
public IBookingAgent BookingAgent
|
|
{
|
|
set { bookingAgent = value; }
|
|
}
|
|
|
|
// model management methods
|
|
protected override void InitializeModel()
|
|
{
|
|
trip = new Trip();
|
|
trip.Mode = TripMode.RoundTrip;
|
|
trip.StartingFrom.Date = DateTime.Today;
|
|
trip.ReturningFrom.Date = DateTime.Today.AddDays(1);
|
|
}
|
|
|
|
protected override void LoadModel(object savedModel)
|
|
{
|
|
trip = (Trip) savedModel;
|
|
}
|
|
|
|
protected override object SaveModel()
|
|
{
|
|
return trip;
|
|
}
|
|
|
|
// data binding rules
|
|
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");
|
|
}
|
|
|
|
// event handler for findFlights button, uses injected 'bookingAgent'
|
|
// service and model 'trip' object to find flights
|
|
private void SearchForFlights(object sender, EventArgs e)
|
|
{
|
|
FlightSuggestions suggestions = bookingAgent.SuggestFlights(trip);
|
|
if (suggestions.HasOutboundFlights)
|
|
{
|
|
// redirect to SuggestedFlights page
|
|
}
|
|
}
|
|
}</programlisting>Note the following about the three preceding pieces of
|
|
code:<orderedlist>
|
|
<listitem>
|
|
<para>When the page is initially loaded (<literal>IsPostback ==
|
|
false</literal>), the <literal>InitializeModel</literal>() method is
|
|
called, which initializes the trip object by creating a new instance
|
|
and setting its properties to desired values. Right before the page
|
|
is rendered, <literal>the SaveModel</literal>() method is invoked,
|
|
and the value it returns is stored within the HTTP session. On each
|
|
postback, <literal>the LoadModel()</literal> method is called, and
|
|
the value returned by the previous call to
|
|
<literal>SaveModel</literal> is passed to <code>SaveModel</code> as
|
|
an argument.</para>
|
|
|
|
<para>In this particular case the implementation is very simple
|
|
because our whole model is just the <literal>trip</literal> object.
|
|
As such, <literal>SaveModel</literal>() simply returns the
|
|
<literal>trip</literal> object, and <literal>LoadModel()</literal>
|
|
casts the <literal>SaveModel</literal>() argument to
|
|
<literal>Trip</literal> and assigns it to the
|
|
<literal>trip</literal> 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.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>InitializeDataBindings</literal> method defines the
|
|
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
|
|
<literal>AddBinding</literal> method on the
|
|
<literal>BindingManager</literal> exposed by the page. <literal>The
|
|
AddBinding</literal> method is heavily overloaded and it allows you
|
|
to specify a <emphasis>binding direction</emphasis> and a
|
|
<emphasis>formatter</emphasis> to use in addition to the
|
|
<emphasis>source and target binding expressions</emphasis> that are
|
|
used above. These optional parameters are discussed later in this
|
|
chapter. For now, focus on the source and target expressions.</para>
|
|
|
|
<para>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 <literal>InitializeDataBindings</literal> method is
|
|
executed only once <emphasis>per page type</emphasis>. 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.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Notice that the <code>SearchForFlights</code> 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.</para>
|
|
|
|
<para><note>
|
|
<para>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.</para>
|
|
</note></para>
|
|
</listitem>
|
|
</orderedlist></para>
|
|
|
|
<sect2 xml:id="web-binding-detail">
|
|
<title>Data binding under the hood</title>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>The Spring.NET data binding framework revolves around two main
|
|
interfaces: <literal>IBinding</literal> and
|
|
<literal>IBindingContainer</literal>. The <literal>IBinding</literal>
|
|
interface is definitely the more important one of the two, as it has to
|
|
be implemented by all binding types. This interface defines several
|
|
methods, with some of them being overloaded for
|
|
convenience:<programlisting language="csharp">public interface IBinding
|
|
{
|
|
void BindSourceToTarget(object source, object target, ValidationErrors validationErrors);
|
|
|
|
void BindSourceToTarget(object source, object target, ValidationErrors validationErrors,
|
|
IDictionary variables);
|
|
|
|
void BindTargetToSource(object source, object target, ValidationErrors validationErrors);
|
|
|
|
void BindTargetToSource(object source, object target, ValidationErrors validationErrors,
|
|
IDictionary variables);
|
|
|
|
void SetErrorMessage(string messageId, params string[] errorProviders);
|
|
}</programlisting>The <literal>BindSourceToTarget</literal> method is used to
|
|
extract and copy bound values from the source object to the target
|
|
object, and <literal>BindTargetToSource</literal> 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.</para>
|
|
|
|
<para>The <literal>ValidationErrors</literal> 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 <code>ValidationErrors</code> 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.</para>
|
|
|
|
<para>The last method in the <literal>IBinding</literal> interface,
|
|
<literal>SetErrorMessage</literal>, 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
|
|
<literal>SetErrorMessage</literal> usage in a later section.</para>
|
|
|
|
<para>The <literal>IBindingContainer</literal> interface extends the
|
|
<literal>IBinding</literal> interface and adds the following
|
|
members:<programlisting language="csharp">public interface IBindingContainer : IBinding
|
|
{
|
|
bool HasBindings { get; }
|
|
|
|
IBinding AddBinding(IBinding binding);
|
|
IBinding AddBinding(string sourceExpression, string targetExpression);
|
|
IBinding AddBinding(string sourceExpression, string targetExpression, BindingDirection direction);
|
|
IBinding AddBinding(string sourceExpression, string targetExpression, IFormatter formatter);
|
|
IBinding AddBinding(string sourceExpression, string targetExpression, BindingDirection direction,
|
|
IFormatter formatter);
|
|
}</programlisting>The <code>IBindingContainer</code> interface has several
|
|
overloaded <literal>AddBinding</literal> methods.
|
|
<literal>AddBinding(IBinding binding)</literal> 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 implementation of the
|
|
<interfacename>IBinding</interfacename> interface,
|
|
<literal>SimpleExpressionBinding</literal>. The
|
|
<literal>SimpleExpressionBinding</literal> was used under the covers in
|
|
the example at the beginning of this section to bind our web form to a
|
|
<literal>Trip</literal> instance when calling methods on the property
|
|
<property>BindingManager</property>. Note that the
|
|
<property>BindingManager</property> property is of the type
|
|
<interfacename>IBindingContainer</interfacename>.
|
|
<classname>SimpleExpressionBinding</classname> uses Spring.NET
|
|
Expression Language (SpEL) to extract and to set values within source
|
|
and target objects. </para>
|
|
|
|
<para>In the TripForm example, the configuration of the BindingManager
|
|
shows the basic usage of how SpEL can be used to specify a
|
|
<literal>sourceExpression</literal> and
|
|
<literal>targetExpression</literal> arguments. This code section is
|
|
repeated below</para>
|
|
|
|
<programlisting> 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");
|
|
}</programlisting>
|
|
|
|
<para>In this case, the first argument is a
|
|
<literal>sourceExpression</literal> evaluated in the context of the page
|
|
itself. The <literal>sourceExpression</literal><literal>
|
|
'tripMode.Value' </literal> represents the value in the HTML control and
|
|
the<literal> targetExpression "Trip.Mode"</literal> represents the value
|
|
it will be mapped onto whent the page is rendered. When the post-back
|
|
happens values from in <literal>"Trip.Mode"</literal> get placed back
|
|
into the HTML control <literal>"tripMode.Value"</literal>. This is a
|
|
common case in which bi-directional data mapping is symmetric in terms
|
|
of the <literal>sourceExpression</literal> and
|
|
<literal>targetExpression</literal> for both the initial rendering of
|
|
the page and when the post-back occurs. There other overloaded methods
|
|
that take <literal>BindingDirection</literal> and
|
|
<literal>IFormatter</literal> arguments are discussed in the next
|
|
section.</para>
|
|
|
|
<sect3 xml:id="web-binding-direction">
|
|
<title>Binding direction</title>
|
|
|
|
<para>The <code>direction</code> argument determines whether the
|
|
binding is bidirectional or unidirectional. By default, all data
|
|
bindings are bidirectional unless the direction argument is set to
|
|
either <literal>BindingDirection.SourceToTarget</literal> or
|
|
<literal>BindingDirection.TargetToSource</literal>. If one of these
|
|
values is specified, binding is evaluated only when the appropriate
|
|
<literal>Bind</literal><emphasis><code>Direction</code></emphasis>
|
|
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.</para>
|
|
|
|
<para>However, unidirectional data bindings are also useful when your
|
|
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 <literal>Airport</literal> class
|
|
and modify our <literal>TripPoint</literal> class as
|
|
follows:<programlisting language="csharp">namespace SpringAir.Domain
|
|
{
|
|
[Serializable]
|
|
public class TripPoint
|
|
{
|
|
// fields
|
|
private Airport airport;
|
|
private DateTime date;
|
|
|
|
// constructors
|
|
public TripPoint()
|
|
{}
|
|
|
|
public TripPoint(Airport airport, DateTime date)
|
|
{
|
|
this.airport = airport;
|
|
this.date = date;
|
|
}
|
|
|
|
// properties
|
|
public Airport Airport
|
|
{
|
|
get { return this.airport; }
|
|
set { this.airport = value; }
|
|
}
|
|
|
|
public DateTime Date
|
|
{
|
|
get { return this.date; }
|
|
set { this.date = value; }
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
public class Airport
|
|
{
|
|
// fields
|
|
private string code;
|
|
private string name;
|
|
|
|
// properties
|
|
public string Code
|
|
{
|
|
get { return this.code; }
|
|
set { this.code = value; }
|
|
}
|
|
|
|
public string Name
|
|
{
|
|
get { return this.name; }
|
|
set { this.name = value; }
|
|
}
|
|
}
|
|
}</programlisting>Instead of the string property <code>AirportCode</code>, our
|
|
<code>TripPoint</code> class now exposes an <code>Airport</code>
|
|
property of type <code>Airport</code>, 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 <code>TripPoint.AirportCode</code> 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.</para>
|
|
|
|
<para>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.
|
|
<programlisting language="csharp">protected override void InitializeDataBindings()
|
|
{
|
|
BindingManager.AddBinding("leavingFromAirportCode.SelectedValue", "Trip.StartingFrom.Airport.Code", BindingDirection.TargetToSource);
|
|
BindingManager.AddBinding("goingToAirportCode.SelectedValue", "Trip.ReturningFrom.Airport.Code", BindingDirection.TargetToSource);
|
|
...
|
|
}</programlisting>You extract the airport code value from the
|
|
<literal>Trip.StartingFrom.Airport.Code</literal> instead of
|
|
<literal>Trip.StartingFrom.AirportCode</literal> 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 <literal>Airport</literal> class based on the airport
|
|
code and set the <literal>TripPoint.Airport</literal> property to it.
|
|
Fortunately, Spring.NET data binding makes this simple, especially
|
|
because you already have <literal>airportDao</literal> object defined
|
|
in the Spring context (see SpringAir Spring context configuration file
|
|
for details.). The AirportDao has a <literal>GetAirport(string
|
|
airportCode)</literal> 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. </para>
|
|
|
|
<para>Our complete set of bindings for these two drop-down lists will
|
|
then look like this:<programlisting language="csharp">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);
|
|
|
|
BindingManager.AddBinding("@(airportDao).GetAirport(goingToAirportCode.SelectedValue)", "Trip.ReturningFrom.Airport", BindingDirection.SourceToTarget);
|
|
BindingManager.AddBinding("goingToAirportCode.SelectedValue", "Trip.ReturningFrom.Airport.Code", BindingDirection.TargetToSource);
|
|
...
|
|
}</programlisting>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.</para>
|
|
</sect3>
|
|
|
|
<sect3 xml:id="web-binding-formatters">
|
|
<title>formatter argument</title>
|
|
|
|
<para>The last overloaded methods of
|
|
<interfacename>IBindingContainer</interfacename> we need to discuss
|
|
are those that take a <literal>IFormatter</literal> argument. is an
|
|
argument to the <literal>AddBinding</literal> 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.</para>
|
|
|
|
<para>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:<programlisting
|
|
language="csharp">public interface IFormatter
|
|
{
|
|
string Format(object value);
|
|
object Parse(string value);
|
|
}</programlisting></para>
|
|
|
|
<para>Standard formatters provided with Spring.NET are:
|
|
<literal>CurrencyFormatter</literal>,
|
|
<literal>DateTimeFormatter</literal>,
|
|
<literal>FloatFormatter</literal>,
|
|
<literal>IntegerFormatter</literal>,
|
|
<literal>NumberFormatter</literal> and
|
|
<literal>PercentFormatter</literal>, which are sufficient for most
|
|
usage scenarios.</para>
|
|
</sect3>
|
|
|
|
<sect3 xml:id="web-id-typeconversion">
|
|
<title>Type conversion</title>
|
|
|
|
<para>Because the data binding framework uses the same expression
|
|
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 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.</para>
|
|
</sect3>
|
|
|
|
<sect3 xml:id="web-binding-events">
|
|
<title>Data binding events</title>
|
|
|
|
<para>Spring.Web's base <literal>Page</literal> class adds two events
|
|
to the standard .NET page lifecycle: <literal>DataBound</literal> and
|
|
<literal>DataUnbound</literal>.</para>
|
|
|
|
<para>You can register for an <literal>DataUnbound</literal> 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 <literal>Load</literal> event and only on
|
|
postbacks, because it not make sense to update the data model with the
|
|
controls' initial values.</para>
|
|
|
|
<para>The <literal>DataBound</literal> event is fired after controls
|
|
are updated with values from the data model. This event occurs right
|
|
before the <literal>PreRender</literal> event.</para>
|
|
|
|
<para>The fact that the data model is updated immediately after the
|
|
<literal>Load</literal> event and that controls are updated right
|
|
before the <literal>PreRender</literal> event means that your event
|
|
handlers can work with a correctly updated data model, as they execute
|
|
after the <literal>Load</literal> 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.</para>
|
|
</sect3>
|
|
|
|
<sect3 xml:id="web-binding-errors">
|
|
<title>Rendering binding errors</title>
|
|
|
|
<para>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:</para>
|
|
|
|
<programlisting language="csharp">[Default.aspx.cs]
|
|
|
|
protected override void InitializeDataBindings()
|
|
{
|
|
// collect txtId.Text binding errors in "id.errors" collection
|
|
BindingManager.AddBinding("txtId.Text", "Employee.Id").SetErrorMessage("ID has to be an integer", "id.errors");
|
|
...
|
|
|
|
[Default.aspx]
|
|
...
|
|
<asp:TextBox ID="txtId" runat="server" />
|
|
<!-- output validation errors from "id.errors" collection -->
|
|
<spring:ValidationError Provider="id.errors" runat="server" />
|
|
...</programlisting>
|
|
|
|
<para>The SetErrorMessage specifies the message text or resource id of
|
|
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 <link
|
|
linkend="web-clientscripting">section</link>.</para>
|
|
|
|
<programlisting> <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></programlisting>
|
|
</sect3>
|
|
|
|
<sect3 xml:id="web-binding-list">
|
|
<title>HttpRequestListBindingContainer</title>
|
|
|
|
<para><classname>HttpRequestListBindingContaine</classname><classname>r
|
|
</classname>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 <code>requestBindings</code>
|
|
collection.</para>
|
|
|
|
<para>Please check out the Web Quick Start sample's demo of
|
|
<classname>HttpRequestListBindingContainer</classname>. Below is an
|
|
exerpt from that example showing how to use a
|
|
<classname>HttpRequestListBindingContainer</classname>.</para>
|
|
|
|
<para><programlisting language="csharp">protected override void InitializeDataBindings()
|
|
{
|
|
// HttpRequestListBindingContainer unbinds specified values from Request -> Productlist
|
|
HttpRequestListBindingContainer requestBindings =
|
|
new HttpRequestListBindingContainer("sku,name,quantity,price", "Products", typeof(ProductInfo));
|
|
requestBindings.AddBinding("sku", "Sku");
|
|
requestBindings.AddBinding("name", "Name");
|
|
requestBindings.AddBinding("quantity", "Quantity", quantityFormatter);
|
|
requestBindings.AddBinding("price", "Price", priceFormatter);
|
|
|
|
BindingManager.AddBinding(requestBindings);
|
|
}</programlisting></para>
|
|
|
|
<note>
|
|
Because browsers do not send the values of unchecked checkboxes, you cannot use HttpRequestListBindingContainer with <input type="checkbox" > html controls.
|
|
</note>
|
|
</sect3>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="web-databindingpanel">
|
|
<title>Using DataBindingPanel</title>
|
|
|
|
<para>To simplify use of Spring's Data Binding feature on web pages and
|
|
controls, Spring.Web provides a special
|
|
<classname>DataBindingPanel</classname> container control. A
|
|
<classname>DataBindingPanel</classname> does not render any html code
|
|
itself, but allows you to define additional, data binding-related
|
|
attributes for its child controls. </para>
|
|
|
|
<para><programlisting language="myxml"><%@ Page Language="C#" CodeFile="Default.aspx.cs" Inherits="DataBinding_EasyEmployeeInfo_Default" %>
|
|
<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %>
|
|
<html>
|
|
<body>
|
|
<spring:DataBindingPanel ID="ctlDataBindingPanel" runat="server">
|
|
<table cellpadding="3" cellspacing="3" border="0">
|
|
<tr>
|
|
<td>Employee ID:</td>
|
|
<td>
|
|
<asp:TextBox ID="txtId" runat="server" BindingTarget="Employee.Id" />
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>First Name:</td>
|
|
<td><asp:TextBox ID="txtFirstName" runat="server" BindingTarget="Employee.FirstName" /></td>
|
|
</tr>
|
|
</table>
|
|
</spring.DataBindingPanel>
|
|
</body>
|
|
</html></programlisting></para>
|
|
|
|
<para>Using DataBindingPanel, you can specify the binding information
|
|
directly on the control declaration. The following attributes are
|
|
recognized by a DataBindingPanel:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><code>BindingTarget</code> corresponds to the target
|
|
expression used in IBindingContainer.AddBinding().</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>BindingSource</code> 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.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>BindingDirection</code> is one of the values of the
|
|
BindingDirection enumeration.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>BindingFormatter</code> is the object name of a custom
|
|
formatter. The formatter instance is obtained by a call to
|
|
IApplicationContext.GetObject() each time it is needed.</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><code>BindingType</code> is the type of a completely
|
|
customized binding. Note that a custom binding type must implement
|
|
the following constructor signature:</para>
|
|
|
|
<para><literal>ctor(string source,string target, BindingDirection,
|
|
IFormatter)</literal></para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<note>
|
|
The Visual Studio Web Form Editor complains about binding attributes because it does not recognize them. You can safely ignore those warnings.
|
|
</note>
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>Customizing model persistence</title>
|
|
|
|
<para>As mentioned in the chapter introduction, model management needs
|
|
an application developer to override
|
|
<literal>InitializeModel()</literal>, <literal>SaveModel() </literal>and
|
|
<literal>LoadModel()</literal> 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
|
|
<property>ModelPersistenceMedium</property> property on Spring's base
|
|
Page or UserContorl class (e.g.<property>
|
|
Spring.Web.UI.UserControl)</property></para>
|
|
|
|
<para><programlisting language="myxml"><object id="modelPersister" type="Sample.DatabaseModelPersistenceMedium, MyCode"/>
|
|
|
|
<object type="UserRegistration.aspx">
|
|
<property name="ModelPersistenceMedium" ref="modelPersister"/>
|
|
</object></programlisting>To implement any arbitrary persistence
|
|
strategy, implement the IModelPersistenceMedium interface:</para>
|
|
|
|
<para><programlisting language="myxml">public interface IModelPersistenceMedium
|
|
{
|
|
// Load the model for the specified control context.
|
|
object LoadFromMedium( Control context );
|
|
|
|
// Save the specified model object.
|
|
void SaveToMedium( Control context, object modelToSave );
|
|
}</programlisting></para>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="web-localization">
|
|
<title>Localization and message sources</title>
|
|
|
|
<para>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. </para>
|
|
|
|
<para>Every <literal>.aspx</literal> 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 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.</para>
|
|
|
|
<para>Spring.Web supports several different approaches to localization
|
|
within a web application, which can be mixed and matched as appropriate.
|
|
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.</para>
|
|
|
|
<tip>
|
|
<para>For introductory information covering ASP.NET globalization and
|
|
localization, see <ulink
|
|
url="http://msdn.microsoft.com/asp.net/community/authors/mlb/default.aspx?pull=/library/en-us/dnaspp/html/aspnet-globalarchi.asp">Globalization
|
|
Architecture for ASP.NET</ulink> and <ulink
|
|
url="http://www.theserverside.net/articles/showarticle.tss?id=LocalizationPractices">Localization
|
|
Practices for ASP.NET 2.0</ulink> by Michele Leroux Bustamante.</para>
|
|
</tip>
|
|
|
|
<sect2 xml:id="web-localizers">
|
|
<title>Working with localizers</title>
|
|
|
|
<para>A localizer is an object that implements the
|
|
<literal>Spring.Globalization.ILocalizer</literal> interface.
|
|
<literal>Spring.Globalization.AbstractLocalizer</literal> is a
|
|
convenient base class for localization: this class has one abstract
|
|
method, <literal>LoadResources</literal>. This method must load and
|
|
return a list of all resources that must be automatically applied from
|
|
the resource store.</para>
|
|
|
|
<para>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
|
|
<literal>'$this'</literal> marker string value, and applies the values
|
|
to the controls that populate the page prior to the page being
|
|
rendered.</para>
|
|
|
|
<para>Spring.NET ships with one concrete implementation of a localizer,
|
|
<literal>Spring.Globalization.Localizers.ResourceSetLocalizer</literal>,
|
|
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 <literal>ILocalizer</literal> implementation that
|
|
loads a list of resources to apply from a database.</para>
|
|
|
|
<para>You typically configure the localizer to be used within an
|
|
abstract base definition for those pages that require
|
|
localization:</para>
|
|
|
|
<programlisting language="myxml"><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></programlisting>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>One last thing to note is that Spring.NET
|
|
<literal>UserControl</literal> 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.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="web-localization-push">
|
|
<title>Automatic localization with localizers ("push"
|
|
localization)</title>
|
|
|
|
<para>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 <literal>UserRegistration.aspx</literal>:</para>
|
|
|
|
<programlisting language="myxml"><%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %>
|
|
<%@ Page language="c#" Codebehind="UserRegistration.aspx.cs"
|
|
AutoEventWireup="false" Inherits="ArtFair.Web.UI.Forms.UserRegistration" %>
|
|
<html>
|
|
<body>
|
|
<spring:Content id="mainContent" contentPlaceholderId="main" runat="server">
|
|
<div align="right">
|
|
<asp:LinkButton ID="english" Runat="server" CommandArgument="en-US">English</asp:LinkButton>&nbsp;
|
|
<asp:LinkButton ID="serbian" Runat="server" CommandArgument="sr-SP-Latn">Srpski</asp:LinkButton>
|
|
</div>
|
|
<table>
|
|
<tr>
|
|
<td><asp:Label id="emailLabel" Runat="server"/></td>
|
|
<td><asp:TextBox id="email" Runat="server" Width="150px"/></td>
|
|
</tr>
|
|
<tr>
|
|
<td><asp:Label id="passwordLabel" Runat="server"/></td>
|
|
<td><asp:TextBox id="password" Runat="server" Width="150px"/></td>
|
|
</tr>
|
|
<tr>
|
|
<td><asp:Label id="passwordConfirmationLabel" Runat="server"/></td>
|
|
<td><asp:TextBox id="passwordConfirmation" Runat="server" Width="150px"/></td>
|
|
</tr>
|
|
<tr>
|
|
<td><asp:Label id="nameLabel" Runat="server"/></td>
|
|
<td><asp:TextBox id="name" Runat="server" Width="150px"/></td>
|
|
</tr>
|
|
...
|
|
|
|
<tr>
|
|
<td colspan="2">
|
|
<asp:Button id="saveButton" Runat="server"/>&nbsp;
|
|
<asp:Button id="cancelButton" Runat="server"/>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</spring:Content>
|
|
</body>
|
|
</html></programlisting>
|
|
|
|
<para>In the preceding <literal>.aspx</literal> code, none of the
|
|
<literal>Label</literal> or <literal>Button</literal> controls have had
|
|
a value assigned to the <literal>Text</literal> property. The values of
|
|
the <literal>Text</literal> property for these controls are stored in
|
|
the local resource file (of the page) using the following convention to
|
|
identify the resource (string).</para>
|
|
|
|
<programlisting>$this.controlId.propertyName</programlisting>
|
|
|
|
<para>The corresponding local resource file,
|
|
<literal>UserRegistration.aspx.resx</literal>, is shown below.</para>
|
|
|
|
<programlisting language="myxml"><root>
|
|
<data name="$this.emailLabel.Text">
|
|
<value>Email:</value>
|
|
</data>
|
|
<data name="$this.passwordLabel.Text">
|
|
<value>Password:</value>
|
|
</data>
|
|
<data name="$this.passwordConfirmationLabel.Text">
|
|
<value>Confirm password:</value>
|
|
</data>
|
|
<data name="$this.nameLabel.Text">
|
|
<value>Full name:</value>
|
|
</data>
|
|
|
|
...
|
|
|
|
<data name="$this.countryLabel.Text">
|
|
<value>Country:</value>
|
|
</data>
|
|
<data name="$this.saveButton.Text"><!--In next section this part of the example is used as a global resource example. Confusing?-->
|
|
<value>$messageSource.save</value>
|
|
</data>
|
|
<data name="$this.cancelButton.Text">
|
|
<value>$messageSource.cancel</value>
|
|
</data>
|
|
</root></programlisting>
|
|
|
|
<tip>
|
|
<title>Viewing .resx file in Visual Studio 2003</title>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>When Visual Studio creates the .resx file, it includes an
|
|
<literal>xds:schema</literal> element and several
|
|
<literal>reshead</literal> elements. Your data elements will follow
|
|
the <literal>reshead</literal> 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.</para>
|
|
|
|
<para>There is no way to visually edit resources in a RESX file. Lutz
|
|
Roeder has created a tool named <link
|
|
ns6:href="http://www.lutzroeder.com/dotnet/">Resourcer</link> that you
|
|
can use to edit them</para>
|
|
</tip>
|
|
|
|
<tip>
|
|
<title>Creating a .resx file in Visual Studio 2005/8</title>
|
|
|
|
<para>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.</para>
|
|
</tip>
|
|
|
|
<para>You must create a localizer for the page to enable automatic
|
|
localization:</para>
|
|
|
|
<programlisting language="myxml"><object id="localizer" type="Spring.Globalization.Localizers.ResourceSetLocalizer, Spring.Core"/>
|
|
|
|
<object type="UserRegistration.aspx">
|
|
<property name="Localizer" ref="localizer"/>
|
|
</object></programlisting>
|
|
|
|
<para>For more information on configuring localizers see <xref
|
|
linkend="web-localizers" /></para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="web-globalresources">
|
|
<title>Global message sources</title>
|
|
|
|
<para>Two resource definitions from the previous section require some
|
|
additional explanation:</para>
|
|
|
|
<programlisting language="myxml"><data name="$this.saveButton.Text">
|
|
<value>$messageSource.save</value>
|
|
</data>
|
|
<data name="$this.cancelButton.Text">
|
|
<value>$messageSource.cancel</value>
|
|
</data></programlisting>
|
|
|
|
<para>In some cases it makes sense to apply a resource that is defined
|
|
<emphasis>globally</emphasis> as opposed to locally. In this example, it
|
|
makes better sense to define values for the <literal>Save</literal> and
|
|
<literal>Cancel</literal> buttons globally as they will probably be used
|
|
throughout the application.</para>
|
|
|
|
<para>The above example demonstrates how one can achieve that by
|
|
defining a <emphasis>resource redirection</emphasis> expression as the
|
|
value of a local resource by prefixing a global resource name with the
|
|
following string.</para>
|
|
|
|
<programlisting>$messageSource.</programlisting>
|
|
|
|
<para>In the preceding example, this string tells the localizer to use
|
|
the <literal>save</literal> and <literal>cancel</literal> portions of
|
|
the resource key as lookup keys to retrieve the actual values from a
|
|
global <!--Is it clear that the example is indicating a global message source? In previous section the same example snippet is characterized as part of a local
|
|
|
|
resource file.-->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 <!--Clarify what you mean by *culture* in this context.-->culture.</para>
|
|
|
|
<para>Global resources are (on a per-context basis) defined as a plain
|
|
vanilla object definition using the reserved name of
|
|
<literal>messageSource</literal>, which you can add to your Spring.NET
|
|
configuration file:</para>
|
|
|
|
<programlisting language="myxml"><object id="messageSource" type="Spring.Context.Support.ResourceSetMessageSource, Spring.Core">
|
|
<property name="ResourceManagers">
|
|
<list>
|
|
<value>MyApp.Web.Resources.Strings, MyApp.Web</value>
|
|
</list>
|
|
</property>
|
|
</object></programlisting>
|
|
|
|
<important>
|
|
<title>for .NET 2.0 or higher</title>
|
|
|
|
<para>To use resources from your App_GlobalResources folder, specify
|
|
<literal>App_GlobalResources</literal> as the assembly name:</para>
|
|
|
|
<literal><value>Resources.Strings,
|
|
App_GlobalResources</value></literal>
|
|
</important>
|
|
|
|
<para>See the SpringAir example application for more. The global
|
|
resources are cached within the Spring.NET
|
|
<literal>IApplicationContext</literal> and are accessible through the
|
|
Spring.NET <literal>IMessageSource</literal> interface.</para>
|
|
|
|
<para>The Spring.Web <literal>Page</literal> and
|
|
<literal>UserControl</literal> classes have a reference to their owning
|
|
<literal>IApplicationContext</literal> and its associated
|
|
<literal>IMessageSource</literal>. As such, they automatically redirect
|
|
resource lookups to a global message source if a local resource cannot
|
|
be found.</para>
|
|
|
|
<para>Currently, the <literal>ResourceSetMessageSource</literal> is the
|
|
only message source implementation that ships with Spring.NET.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="web-pull-localization">
|
|
<title>Applying resources manually ("pull" localization)</title>
|
|
|
|
<para>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 <xref
|
|
linkend="springair" />).</para>
|
|
|
|
<para>These situations call for a pull-style mechanism for localization,
|
|
which is a simple <literal>GetMessage</literal> call:</para>
|
|
|
|
<programlisting language="myxml"><asp:Repeater id="outboundFlightList" Runat="server">
|
|
<HeaderTemplate>
|
|
<table border="0" width="90%" cellpadding="0" cellspacing="0" align="center" class="suggestedTable">
|
|
<thead>
|
|
<tr class="suggestedTableCaption">
|
|
<th colspan="6">
|
|
<%= GetMessage("outboundFlights") %>
|
|
</th>
|
|
</tr>
|
|
<tr class="suggestedTableColnames">
|
|
<th><%= GetMessage("flightNumber") %></th>
|
|
<th><%= GetMessage("departureDate") %></th>
|
|
<th><%= GetMessage("departureAirport") %></th>
|
|
<th><%= GetMessage("destinationAirport") %></th>
|
|
<th><%= GetMessage("aircraft") %></th>
|
|
<th><%= GetMessage("seatPlan") %></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
</HeaderTemplate></programlisting>
|
|
|
|
<para>The <literal>GetMessage</literal> method is available within both
|
|
the <literal>Spring.Web.UI.Page </literal> and
|
|
<literal>Spring.Web.UI.UserControl</literal> classes, and it falls back
|
|
automatically to a global message source lookup if a local resource is
|
|
not found.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="web-localizing-images">
|
|
<title>Localizing images within a web application</title>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>The Spring.Web <literal>Page</literal> class exposes the
|
|
<literal>ImagesRoot</literal> 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.</para>
|
|
|
|
<para>To localize images, you create a directory for each localized
|
|
culture under the <literal>ImagesRoot</literal> directory:</para>
|
|
|
|
<programlisting>/MyApp
|
|
/Images
|
|
/en
|
|
/en-US
|
|
/fr
|
|
/fr-CA
|
|
/sr-SP-Cyrl
|
|
/sr-SP-Latn
|
|
...</programlisting>
|
|
|
|
<para>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
|
|
<literal><spring:LocalizedImage></literal>:</para>
|
|
|
|
<programlisting language="myxml"><%@ Page language="c#" Codebehind="StandardTemplate.aspx.cs"
|
|
AutoEventWireup="false" Inherits="SpringAir.Web.StandardTemplate" %>
|
|
<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %>
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
|
|
<html>
|
|
<body>
|
|
<spring:LocalizedImage id="logoImage" imageName="spring-air-logo.jpg" borderWidth="0" runat="server" />
|
|
</body>
|
|
</html></programlisting>
|
|
|
|
<para>This control will find the most specific directory that contains
|
|
an image with the specified name using standard localization fallback
|
|
rules and the user's culture. For example, if the user's culture is
|
|
<literal>'en-US'</literal>, the localizer will look for the
|
|
<literal>spring-air-logo.jpg</literal> file in
|
|
<literal>Images/en-US</literal>, then in <literal>Images/en</literal>
|
|
and finally, if the image file has still not been found, in the root
|
|
<literal>Images</literal> directory (which for all practical purposes
|
|
serves as an invariant culture folder).</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="web-culture">
|
|
<title>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</title>
|
|
|
|
<para>In addition to global and local resource management, Spring.Web
|
|
supports user culture management by exposing the current
|
|
<literal>CultureInfo</literal> through the
|
|
<literal>UserCulture</literal> property on the <literal>Page</literal>
|
|
and <literal>UserControl</literal> classes.</para>
|
|
|
|
<para>The <literal>UserCulture</literal> property delegates culture
|
|
resolution to an implementation of the
|
|
<literal>Spring.Globalization.ICultureResolver</literal> interface. You
|
|
specify the culture resolver to use by configuring the
|
|
<literal>CultureResolver</literal> property of the
|
|
<literal>Page</literal> class in the relevant object definition:</para>
|
|
|
|
<programlisting language="myxml"><object name="BasePage" abstract="true">
|
|
<property name="CultureResolver">
|
|
<object type="Spring.Globalization.Resolvers.CookieCultureResolver, Spring.Web"/>
|
|
</property>
|
|
</object></programlisting>
|
|
|
|
<para>Several useful implementations of
|
|
<literal>ICultureResolver</literal> ship as part of Spring.Web, so it is
|
|
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 <literal>ICultureResolver</literal>
|
|
interface.</para>
|
|
|
|
<sect3>
|
|
<title>DefaultWebCultureResolver</title>
|
|
|
|
<para><methodname>DefaultWebCultureResolver</methodname>, the default
|
|
culture resolver implementation, is used if you do not specify a
|
|
culture resolver for a page, or if you inject a
|
|
<literal>DefaultWebCultureResolver</literal> 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 <literal>DefaultCulture</literal> property on the
|
|
resolver.</para>
|
|
|
|
<para>The <literal>DefaultWebCultureResolver</literal> looks first at
|
|
the <literal>DefaultCulture</literal> property and return its value if
|
|
said property value is not null. If it is null, the
|
|
<literal>DefaultWebCultureResolver</literal> falls back to request
|
|
header inspection. If no <literal>'Accept-Lang'</literal> request
|
|
headers are present , the resolver returns the UI culture of the
|
|
currently executing thread.</para>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<title>RequestCultureResolver</title>
|
|
|
|
<para>The RequestCultureResolver resolver operates similar to the
|
|
<literal>DefaultWebCultureResolver</literal>, except that it always
|
|
checks request headers <emphasis>first</emphasis>, and only then falls
|
|
back to the value of the <literal>DefaultCulture</literal> property or
|
|
the culture code of the current thread.</para>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<title>SessionCultureResolver</title>
|
|
|
|
<para>The <methodname>SessionCultureResolver</methodname> resolver
|
|
looks for culture information in the user's session and returns the
|
|
information if it finds it. If not,
|
|
<methodname>SessionCultureResolver</methodname> falls back to the
|
|
behavior of the <literal>DefaultWebCultureResolver</literal>.</para>
|
|
</sect3>
|
|
|
|
<sect3>
|
|
<title>CookieCultureResolver</title>
|
|
|
|
<para>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 <literal>DefaultWebCultureResolver</literal>.</para>
|
|
|
|
<warning>
|
|
<para><literal>CookieCultureResolver</literal> does not work if your
|
|
application uses <literal>localhost</literal> as the server URL,
|
|
which is a typical setting in a development environment.</para>
|
|
|
|
<para>To work around this limitation, use
|
|
<literal>SessionCultureResolver</literal> during development and
|
|
switch to <literal>CookieCultureResolver</literal> before you deploy
|
|
the application in a production. This is easily accomplished in
|
|
Spring.Web (simply change the config file) but is something that you
|
|
should be aware of.</para>
|
|
</warning>
|
|
</sect3>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="web-culture-changing">
|
|
<title>Changing cultures</title>
|
|
|
|
<para>To change the culture, application developers need to define one
|
|
of the culture resolvers that support culture changes, such as
|
|
<literal>SessionCultureResolver</literal> or
|
|
<literal>CookieCultureResolver</literal> in the Spring application
|
|
context. For example, </para>
|
|
|
|
<para> You also can write a custom <literal>ICultureResolver</literal>
|
|
that persists culture information in a database, as part of a user's
|
|
profile.</para>
|
|
|
|
<programlisting> <object id="cultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web" /></programlisting>
|
|
|
|
<para>Once that requirement is satisfied, you set the
|
|
<literal>UserCulture</literal> property to a new
|
|
<literal>CultureInfo</literal> object before the page is rendered. In
|
|
the following <literal>.aspx</literal> 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
|
|
(<literal>UserRegistration.aspx.cs</literal>) is shown below.</para>
|
|
|
|
<programlisting language="csharp">protected override void OnInit(EventArgs e)
|
|
{
|
|
InitializeComponent();
|
|
|
|
this.english.Command += new CommandEventHandler(this.SetLanguage);
|
|
this.serbian.Command += new CommandEventHandler(this.SetLanguage);
|
|
|
|
base.OnInit(e);
|
|
}
|
|
|
|
private void SetLanguage(object sender, CommandEventArgs e)
|
|
{
|
|
this.UserCulture = new CultureInfo((string) e.CommandArgument);
|
|
}</programlisting>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="web-resultmapping">
|
|
<title>Result mapping</title>
|
|
|
|
<para>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 <literal>Response.Redirect</literal>
|
|
and <literal>Server.Transfer</literal> methods within event
|
|
handlers.</para>
|
|
|
|
<para>This approach is problematic because any changes to the flow of an
|
|
application necessitates code changes (with the attendant recompilation,
|
|
testing, redeployment, and so on). A better way, which works in many MVC (
|
|
<ulink
|
|
url="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpatterns/html/DesMVC.asp">Model-View-Controller</ulink>)
|
|
web frameworks, is to enable you to externalize the mapping of action
|
|
results to target pages.</para>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>In Spring.Web, a logical result is encapsulated and defined by the
|
|
<literal>Result</literal> class; thus you can configure results like any
|
|
other object:</para>
|
|
|
|
<programlisting language="myxml">
|
|
<objects xmlns="http://www.springframework.net">
|
|
|
|
<object id="homePageResult" type="Spring.Web.Support.Result, Spring.Web">
|
|
<property name="TargetPage" value="~/Default.aspx"/>
|
|
<property name="Mode" value="Transfer"/>
|
|
<property name="Parameters">
|
|
<dictionary>
|
|
<entry key="literal" value="My Text"/>
|
|
<entry key="name" value="%{UserInfo.FullName}"/>
|
|
<entry key="host" value="%{Request.UserHostName}"/>
|
|
</dictionary>
|
|
</property>
|
|
</object>
|
|
|
|
<object id="loginPageResult" type="Spring.Web.Support.Result, Spring.Web">
|
|
<property name="TargetPage" value="Login.aspx"/>
|
|
<property name="Mode" value="Redirect"/>
|
|
</object>
|
|
|
|
<object type="UserRegistration.aspx" parent="basePage">
|
|
<property name="UserManager" ref="userManager"/>
|
|
<property name="Results">
|
|
<dictionary>
|
|
<entry key="userSaved" value-ref="homePageResult"/>
|
|
<entry key="cancel" value-ref="loginPageResult"/>
|
|
</dictionary>
|
|
</property>
|
|
</object>
|
|
|
|
</objects>
|
|
</programlisting>
|
|
|
|
<para>The only property for which you <emphasis>must</emphasis> supply a
|
|
value for each result is the <literal>TargetPage</literal> property. The
|
|
value of the <literal>Mode</literal> property can be
|
|
<literal>Transfer</literal>, <literal>TransferNoPreserve</literal>,
|
|
or<literal> Redirect</literal>, and defaults to
|
|
<literal>Transfer</literal> if none is specified.
|
|
<property>TransferNoPreserve</property> issues a server-side transfer with
|
|
'preserveForm=false', so that QueryString and Form data are not
|
|
preserved.</para>
|
|
|
|
<para>If your target page requires parameters, you can define them with
|
|
the <literal>Parameters</literal> dictionary property. You specify literal
|
|
values or <link linkend="expressions">object navigation expressions</link>
|
|
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 <literal>homePageResult</literal> needs to
|
|
expose a <literal>UserInfo</literal> property on the page class
|
|
itself.<note>
|
|
<para>In Spring.NET 1.1.0 and earlier, the prefix indicated an object
|
|
navigation expression in the <literal>Parameters</literal> dictionary
|
|
property was the dollar sign, for example,
|
|
<literal>${UserInfo.FullName}.</literal>This convention conflicted
|
|
with the prefix used to perform property replacement, the dollar sign,
|
|
as described in the section <link
|
|
linkend="objects-factory-placeholderconfigurer">PropertyPlaceholderConfigurer</link>.
|
|
As a workaround you can differentiate the prefix and suffix used in
|
|
<property>PropertyPlaceholderConfigurer</property>, for example prefix
|
|
= $${ and suffix = }. In Spring. NET 1.1.1, a new prefix character,
|
|
the percent sign (i.e.<literal>%{UserInfo.FullName}</literal>.) can be
|
|
used in the <literal>Parameters</literal> dictionary to avoid this
|
|
conflict , so you can keep the familiar NAnt style
|
|
PropertyPlaceholderConfigurer defaults.</para>
|
|
</note></para>
|
|
|
|
<para>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 <literal>HttpContext.Items</literal>
|
|
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).</para>
|
|
|
|
<tip>
|
|
<para>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 <literal>string
|
|
BuildUrl( string resolvedPath, IDictionary resolvedParameters
|
|
)</literal>. See the API documentation for additional
|
|
information.</para>
|
|
</tip>
|
|
|
|
<para>The preceding example shows independent result object definitions,
|
|
which are useful for global results such as a home- and login- page.
|
|
<literal>Result</literal> 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:</para>
|
|
|
|
<programlisting language="myxml">
|
|
<object type="~/UI/Forms/UserRegistration.aspx" parent="basePage">
|
|
<property name="UserManager">
|
|
<ref object="userManager"/>
|
|
</property>
|
|
|
|
<property name="Results">
|
|
<dictionary>
|
|
<entry key="userSaved" value="redirect:UserRegistered.aspx?status=Registration Successful,user=${UserInfo}"/>
|
|
<entry key="cancel" value-ref="homePageResult"/>
|
|
</dictionary>
|
|
</property>
|
|
</object>
|
|
</programlisting>
|
|
|
|
<para>The short notation for the result must adhere to the following
|
|
format...</para>
|
|
|
|
<programlisting>[<mode>:]<targetPage>[?param1,param2,...,paramN]</programlisting>
|
|
|
|
<para>Possible values for the <literal>mode</literal> value referred to in
|
|
the preceding notation snippet:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para><literal>redirect</literal>: calls
|
|
Response.Redirect(string)</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>redirectNoAbort</literal>: calls
|
|
Response.Redirect(string, false)</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>transfer</literal>: calls
|
|
Server.Transfer(string)</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para><literal>TransferNoPreserve</literal>: calls
|
|
Server.Transfer(string, false)</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>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.</para>
|
|
|
|
<para>After you define your results, you can use them within the event
|
|
handlers of your pages
|
|
(<literal>UserRegistration.apsx.cs</literal>):</para>
|
|
|
|
<programlisting language="csharp">private void SaveUser(object sender, EventArgs e)
|
|
{
|
|
UserManager.SaveUser(UserInfo);
|
|
SetResult("userSaved");
|
|
}
|
|
|
|
public void Cancel(object sender, EventArgs e)
|
|
{
|
|
SetResult("cancel");
|
|
}
|
|
|
|
protected override void OnInit(EventArgs e)
|
|
{
|
|
InitializeComponent();
|
|
|
|
this.saveButton.Click += new EventHandler(this.SaveUser);
|
|
this.cancelButton.Click += new EventHandler(this.Cancel);
|
|
|
|
base.OnInit(e);
|
|
}</programlisting>
|
|
|
|
<para>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.</para>
|
|
|
|
<sect2 xml:id="web-resultmapping-custom">
|
|
<title>Registering user defined transfer modes</title>
|
|
|
|
<para>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:</para>
|
|
|
|
<programlisting><resultmode>:<textual result representation></programlisting>
|
|
|
|
<para>The interface <literal>IResultFactory</literal> is responsible for
|
|
creating an IResult object from these two pieces:</para>
|
|
|
|
<programlisting language="csharp">public interface IResultFactory
|
|
{
|
|
IResult CreateResult( string resultMode, string resultText );
|
|
}</programlisting>
|
|
|
|
<para>You use a <literal>ResultFactoryRegistry</literal> to associate a
|
|
given resultmode string with an <literal>IResultFactory</literal>
|
|
implementation:</para>
|
|
|
|
<programlisting language="csharp">class MySpecialResultLogic : IResult
|
|
{
|
|
...
|
|
}
|
|
|
|
class MySpecialResultLogicFactory : IResultFactory
|
|
{
|
|
IResult Create( string mode, string expression ) {
|
|
/* ... convert 'expression' into MySpecialResultLogic */
|
|
}
|
|
}
|
|
|
|
// register with global factory
|
|
ResultFactoryRegistry.RegisterResultFactory( "mySpecialMode", new MySpecialResultLogicFactory );
|
|
</programlisting>
|
|
|
|
<para>You then use the custom <property>continue</property> mode in your
|
|
page:</para>
|
|
|
|
<programlisting language="xml"><-- configure your Results -->
|
|
<object type="mypage.aspx">
|
|
<property name="Results">
|
|
<dictionary>
|
|
<entry key="continue" value="mySpecialMode:<some MySpecialResultLogic string representation>" />
|
|
</dictionary>
|
|
</property>
|
|
</object></programlisting>
|
|
|
|
<para>The result redirection is done as before, by calling
|
|
<literal>myPage.SetResult("cancel");</literal></para>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="web-clientscripting">
|
|
<title>Client-side scripting</title>
|
|
|
|
<para>ASP.NET supports client-side scripting through the use of the
|
|
<literal>Page.RegisterClientScriptBlock</literal> and
|
|
<literal>Page.RegisterStartupScript</literal> methods. However, neither
|
|
method allows you to output a registered script markup within a
|
|
<literal><head></literal> section of a page, which in many cases is
|
|
exactly what you need to do.</para>
|
|
|
|
<sect2 xml:id="web-scripthead">
|
|
<title>Registering scripts within the head HTML section</title>
|
|
|
|
<para>Spring.Web adds several methods to enhance client-side scripting
|
|
to the base <literal>Spring.Web.UI.Page</literal> class:
|
|
<literal>RegisterHeadScriptBlock</literal> and
|
|
<literal>RegisterHeadScriptFile</literal>, each with a few overrides.
|
|
You can call these methods from your custom pages and controls in order
|
|
to register script blocks and script files that must be included in the
|
|
<literal><head></literal> section of the final HTML page.</para>
|
|
|
|
<para>You must use the <literal><spring:Head></literal>
|
|
server-side control to define your <literal><head></literal>
|
|
section instead of using the standard HTML
|
|
<literal><head></literal> element. This is shown below.</para>
|
|
|
|
<programlisting language="myxml"><%@ Page language="c#" Codebehind="StandardTemplate.aspx.cs"
|
|
AutoEventWireup="false" Inherits="SpringAir.Web.StandardTemplate" %>
|
|
<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %>
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
|
|
<html>
|
|
<spring:Head runat="server" id="Head1">
|
|
<title>
|
|
<spring:ContentPlaceHolder id="title" runat="server">
|
|
<%= GetMessage("default.title") %>
|
|
</spring:ContentPlaceHolder>
|
|
</title>
|
|
<LINK href="<%= CssRoot %>/default.css" type="text/css" rel="stylesheet">
|
|
<spring:ContentPlaceHolder id="head" runat="server"></spring:ContentPlaceHolder>
|
|
</spring:Head>
|
|
<body>
|
|
...
|
|
</body>
|
|
</html></programlisting>
|
|
|
|
<para>The preceding example above shows how you typically set-up a
|
|
<literal><head></literal> section within a master page template to
|
|
be able to change the title value and to add additional elements to the
|
|
<literal><head></literal> section from the child pages using
|
|
<literal><spring:ContentPlaceholder></literal> controls. However,
|
|
only the <literal><spring:Head></literal> declaration is required
|
|
in order for Spring.NET <literal>Register*</literal> scripts to work
|
|
properly.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="web-scriptcss">
|
|
<title>Adding CSS definitions to the head section</title>
|
|
|
|
<para>In a similar fashion, you can add references to CSS files, or even
|
|
specific styles, directly to the <literal><head></literal> HTML
|
|
section using <literal>Page.RegisterStyle</literal> and
|
|
<literal>Page.RegisterStyleFile</literal> methods. The latter one simply
|
|
allows you to include a reference to an external CSS file, while the
|
|
former one allows you to define embedded style definitions by specifying
|
|
the style name and definition as the parameters. The final list of style
|
|
definitions registered this way will be rendered within the single
|
|
embedded <literal>style</literal> section of the final HTML
|
|
document.</para>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="web-wellknowdirectory">
|
|
<title>Well-known directories</title>
|
|
|
|
<para>To make the manual inclusion of client-side scripts, CSS files and
|
|
images easier, the Spring.Web <literal>Page</literal> 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.</para>
|
|
|
|
<para>These properties are <literal>ScriptsRoot</literal>,
|
|
<literal>CssRoot</literal> and <literal>ImagesRoot</literal>. They have
|
|
default values of <literal>Scripts</literal>, <literal>CSS</literal> and
|
|
<literal>Images</literal>, 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:</para>
|
|
|
|
<programlisting language="myxml"><object name="basePage" abstract="true">
|
|
<description>
|
|
Convenience base page definition for all the pages.
|
|
|
|
Pages that reference this definition as their parent (see the examples below)
|
|
will automatically inherit the following properties....
|
|
|
|
</description>
|
|
<property name="CssRoot" value="Web/CSS"/>
|
|
<property name="ImagesRoot" value="Web/Images"/>
|
|
</object>
|
|
</programlisting>
|
|
</sect2>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="web-usercontrols">
|
|
<title>Spring user controls</title>
|
|
|
|
<para>Spring provides several custom user controls that are located in the
|
|
<literal>Spring.Web.UI.Controls</literal> 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.</para>
|
|
|
|
<sect2 xml:id="web-validation-controls">
|
|
<title>Validation controls</title>
|
|
|
|
<para>You can specify the location in the web page where validation
|
|
errors are to be rendered by using the
|
|
<literal>ValidationSummary</literal> and
|
|
<literal>ValidationError</literal> controls. Two controls exist because
|
|
they have different defaults for how errors are rendered.
|
|
<literal>ValidationSummary</literal> is used to display potentially
|
|
multiple errors identified by the validation framework.
|
|
<literal>ValidationError</literal> is used to display field-level
|
|
validation errors. Please refer to the section <link
|
|
linkend="validation-aspnet-usage">ASP.NET usage tips</link> in the
|
|
chapter on the <link linkend="validation">Validation Framework</link>
|
|
more information.</para>
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>Databinding controls</title>
|
|
|
|
<para>Some standard controls are not easy to use with Spring's
|
|
databinding support. Examples are check boxes and ratio button groups.
|
|
Here you should use the <literal>CheckBoxList</literal> and
|
|
<literal>RadioButtonGroup</literal> controls. You can do databinding
|
|
itself by using the <link
|
|
linkend="web-databindingpanel">DataBindingPanel</link> instead of the
|
|
using the BindingManager API within the code behind page.</para>
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>Calendar control</title>
|
|
|
|
<para>A pop-up DHTML calendar control is provided. It is a slightly
|
|
modified version of the <ulink
|
|
url="http://www.dynarch.com/projects/calendar">Dynarch.com DHTML
|
|
Calendar</ulink> control written by Mihai Bazon.</para>
|
|
</sect2>
|
|
|
|
<sect2>
|
|
<title>Panel control</title>
|
|
|
|
<para>You can suppress dependency injection for controls inside your
|
|
ASP.NET by using the Panel control. See <link
|
|
linkend="web-controlling-di">Customizing control dependency
|
|
injection</link> .</para>
|
|
</sect2>
|
|
</sect1>
|
|
</chapter>
|