Client Side Validation against Telerik Calendar Control

Topics: Developer Forum
Feb 14, 2007 at 8:29 AM
Hi Everyone,

I am attempting to apply validation on a UI against a control which is bound to an property on my business object which is decorated with validation attributes. The control is a Telerik Calendar Control, and i have a few problems:

1. If more that one instance of the control exists on the form which is bound against a validatable property, I get a "An item with the same key has already been added." argument exception (in ValidatorGenerator.FindAllControls) due to the control containing child controls with the same ID (but in different places in the control hierarchy), and the dictionary using the control's id (no matter where it resided) as the key.

2. Is there a way to set the initial value of the created required validators that gets generated on the page? Telerik advise:

Our date-picker control can work seamlessly with validation controls. You just need to have in mind that the DateInput control is never actually empty:

RequiredFieldValidator

In order to use RequiredFieldValidator to check if a date has been selected in the date-picker, you need to set the InitialValue property of the validator to match the MinDate of the date-picker control:


<radCln:RadDatePicker ID="RadDatePicker1" runat="server" MinDate="1980-01-01">
</radCln:RadDatePicker>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="RadDatePicker1"
ErrorMessage="RequiredFieldValidator" InitialValue="1980-01-01"></asp:RequiredFieldValidator>

The use of the other validation controls is like with any normal date-input controls.

Can you offer any help on these two issues?

Many Thanks - and keep up the great work!

Dan
Feb 14, 2007 at 3:40 PM
Can I just point out that part 1 of my issue is a more general problem - the ID's are not guaranteed to be unique in a page containing controls and child controls etc. Could your code not use the UniqueID or ClientID properties for the key in the dictionary instead?

Thanks,

Dan
Coordinator
Feb 15, 2007 at 11:11 AM
Dan
Have a go with the latest source http://www.codeplex.com/ValidationFramework/SourceControl/ListDownloadableCommits.aspx
1 ValidatorGenerator is now strongly typed to add validations for Controls.
The code is not finalized yet, still needs some more doco and testing, so it may change a little before it is included in the next release.
Hopefully this should fix your problems.
2 Have a go with the new ValidatorGenerator. It “should” work with the initial invalid value.

Let me know if you have any problems.
Feb 15, 2007 at 4:13 PM
Hi Simon - thanks for the update - it looks very promising!

I have a couple of issues:

1. The AddAssociation() and ServerValidateHanler() methods are mis-spelled ;)
2. I get an ArgumentNull exception when validating a business object that is valid:

System.NullReferenceException was unhandled by user code
Message="Object reference not set to an instance of an object."
Source="App_Code.kfk5eaqn"
StackTrace:
at (Object , Object[] )
at ValidationFramework.Reflection.FastInvokeHandler.Invoke(Object target, Object[] paramters)
at ValidationFramework.Reflection.PropertyDescriptor.GetValue(Object target)
at ValidationFramework.Web.ValidatorGenerator.ServerValidateHanler(IPropertyRule validatorRule)
at ValidationFramework.Web.ServerWebValidator.EvaluateIsValid()
at System.Web.UI.WebControls.BaseValidator.Validate()
at System.Web.UI.Page.Validate()
at System.Web.UI.Page.Validate(String validationGroup)
at System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument)
at System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument)
at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument)
at System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData)
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

i presume before i call GenerateValidators that i need to set the TypeToValidate string property? - I set it to the assemblyqualifiedname property of my b.o. type.

Keep up the great work!

All the best,

Dan
Coordinator
Feb 15, 2007 at 8:16 PM
Dan

I have fixed the spelling errors. Thanks for that.
Regarding the null reference exception. I suspect you have not attached to the TargetObjectRequired event of the ValidatorGenerator. If you have done this then ignore the following and email me your code. On the server side a target object is required to perform the validate. This target object should be what is currently being edited on the page. It is not possible for me interpret the state of this object so i need the developer to provide it for me. I have checked in code that will throw a more helpful exception if the developer is not providing me with the target object.
Have a look at the code in TraditionalForm2.aspx.
protected void ValidatorGenerator1_TargetObjectRequired(object sender, TargetObjectRequiredEventArgs e)
  {
    if (temppersonerson == null)
    {
      temppersonerson = new Person(false);
      temppersonerson.FirstName = firstNameTextBox.Text;
      temppersonerson.LastName = lastNameTextBox.Text;
      temppersonerson.EmailAddress = emailTextBox.Text;
      temppersonerson.CreditCardNumber = cardNumberTextBox.Text;
      temppersonerson.CardType = (CardType)Enum.Parse(typeof(CardType), ddlCardType.SelectedValue);
      int age;
      if (int.TryParse(ageTextBox.Text, out age))
      {
        temppersonerson.Age = age;
      }
      int orderTotal;
      if (int.TryParse(orderTotalTextBox.Text, out orderTotal))
      {
        temppersonerson.OrderTotal = age;
      }
    }
    else
    {
      e.TargetObject = temppersonerson;
    }
  }

note that because I am only editing one object on my page I can cache it in a variable.
Feb 16, 2007 at 9:20 AM
Edited Feb 16, 2007 at 9:35 AM
Hi Simon,

Thanks for the update. I tried your sample app using TraditionalForm2.aspx and unfortunately it failed because it looks like TargetObject property requires to be set and it isn't being (all the time).

