I have created server created forms using Telerik AJAX controls with a submit button that does a post (no click event). My forms use validators which work fine except they happen on the next post after the field validation error.
If I make an email field empty which the code logic catches and returns an error. Submit again and the email required field validator works to catch the error before the code. Now put valid data and submit and the last empty field validator error with appears again. Submit the good data a second time and it updates.
Also, if I produce my own validator error then the validators work fine. I have validator code for required checkboxes. If a missing check happens I call
ValidatorError.Display(Page, message);
which causes the subsequent
Page.Validate(FormEntryBase.VALIDATION_GROUP);
to properly validate required fields in current submit.
protected void Page_Load(object sender, EventArgs e) {
localValidationError = theForm.CustomValidateForm(Request.Form);
if (localValidationError) {
validateErrorList = form.validateErrorList;
// get local valiation error put into validation summary error list
if (validateErrorList != null) {
foreach (string message in validateErrorList) {
ValidatorError.Display(Page, message);
}
}
}
Page.Validate(FormEntryBase.VALIDATION_GROUP);
if (!Page.IsValid ) {
return;
}
Validator errors the code does not catch are not caught on an initial submit which lets bad data in.
I have another web form that uses the click method and only uses Page.IsValid which works fine.
Very strange behaviour, some sort life cycle problem probably.
With some luck, I figured out a solution by adding a click event in code. To add a click event, first I added the RadButton to my form creation code be accessed in the aspx.cs.
RadButton button = new RadButton();
button.ValidateRequestMode = ValidateRequestMode.Enabled;
button.ValidationGroup = VALIDATION_GROUP;
button.UseSubmitBehavior = true;
theForm.updateButton = button;
In the aspx.cs Page_Load I added the click method to the button.
protected void Page_Load(object sender, EventArgs e) {
...
theForm = group.GetJoinForm();
theForm.updateButton.Click += new EventHandler(UpdateButton_Click);
...
void UpdateButton_Click(object sender, EventArgs e) {
Page.Validate(FormEntryBase.VALIDATION_GROUP);
if (!Page.IsValid) {
return;
}
... most of page load code moved here
These three steps fixed my validate delay problems and all my forms now validate quite well.
Related
I have a little form with some text boxes and combo boxes, each with their own validating event which just fills the default value either 1 or 0 depending on the box when the user moves on to the next box without entering anything, however i also want to run all the validations when the user just clicks on the submit button directly.
private void Validating_Zero(object sender, CancelEventArgs e)
{
if (((TextBox_Pro)sender).Text == "")
{
((TextBox_Pro)sender).Text = "0";
}
}
private void Validating_One(object sender, CancelEventArgs e)
{
if (((TextBox_Pro)sender).Text == "")
{
((TextBox_Pro)sender).Text = "1";
}
}
private void Start_Validating(object sender, CancelEventArgs e)
{
}
Calling ValidateChildren() method of the form, cause all of the child controls of the form to validate their data.
You can also use the the other overload of the method which accepts a validation constraint, ValidateChildren(ValidationConstraints) to limit the validation to immediate child controls, enabled controls, tab stop controls, selectable controls, visible controls or all control.
To see an example of validation using Validating event and Error provider control and showing error summary and also read more about validation options in Windows Forms, take a look at these posts:
Validating user input / Give .NET controls status OK or NOK
Validation using Validating event and ErrorProvider - Show Error Summary
I am trying to follow the MSDN example code for the Control.Validating Event and to apply it to a bindingNavigator item instead of the textbox as in the example.
The code provided for the textbox validation event is shown below.
private void textBox1_Validating(object sender,
System.ComponentModel.CancelEventArgs e)
{
string errorMsg;
if(!ValidEmailAddress(textBox1.Text, out errorMsg))
{
// Cancel the event and select the text to be corrected by the user.
e.Cancel = true;
textBox1.Select(0, textBox1.Text.Length);
// Set the ErrorProvider error with the text to display.
this.errorProvider1.SetError(textBox1, errorMsg);
}
}
I too intend on validating texboxes however I have several and have already written the validation methods for them which handle the error messages. I would like these methods to be tested when the user selects an item (Arrow buttons / Save button / Add button) on the bindingNavidator.
I therefore have the code below to attempt this,
private void myBindingNavigator_Validating(object sender, CancelEventArgs e)
{
if (!IsPostCodeValid())
{
PostCodeTextBox.Focus();
e.Cancel = true;
}
}
I have the ItemClicked event for the bindingNavigator set to bindingNavigator.Focus() with the hope of initiating the Validating event. (myBindingNavigator.CausesValidation = true; has been declared on formLoad). However I feel this is where an infinite loop of focusing the Navigation bar follows? I have stepped through and no code is executed once it has locked on to the Navigation bar it simply won't let the user interact with the rest of the form to change and correct the error after the navigation bar has been locked onto.
I can provide any extra information required and test any suggestions to discover what is going on.
Thanks
When you work with BindingNavigator and you have put your controls in details mode on the form, to ensure save only valid data, you should write validation rules for your controls and also you should handle your binding navigator items yourself.
This way you even don't need to set AutoValidate Property of your form to annoying EnablePreventFocusChange and you can set it to be the friendly EnableAllowFocusChange and because you can't navigate or save anything when there is validation errors, then you can be sure only valid data will save in database.
To do so, follow these steps:
Step 1
Handle Validating event of child controls and set e.cancel = true when the value is not valid.
private void nameTextBox_Validating(object sender, CancelEventArgs e)
{
//Put validation logic here
if (this.nameTextBox.Text.Length < 10)
{
e.Cancel = true;
this.errorProvider1.SetError(this.nameTextBox, "Some Error");
}
else
{
this.errorProvider1.SetError(this.nameTextBox, "");
}
}
Step 2
Go to BindingNavigator properties, and set MoveFirstItem, MovePreviousItem, MoveNextItem, MoveLastItem, AddNewItem, DeleteItem properties To (none). Also from designer click on the text box that shows record number, it is bindingNavigatorPositionItem, and then set its ReadOnly property to true.
Step 3
For all buttons, including navigation buttons, add button, delete button, save button and other custom buttons, handle Click event and call ValidateChildren method of the container of your text boxes and check if ValidateChildren() doesn't return true, exit the method, else do the task that the method should do, for example:
private void toolStripButton1_Click(object sender, EventArgs e)
{
if (!this.ValidateChildren())
return;
//Put the logic for this button here
//MoveFirstItem: `this.myBindingSource.MoveFirst();`
//MovePreviousItem: `this.myBindingSource.MovePrevious();`
//MoveNextItem: `this.myBindingSource.MoveNext();`
//MoveLastItem: `this.myBindingSource.MoveLast();`
//AddNewItem: `this.myBindingSource.AddNew();`
//DeleteItem: `this.myBindingSource.RemoveCurrent();`
}
In my button click, I would like to cause the page validation to fail if it meets a certain criteria. The problem is that that Page.IsValid is read-only.
This is what I am trying in my button click:
protected override void CreateChildControls()
{
base.CreateChildControls(container);
MyBtn += MyBtn_Click;
MyBtn += MyBtn_Click2; // Cannot move this
}
protected void MyBtn_Click(object sender, System.EventArgs e)
{
CaptchaControl.Validate();
if (!CaptchaControl.IsValid)
{
Page.IsValid = false; // Error because read-only
// Stop before running MyBtn_Click2!
}
}
If my captcha fails validation, I want to return to the page immediately, before it starts running the 2nd click event. Any ideas on how to do this?
I would personally use a hidden field and mark it as required. Put a default value in it, and if your captcha fails, remove the value and revalidate the page.
if (!CaptchaControl.IsValid)
{
myHiddenField.Value = null;
Page.Validate();
}
If your myBtn2 control is using if (Page.IsValid) to execute code, the hidden required field should be empty and now invalid.
I have a custom error handler for an asp.net site.
void Application_Error(object sender, EventArgs e)
{
// Code that runs when an unhandled error occurs
//if (System.Configuration.ConfigurationManager.AppSettings["ProductionMode"] == "Yes")
#if (!DEBUG)
Server.Transfer("~\\GlobalExceptionHandler.aspx");
#endif
}
It works fine and dandy when retrieving exception information cause it'll just snag Server.getLastException() and email it on to me for review
However, I have some TextBoxes on the page and I'd like to send the value of those textboxes along with the email. Maybe it's not quite the DOM I'm looking for access to but instead those posted variables.
I tried looking at HttpContext.Current.Request.Form but it showed no keys.
So Does anyone know how to access the formvalues when globally catching an exception?
Accessing the Form Values
To access the form values in Global.Application_Error, you can simply use HttpContext.Current.Request.Form.
Here is a proof of concept, where a page immediately throws an exception on post(back) to hit the application error handler:
void Application_Error(object sender, EventArgs e)
{
var test = HttpContext.Current.Request.Form;
}
Setting a breakpoint on the assignment to test, then stepping over it (with F10) when it is hit, you can see that test is indeed set to the post(back)'s form-values collection.
Accessing the Postback Control Values
Alternatively, you can access the postback control values by adding them to the session on postback, for example...
// ************code behind
protected void TextBox1_TextChanged(object sender, EventArgs e)
{
Session["TextBox1"] = TextBox1.Text;
}
..., and accessing the session in the application error handler - for instance:
// ************Global.asax.cs
protected void Application_Error(object sender, EventArgs e)
{
// Use Session["TextBox1"].
}
A CodeVerge thread speaks to approach well - particularly Benson Yu's reply.
In one of my projects I need to build an ASP.NET page and some of the controls need to be created dynamically. These controls are added to the page by the code-behind class and they have some event-handlers added to them. Upon the PostBacks these event-handlers have a lot to do with what controls are then shown on the page. To cut the story short, this doesn't work for me and I don't seem to be able to figure this out.
So, as my project is quite involved, I decided to create a short example that doesn't work either but if you can tweak it so that it works, that would be great and I would then be able to apply your solution to my original problem.
The following example should dynamically create three buttons on a panel. When one of the buttons is pressed all of the buttons should be dynamically re-created except for the button that was pressed. In other words, just hide the button that the user presses and show the other two.
For your solution to be helpful you can't statically create the buttons and then use the Visible property (or drastically change the example in other ways) - you have to re-create all the button controls dynamically upon every PostBack (not necessarily in the event-handler though). This is not a trick-question - I really don't know how to do this. Thank you very much for your effort. Here is my short example:
From the Default.aspx file:
<body>
<form id="form1" runat="server">
<div>
<asp:Panel ID="ButtonsPanel" runat="server"></asp:Panel>
</div>
</form>
</body>
From the Default.aspx.cs code-behind file:
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DynamicControls
{
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
AddButtons();
}
protected void AddButtons()
{
var lastClick = (string) Session["ClickedButton"] ?? "";
ButtonsPanel.Controls.Clear();
if (!lastClick.Equals("1")) AddButtonControl("1");
if (!lastClick.Equals("2")) AddButtonControl("2");
if (!lastClick.Equals("3")) AddButtonControl("3");
}
protected void AddButtonControl(String id)
{
var button = new Button {Text = id};
button.Click += button_Click;
ButtonsPanel.Controls.Add(button);
}
private void button_Click(object sender, EventArgs e)
{
Session["ClickedButton"] = ((Button) sender).Text;
AddButtons();
}
}
}
My example shows the three buttons and when I click one of the buttons, the pressed button gets hidden. Seems to work; but after this first click, I have to click each button TWICE for it to get hidden. !?
I think that you have to provide the same ID for your buttons every time you add them like this for example (in first line of AddButtonControl method):
var button = new Button { Text = id , ID = id };
EDIT - My solution without using session:
public partial class _Default : Page
{
protected override void OnPreInit(EventArgs e)
{
base.OnPreInit(e);
AddButtons();
}
protected void AddButtons()
{
AddButtonControl("btn1", "1");
AddButtonControl("btn2", "2");
AddButtonControl("btn3", "3");
}
protected void AddButtonControl(string id, string text)
{
var button = new Button { Text = text, ID = id };
button.Click += button_Click;
ButtonsPanel.Controls.Add(button);
}
private void button_Click(object sender, EventArgs e)
{
foreach (Control control in ButtonsPanel.Controls)
control.Visible = !control.Equals(sender);
}
}
You need to make sure that your dynamic controls are being added during the Pre_Init event.
See here for the ASP.NET Page Lifecycle: http://msdn.microsoft.com/en-us/library/ms178472.aspx
When adding events you need to do it no later than the Page_Load method and they need to be added every single request, ie you should never wrap event assignment in a !IsPostBack.
You need to create dynamic controls ever single request as well. ViewState will not handle the recreation on your behalf.
One thing I notice is that when you click a button you are invoking AddButtons() twice, once in the Page_Load() and once in the button_Click() method. You should probably wrap the one in Page_Load() in an if (!IsPostBack) block.
if (!IsPostBack)
{
AddButtons();
}
AFAIK, creating of controls should not be placed in Page_Load but in Page_PreInit (ViewState and SessionState is loaded before Page_Load but after Page_PreInit).
With your problem, I would suggest to debug the AddButtons function to find out what exactly (and when) is stored in Session["ClickedButton"]. Then, you should be able to figure out the problem.
the controls that are added dynamically are not cached so this migth me one of your problems