fixed SPRNET-1118 + added sample "NestedEmployeeInfo" to WebQuickStart

This commit is contained in:
eeichinger
2009-04-16 23:07:09 +00:00
parent e935787f6e
commit 71f2834158
11 changed files with 358 additions and 84 deletions

View File

@@ -35,6 +35,10 @@
<p>
Demonstrates basic model validation capabilities
</p>
<h3><a href="NestedEmployeeInfo/Default.aspx">Nested Employee Info</a></h3>
<p>
Demonstrates basic model validation capabilities on controls
</p>
<h3><a href="Lists/Default.aspx">Lists</a></h3>
<p>
Demonstrates bi-directional binding of multi-selection listboxes

View File

@@ -0,0 +1,25 @@
<%@ Page Language="C#" EnableViewState="false" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="DataBinding_RobustEmployeeInfo_Default" %>
<%-- @ Reference Control="EmployeeInfoEditor.ascx" --%>
<%@ Register TagPrefix="user" TagName="EmployeeInfoEditor" Src="EmployeeInfoEditor.ascx" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Data Binding - Nested Employee Info example</title>
</head>
<body>
<h2><a href="../../Default.aspx">Welcome to Spring.NET Web Framework Quick Start Guide</a></h2>
<h2><a href="../Default.aspx">Bi-directional DataBinding</a></h2>
<form id="form1" runat="server">
<div>
<h2>Nested Employee Info example</h2>
<p>
This is the same as Robust Employee Info example except that all controls are placed on nested a user control
</p>
<!-- this summary control captures validation errors from the "Editor" control below -->
<spring:ValidationSummary ID="summary" Provider="summary" ValidationContainerName="Editor" runat="server" />
<user:EmployeeInfoEditor ID="Editor" runat="server" />
</div>
</form>
</body>
</html>

View File

@@ -0,0 +1,9 @@
/// <summary>
/// Notice that your web page has to extend Spring.Web.UI.Page class
/// in order to enable data binding and many other features.
/// </summary>
public partial class DataBinding_RobustEmployeeInfo_Default : Spring.Web.UI.Page
{
private EmployeeInfoEditor employeeInfoEditor;
}

View File

@@ -0,0 +1,87 @@
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="EmployeeInfoEditor.ascx.cs" Inherits="EmployeeInfoEditor" %>
<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %>
<table cellpadding="3" cellspacing="3" border="0">
<tr>
<td>Employee ID:</td>
<td>
<asp:TextBox ID="txtId" runat="server" EnableViewState="false" />
<spring:ValidationError ID="errId" Provider="id.errors" runat="server" />
</td>
</tr>
<tr>
<td>First Name:</td>
<td><asp:TextBox ID="txtFirstName" runat="server" EnableViewState="false" /></td>
</tr>
<tr>
<td>Last Name:</td>
<td><asp:TextBox ID="txtLastName" runat="server" EnableViewState="false" /></td>
</tr>
<tr>
<td>Date of Birth:</td>
<td>
<asp:TextBox ID="txtDOB" runat="server" EnableViewState="false" />
<spring:ValidationError ID="errDOB" Provider="dob.errors" runat="server" />
</td>
</tr>
<tr>
<td>Gender:</td>
<td>
<!--
RadioButtonGroup control exposes Value property which is set
to the value of the ID property of the selected radio button
-->
<spring:RadioButtonGroup ID="rbgGender" runat="server" EnableViewState="false">
<asp:RadioButton ID="Male" Text="Male" runat="server" EnableViewState="false" />
<asp:RadioButton ID="Female" Text="Female" runat="server" EnableViewState="false" />
</spring:RadioButtonGroup>
</td>
</tr>
<tr>
<td>Salary:</td>
<td>
<asp:TextBox ID="txtSalary" runat="server" EnableViewState="false" />
<spring:ValidationError ID="errSalary" Provider="salary.errors" runat="server" />
</td>
</tr>
<tr>
<td>Address Type:</td>
<td>
<!--
Another way to select one of the predefined values, which is
how enum values should always be entered
-->
<asp:DropDownList ID="ddlAddressType" runat="server" EnableViewState="false">
<asp:ListItem Value="Home" Text="Home" Selected="True" />
<asp:ListItem Value="Office" Text="Office" />
</asp:DropDownList>
</td>
</tr>
<tr>
<td>Street 1:</td>
<td><asp:TextBox ID="txtStreet1" runat="server" EnableViewState="false" /></td>
</tr>
<tr>
<td>Street 2:</td>
<td><asp:TextBox ID="txtStreet2" runat="server" EnableViewState="false" /></td>
</tr>
<tr>
<td>City:</td>
<td><asp:TextBox ID="txtCity" runat="server" EnableViewState="false" /></td>
</tr>
<tr>
<td>State:</td>
<td><asp:TextBox ID="txtState" runat="server" EnableViewState="false" /></td>
</tr>
<tr>
<td>Postal Code/ZIP:</td>
<td><asp:TextBox ID="txtPostalCode" runat="server" EnableViewState="false" /></td>
</tr>
<tr>
<td>Country:</td>
<td><asp:TextBox ID="txtCountry" runat="server" EnableViewState="false" /></td>
</tr>
<tr>
<td>&nbsp;</td>
<td><asp:Button ID="btnSave" runat="server" Text="Save" OnClick="btnSave_Click" /></td>
</tr>
</table>