My code for the TargetObjectRequired event is as follows:

void validator_TargetObjectRequired(object sender, TargetObjectRequiredEventArgs e)
{
if (this.DataItem != null)
{
e.TargetObject = this.DataItem;
}
}

My scenario I have is that my custom DetailsView control can bind against a business object of mine (decorated with validation attributes) and wired up via an ObjectDataSource. The problem is that I can't do the manual creation of a temporary business object at the DetailsView level because the business object is auto generated by the CodeDom using my own build provider, so at the control level I can't manually specify values for properties - but I hope the DateItem property is good enough!

I'm still however getting an exception (your new handled one - which is the same exception i get in your sample app), which occurs after my handled TargetObjectRequired event has fired:

InvalidOperationException: No target object could be found to validate. You must attach to ValidatorGenerator.TargetObjectRequired to provide a target object for server validates.
ValidationFramework.Web.ValidatorGenerator.ServerValidateHandler(IPropertyRule validatorRule) +335
ValidationFramework.Web.ServerWebValidator.EvaluateIsValid() +41
System.Web.UI.WebControls.BaseValidator.Validate() +86
System.Web.UI.Page.Validate() +133
System.Web.UI.Page.Validate(String validationGroup) +105
System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +81
System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +7
System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +11
System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +33
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +5102

Any help you can provide would be great.

Many Thanks,

Dan
Feb 16, 2007 at 11:06 AM
Edited Feb 16, 2007 at 11:10 AM
oops! a little bit of reflection trickery caused this all to work nicely :)

/// <summary>
/// Overriden to create a temporary version of the submitted object
/// which is then passed as the target object to the validation framework
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void validator_TargetObjectRequired(object sender, TargetObjectRequiredEventArgs e)
{
// Create an instance of the object based on the submitted form values

// Get the submitted form values
DetailsViewInsertEventArgs args = new DetailsViewInsertEventArgs("");
this.ExtractRowValues(args.Values, false, true);

// Create an instance of the business object and populate it's values
// ...using the dictionary object returned from ExtractRowValues
object o = Activator.CreateInstance(dataItemType);
foreach (string key in args.Values.Keys)
{
dataItemType.InvokeMember(key, BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.Instance, null, o, new object[] { args.Valueskey });
}

// Set the target object to be the submitted object
e.TargetObject = o;
}

Like I have said before, Keep up the great work Simon - this is an absolutely amazing piece of development, which has enabled me to pass on validation rules from a third party data schema and allow them to be displayed correctly on the UI, with no user/web developer modifications required!

Cheers,

Dan
Coordinator
Feb 16, 2007 at 12:21 PM
Dan

Good to see you worked it out.

I have made a few more changes to the samples. They should work now.

I also added a “Mode” property to the ValidatorGenerator. This allows you to control whether client, server or both kind of validators get created. So if you come across the scenario where you cannot provide an object at validate time set the Mode to Client. This will cause the javascript validators to be created but none of the server validators will be created. This means you do not need to attach to TargetObjectRequired. You will however be responsible for validating the object at a lower level, and propagating those errors back up to the user.
You should also note that the TargetObjectRequiredEventArgs class provides you with all the controls that have web validators attached. See the property ControlsToValidate. This may help with your reflection.

I would also appreciate your opinion on the aspx samples. Do you think they are following best practise? Is there any way they can be made more efficient? etc
Feb 19, 2007 at 2:29 PM
Hi Simon,

I don't really feel qualified to advise you on best practice ;)

Seriously though, one thing I would have found more useful for a quick sample is to have the code behind in the same file as the ASPX - this doesn't follow best practice i know, however it means that all the required 'stuff' for getting a feature of your framework to function is immediately obvious in a single file - my humble opinion :)

Also since you asked, the code behind could do with some comments to explain why the code is required, but this is a free framework which I wouldn't expect that level of service!

Keep up the good work,

Dan
Mar 13, 2007 at 1:55 PM
Hi Simon,

After doing some more stuff on this, whilst the server side validation works for the telerik calendar, the client side does not - a postback is required before the message will appear. I am sure this is down to the initial value of the telerik control - which is always set to '0001-01-01'. Is there a way of me setting the initial value in my validation attribute or validation association?

Many Thanks,

Dan
Coordinator
Mar 15, 2007 at 12:37 PM
Dan
I installed the telerik controls and got this working. Turns out I was not using the correct format when populating the web validators. I have rectified the problem so it should work now if you get the latest source.

One gotcha is that the MinDate of the telerik date picket should match the date generated by the RequiredDateTimeRuleAttribute. So since, by default, the RequiredDateTimeRuleAttribute generates “0001-01-01” the raddatepicker should appear as follows
<radcln:raddatepicker id="RadDatePicker1" runat="server" MinDate="0001-01-01">

So get the latest source and let me know how you go. Hopefully the other changes I have been making don’t interfere with what you are doing.

Also note that if the property is a Nullable<DateTime> you will have to set the initial value of RequiredDateTimeRuleAttribute to “01 Jan 0001”.

BTW I got most of the info off http://www.telerik.com/demos/aspnet/calendar/examples/datepicker/validation/defaultcs.aspx
Mar 19, 2007 at 11:39 AM
Thanks Simon - yup i saw that page on the Telerik site, and checked that validation was possible prior to posting.

With your most recent code, it now works. Nice work!

Cheers,

Dan