View File

@@ -0,0 +1,46 @@
using System;
using System.Diagnostics;
using Spring.Globalization.Formatters;
public partial class EmployeeInfoEditor : Spring.Web.UI.UserControl
{
private EmployeeInfo employee = new EmployeeInfo();
public EmployeeInfo Employee
{
get { return employee; }
}
/// <summary>
/// In order to declare data bindings, all you need to do is
/// override InitializeDataBindings method and add all necessary
/// data bindings to the BindingManager.
/// </summary>
protected override void InitializeDataBindings()
{
BindingManager.AddBinding("txtId.Text", "Employee.Id")
.SetErrorMessage("ID has to be an integer", "id.errors", "summary");
BindingManager.AddBinding("txtFirstName.Text", "Employee.FirstName");
BindingManager.AddBinding("txtLastName.Text", "Employee.LastName");
BindingManager.AddBinding("txtDOB.Text", "Employee.DateOfBirth")
.SetErrorMessage("Invalid date value", "dob.errors", "summary");
BindingManager.AddBinding("txtSalary.Text", "Employee.Salary", new CurrencyFormatter())
.SetErrorMessage("Salary must be a valid currency value.", "salary.errors", "summary");
BindingManager.AddBinding("rbgGender.Value", "Employee.Gender");
BindingManager.AddBinding("ddlAddressType.SelectedValue", "Employee.MailingAddress.AddressType");
BindingManager.AddBinding("txtStreet1.Text", "Employee.MailingAddress.Street1");
BindingManager.AddBinding("txtStreet2.Text", "Employee.MailingAddress.Street2");
BindingManager.AddBinding("txtCity.Text", "Employee.MailingAddress.City");
BindingManager.AddBinding("txtState.Text", "Employee.MailingAddress.State");
BindingManager.AddBinding("txtPostalCode.Text", "Employee.MailingAddress.PostalCode");
BindingManager.AddBinding("txtCountry.Text", "Employee.MailingAddress.Country");
}
protected void btnSave_Click(object sender, EventArgs e)
{
// do something with employee such as:
// employeeDao.Save(Employee);
Debug.Write(Employee);
}
}

View File

@@ -33,12 +33,14 @@
on the Debug.Write(Employee) statement in the event handler for the Save button,
so you can see how Employee object was populated by the framework.
</p>
<!-- this summary control captures validation errors on this page -->
<spring:ValidationSummary ID="summary" Provider="summary" ValidationContainerName="Editor" runat="server" />
<table cellpadding="3" cellspacing="3" border="0">
<tr>
<td>Employee ID:</td>
<td>
<asp:TextBox ID="txtId" runat="server" EnableViewState="false" />
<spring:ValidationError ID="errId" Provider="id.errors" runat="server" />
<spring:ValidationError ID="errId" Provider="id.errors" runat="server" /><!-- read msg from "id.error" provider -->
</td>
</tr>
<tr>

View File

@@ -23,7 +23,7 @@ public partial class DataBinding_RobustEmployeeInfo_Default : Spring.Web.UI.Page
protected override void InitializeDataBindings()
{
BindingManager.AddBinding("txtId.Text", "Employee.Id")
.SetErrorMessage("ID has to be an integer", "id.errors");
.SetErrorMessage("ID has to be an integer", "id.errors"); // send msg to "id.errors" provider
BindingManager.AddBinding("txtFirstName.Text", "Employee.FirstName");
BindingManager.AddBinding("txtLastName.Text", "Employee.LastName");
BindingManager.AddBinding("txtDOB.Text", "Employee.DateOfBirth")

View File

@@ -1,81 +1,73 @@
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web"/>
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/>
</sectionGroup>
<sectionGroup name="common">
<section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging"/>
</sectionGroup>
</configSections>
<!-- to see logging output in the attached debugger -->
<common>
<logging>
<factoryAdapter type="Common.Logging.Simple.TraceLoggerFactoryAdapter, Common.Logging">
<arg key="Level" value="ALL"/>
</factoryAdapter>
</logging>
</common>
<system.web>
<customErrors mode="Off" />
<compilation debug="true">
<assemblies>
<add assembly="System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
</assemblies>
</compilation>
<pages>
<controls>
<add tagPrefix="spring" namespace="Spring.Web.UI.Controls" assembly="Spring.Web"/>
</controls>
</pages>
<!--
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.WebContextHandler, Spring.Web"/>
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core"/>
</sectionGroup>
<sectionGroup name="common">
<section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging"/>
</sectionGroup>
</configSections>
<!-- to see logging output in the attached debugger -->
<common>
<logging>
<factoryAdapter type="Common.Logging.Simple.TraceLoggerFactoryAdapter, Common.Logging">
<arg key="Level" value="ALL"/>
</factoryAdapter>
</logging>
</common>
<system.web>
<customErrors mode="Off"/>
<compilation debug="true">
</compilation>
<pages>
<controls>
<add tagPrefix="spring" namespace="Spring.Web.UI.Controls" assembly="Spring.Web"/>
</controls>
</pages>
<!--
The following needs to be configured in order to enable Spring.NET Web Framework features
-->
<httpModules>
<add name="SpringModule" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
<add name="HtmlCommentAppender" type="HtmlCommentAppenderModule"/>
<!-- this custom module is only for DI demonstration purposes -->
</httpModules>
<httpHandlers>
<add verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web"/>
</httpHandlers>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules>
<add name="SpringModule" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
<add name="HtmlCommentAppender" type="HtmlCommentAppenderModule"/>
</modules>
<handlers>
<add name="SpringPageHandler" verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web"/>
</handlers>
</system.webServer>
<!-- demonstrates how to configure IHttpModule instances -->
<spring>
<context>
<resource uri="config://spring/objects"/>
</context>
<objects xmlns="http://www.springframework.net">
<!-- configure our custom HttpModule -->
<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>
</objects>
</spring>
<httpModules>
<add name="SpringModule" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
<add name="HtmlCommentAppender" type="HtmlCommentAppenderModule"/>
<!-- this custom module is only for DI demonstration purposes -->
</httpModules>
<httpHandlers>
<add verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web"/>
</httpHandlers>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules>
<add name="SpringModule" type="Spring.Context.Support.WebSupportModule, Spring.Web"/>
<add name="HtmlCommentAppender" type="HtmlCommentAppenderModule"/>
</modules>
<handlers>
<add name="SpringPageHandler" verb="*" path="*.aspx" type="Spring.Web.Support.PageHandlerFactory, Spring.Web"/>
</handlers>
</system.webServer>
<!-- demonstrates how to configure IHttpModule instances -->
<spring>
<context>
<resource uri="config://spring/objects"/>
</context>
<objects xmlns="http://www.springframework.net">
<!-- configure our custom HttpModule -->
<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>
</objects>
</spring>
</configuration>

View File

@@ -20,8 +20,11 @@
#region Imports
using System;
using System.Collections;
using System.Diagnostics;
using System.Web.UI;
using System.Web.UI.WebControls;
using Spring.Context;
using Spring.Util;
using Spring.Validation;
@@ -38,6 +41,7 @@ namespace Spring.Web.UI.Controls
public abstract class AbstractValidationControl : Control
{
private string provider;
private string validationContainerName;
private IValidationErrorsRenderer renderer;
#if !NET_2_0
@@ -70,6 +74,18 @@ namespace Spring.Web.UI.Controls
}
}
#endif
/// <summary>
/// If set, <see cref="ValidationContainer"/> will resolve to the named control specified
/// by this property. The behavior of name resolution is identical to
/// <see cref="System.Web.UI.WebControls.BaseValidator.ControlToValidate"/>, except that if the name
/// starts with "::", the resolution will start at the page level instead of relative to this
/// control
/// </summary>
public virtual string ValidationContainerName
{
get { return validationContainerName; }
set { validationContainerName = value; }
}
/// <summary>
/// Gets or sets the provider.
@@ -146,15 +162,34 @@ namespace Spring.Web.UI.Controls
{
get
{
// is an explicit container specified?
if (ValidationContainerName != null)
{
Control start = this.NamingContainer;
string containerName = this.ValidationContainerName;
// shall we do a global search?
if (containerName.StartsWith("::"))
{
containerName = containerName.Substring(2);
start = this.Page;
}
IValidationContainer container = start.FindControl(containerName) as IValidationContainer;
if (container == null)
{
throw new ArgumentException(string.Format("Validation Container Control specified by {0} does not exist or does not implement IValidationContainer", this.ValidationContainerName));
}
return container;
}
for (Control parent = this.Parent; parent != null; parent = parent.Parent)
{
IValidationContainer container = parent as IValidationContainer;
if (container != null)
if (container != null
&& container.ValidationErrors != null)
{
return container;
}
}
// throw new NotSupportedException(string.Format("Controls of Type {0} must be placed on a container control implementing IValidationContainer", this.GetType().Name));
return null;
}
}
@@ -201,7 +236,7 @@ namespace Spring.Web.UI.Controls
errorMessages = ResolveErrorMessages();
Renderer.RenderErrors(Page as Spring.Web.UI.Page, writer, errorMessages);
Renderer.RenderErrors(Page as Page, writer, errorMessages);
}
}
}

View File

@@ -676,7 +676,7 @@ namespace Spring.Web.UI
{
throw new ArgumentException( "Validator is not defined." );
}
result = validator.Validate( validationContext, contextParams, this.validationErrors ) && result;
result = validator.Validate( validationContext, contextParams, this.ValidationErrors ) && result;
}
return result;
@@ -837,7 +837,7 @@ namespace Spring.Web.UI
{
if (BindingManager.HasBindings)
{
BindingManager.BindTargetToSource( this, Controller, Page.ValidationErrors );
BindingManager.BindTargetToSource( this, Controller, this.ValidationErrors );
}
OnDataBound( EventArgs.Empty );
}
@@ -849,7 +849,7 @@ namespace Spring.Web.UI
{
if (BindingManager.HasBindings)
{
BindingManager.BindSourceToTarget( this, Controller, Page.ValidationErrors );
BindingManager.BindSourceToTarget( this, Controller, this.ValidationErrors );
}
OnDataUnbound( EventArgs.Empty );
}

View File

@@ -179,6 +179,80 @@ namespace Spring.Web.UI.Controls
Assert.AreEqual(messageSource, ctl.TheMessageSource);
}
[Test]
public void ReadsErrorsFromClosestValidationContainerByDefault()
{
// indirect containment
Spring.Web.UI.Page page = new Spring.Web.UI.Page();
TestValidationControl vc = new TestValidationControl();
Spring.Web.UI.UserControl inBetweenControl = new Spring.Web.UI.UserControl();
inBetweenControl.Controls.Add(vc);
page.Controls.Add(inBetweenControl);
Assert.AreSame(inBetweenControl, vc.TheValidationContainer);
}
[Test]
public void DoesNotReadErrorsFromCrossValidationContainerByDefault()
{
// containment tree:
//
// Page : IValidationContainer
// - outerVC
// - inBetweenControl1 : IValidationContainer
// - innerVC
Spring.Web.UI.Page page = new Spring.Web.UI.Page();
Spring.Web.UI.UserControl inBetweenControl1 = new Spring.Web.UI.UserControl();
inBetweenControl1.ID = "InBetweenControl1";
TestValidationControl innerVC = new TestValidationControl();
inBetweenControl1.Controls.Add(innerVC);
page.Controls.Add(inBetweenControl1);
TestValidationControl outerVC = new TestValidationControl();
page.Controls.Add(outerVC);
inBetweenControl1.ValidationErrors.AddError("provider", new ErrorMessage("msg"));
Assert.AreSame(inBetweenControl1, innerVC.TheValidationContainer);
Assert.AreSame(page, outerVC.TheValidationContainer);
Assert.AreEqual(1, innerVC.TheValidationContainer.ValidationErrors.GetErrors("provider").Count);
Assert.AreEqual(0, outerVC.TheValidationContainer.ValidationErrors.GetErrors("provider").Count);
}
[Test]
public void CanReadErrorsFromCrossValidationContainerByDefault()
{
// containment tree:
//
// Page : IValidationContainer
// - outerVC
// - inBetweenControl1 : IValidationContainer
// - innerVC
Spring.Web.UI.Page page = new Spring.Web.UI.Page();
Spring.Web.UI.UserControl userControl = new Spring.Web.UI.UserControl();
userControl.ID = "userControl";
TestValidationControl innerVC = new TestValidationControl();
userControl.Controls.Add(innerVC);
page.Controls.Add(userControl);
TestValidationControl outerVC = new TestValidationControl();
page.Controls.Add(outerVC);
userControl.ValidationErrors.AddError("provider", new ErrorMessage("msg"));
Assert.AreSame(userControl, innerVC.TheValidationContainer);
Assert.AreSame(page, outerVC.TheValidationContainer);
Assert.AreEqual(1, innerVC.TheValidationContainer.ValidationErrors.GetErrors("provider").Count);
Assert.AreEqual(0, outerVC.TheValidationContainer.ValidationErrors.GetErrors("provider").Count);
// test local-relative name resolution
outerVC.ValidationContainerName = "userControl";
Assert.AreEqual(1, outerVC.TheValidationContainer.ValidationErrors.GetErrors("provider").Count);
// test global name resolution
outerVC.ValidationContainerName = "::userControl";
Assert.AreEqual(1, outerVC.TheValidationContainer.ValidationErrors.GetErrors("provider").Count);
}
#region TestValidationControl
private class TestValidationControl : AbstractValidationControl