|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
button validation preventing clicki know validation is normally not done on a button itself, but i have a good reason to do it in my app. this is not the OK or Cancel button, it's a button to select a file. when the user does this, the filename is inserted in a read-only text box. i can't apply the validation to the textbox itself because the errorProvider prevents the focus from shifting to the button (which is the only way to get the filename into the textbox). so if the button fails validation, the errorprovider pops up next to it, and i go to click the button but the first click does not fire any code. the second click works as expected. i've spent hours on this trying to get it to work, every variation of setting the focus to the button etc. i read most of the posts here about cancel button validation but this doesn't apply i think. i've pasted the code below for the validate and validated and btnOK_Click events. i appreciate any help that anyone can provide. cheers tim private void btn_ValidateRequiredField(object sender, System.ComponentModel.CancelEventArgs e) { // check that all required values are present Button btn = sender as Button; TextBox textbox = btn.Tag as TextBox; // this contains the filename if(textbox.Text == "") { e.Cancel = true; this.errorProvider1.SetError(btn, "Please select a file"); } } private void btn_Validated(object sender, System.EventArgs e) { this.errorProvider1.SetError(sender as Button, ""); } private void btnOK_Click(object sender, System.EventArgs e) { // focus each control to force validation foreach(Control c in this.panel1.Controls) c.Focus(); if(this.Validate()) { this.DialogResult = DialogResult.OK; this.Close(); } else this.DialogResult = DialogResult.None; } \\ email: tim at mackey dot ie // \\ blog: http://tim.mackey.ie // 67d0ebfec70e8db3 I would go about this quite differently. I've added my comments
in-line with your post below; I hope what I say can be of help. "Tim Mackey" <t**@scootasp.net> wrote in message news:<30hbueF2vqpi7U1@uni-berlin.de>... I have no problem with doing validation when a button is pressed. I do> hi, > i know validation is normally not done on a button itself, but i have a good > reason to do it in my app. this is not the OK or Cancel button, it's a > button to select a file. when the user does this, the filename is inserted > in a read-only text box. that for the Save button in my application. However, I would never have an actual Validating or Validated event handler for a button, but would embed the validation code in the button's Click event. More on that later. > i can't apply the validation to the textbox itself Then the button needs to have CausesValidation set to False so that> because the errorProvider prevents the focus from shifting to the button > (which is the only way to get the filename into the textbox). you can shift focus to it and click it even if the text box is not validating. However, if I understand you correctly the text box is filled only programmatically, never by the user, so "validating" doesn't strike me as the right description of what you're doing with this text box. > so if the button fails validation, the errorprovider pops up next to it, I'm going to make some assumptions here; I hope that they're right. Iassume that you're not really "validating" the text box contents in any complex way. After all, you filled it in, so it can't be malformed, can it? Probably what you want to ensure is that the text box has something in it, that the user did, indeed, select a file. There is, I think, a better way to go about this. Think about it in user terms: you're not really "validating" the button. What's to validate? The button can't be "invalid" or "valid", can it? Are you really "validating" the contents of the text box? The user didn't enter any data in it, so it can't contain junk. What you really want to know, before some critical event happens (such as the data is saved to the database) is that the user did choose a proper file. This has nothing to do with the button or the text box, except that the text box happens to be a convenient place to put the file name. What you're really after, I submit, is a method like this: private bool IsFileChoiceValid() { if (this.txtFileName.Text.Trim().Length == 0) { this.errorProvider1.SetError(this.btnSelectFile, "Please select a file"); } else { this.errorProvider1.SetError(this.btnSelectFile, ""); } } I had the error provider place the error against the button because that's what the user needs to manipulate, but one could just as easily put it against the text box if the effect is more pleasing. Now you can call this wherever you think it's appropriate to signal the user that something may be wrong. So, in your btnOK_Click() method, instead of saying if (this.Validate()) ... I would say, if (ValidateAllControls()) ... where private bool ValidateAllControls() { bool fileNameValid = IsFileChoiceValid(); bool otherThingValid = ... ; ... return fileNameValid && otherThingValid && ... ; } (By the way, you have to use individual boolean variables in ValidateAllControls() because otherwise McCarthy evaluation will stop validating controls as soon as the first invalid control is found, and only one error provider will flash, leading to the annoying effect of the user having to correct one problem only to be told about another. The way I've written it above, all of the offending fields will have icons flashing beside them.) > private void btn_ValidateRequiredField(object sender, The problem I have with the above is that I have no idea what> System.ComponentModel.CancelEventArgs e) > { > // check that all required values are present > Button btn = sender as Button; > TextBox textbox = btn.Tag as TextBox; // this contains the filename > if(textbox.Text == "") > { > e.Cancel = true; > this.errorProvider1.SetError(btn, "Please select a file"); > } > } canceling a Validating event on a button does. Does it prevent you from moving focus away from the button? That seems a little harsh on the poor user. After all, I may want to go on to fill in other parts of the form and then press the button and choose a file later. Why should I have to do it right now, just because I happened to move focus to the button? If you want to tell the user when they pressed the button but didn't choose a file that they will have to choose a file eventually, I would just do this: private void btn_Click(object sender, System.EventArgs e) { ... Do whatever the button does ... IsFileChoiceValid(); // Will flash an error icon but won't impede user } This will show the user that they didn't do the right thing, but won't force them to choose a file before moving on. Show quote > private void btnOK_Click(object sender, System.EventArgs e) Down here in the btnOK_Click, NOW you can force the user to correct> { > // focus each control to force validation > foreach(Control c in this.panel1.Controls) > c.Focus(); > > if(this.Validate()) > { > this.DialogResult = DialogResult.OK; > this.Close(); > } > else > this.DialogResult = DialogResult.None; > } ALL outstanding problems on the form: private void btnOK_Click(object sender, System.EventArgs e) { if (ValidateAllControls()) { this.DialogResult = DialogResult.OK; this.Close(); } else { this.DialogResult = DialogResult.None; } } I prefer this to going through the controls, changing focus to each one, because it makes my software more flexible: the method that determines that there's a problem and flashes the error icon isn't tied to any particular event, so I can do the check whenever it's convenient, and I can check them all at the end with relative ease. In the end, I think that all of this comes down to the user experience: the idea behind Validating and Validated is to stop the user from leaving a text box or other input control after having entered invalid data into it. Overuse of these events leads to forms that "trap" users in fields and won't let them do other things on the form, which can turn the form into an old-fashioned question-and-response style interaction, which defeats the purpose of having forms in the first place. For example, even my forms which use Validating sparingly have the annoying property that if I click on a text box and then decide that I really want to enter data in some other text box first, I can't get OUT of the text box I'm in until I type some valid stuff into it. How annoying! However, it seems that this was the way that Validating events were intended to work for text boxes. However, extending this kind of behaviour to buttons is going too far, IMHO. Not allowing me to shift focus away from a button until I press it and choose a file... all just because I tabbed past it... well, that's a bit much. I may want to fill in some other text boxes and make some choices on the form from information I have at my finger tips before I go surfing around for the file. Of course, when I press OK I must have finished filling in all of the information on the form, and THEN it's fair enough to stop me in my tracks and refuse to go on until I finish filling everything in. My quick rules (so far) are these: Text box: Has Validating events and will not allow the user to leave until there is valid data in the text box. Combo box: Never validate, because your program should never offer an invalid choice. If you have no choice but to offer an invalid choice (such as None), then validate as would a text box. Buttons: Never validate. Check boxes: Never validate. If it's not valid to check (or uncheck) them, then why are they enabled? Radio buttons: Never validate. If a choice isn't valid then it should be disabled or invisible. OK button: Validates every control individually (using a method as shown above), and then performs inter-field validation (all fields are syntactically valid in isolation, but do they make sense together?), and signals any fields with problems. If two fields have a relationship problem (e.g. A can't contain X if B contains Y) then I flash error icons beside both of them. Hi Bruce,
thanks a million for your knowledgable reply to my problem. your solution worked perfectly. i usually try to follow the standards (i.e. not try to invent my own validation) where possible but i see from your post that .Net validation is not ideal in every situation and shouldn't be implemented blindly. i noticed how annoying it was the way it locked users into a textbox, and also the one-at-a-time notification of errors. i ended up having 2 methods: IsFileChoiceValid and IsRequiredTextBoxValid. They follow the approach you gave of simply setting the errorProvider on the textbox, outside the standard .net validation events. For regular expression validation, i went with the built-in validation events, because they'll only trigger if the user enters in some bad input, and presumably they won't mind being locked into that field until they fix it. i've included my code below for reference. all my controls are generated dynamically so that added an extra element of fun to the code. i was able to get away with only using one boolean variable, as long as i evaluated the IsRequiredTextBoxValid() expression before ANDing it with the boolean var so that the expression gets evaluated regardless of the True or Falseness of the boolean var. oh the subtleties of compiler optimisation.. many thanks again for your help. tim private bool ValidateAllFields() { bool valid = true; foreach(Control c in this.panel1.Controls) { if(c is TextBox) { TextBox txt = c as TextBox; if(txt.Tag == "Required") // its a required field { if(txt.ReadOnly) // it's a file field textbox valid = IsFileChoiceValid(txt) && valid; // order of operands is important because False && (EXPR) will never evaluate EXPR else // its a regular text field valid = IsRequiredTextBoxValid(txt) && valid; } } } return valid; } private bool IsRequiredTextBoxValid(TextBox txt) { if(txt.Text.Trim().Length == 0) { this.errorProvider1.SetError(txt, "This textbox requires a value"); return false; } else { this.errorProvider1.SetError(txt, ""); return true; } } I get the feeling that Microsoft is still working out the kinks in the
whole Validating / ErrorProvider thing. The classes and methods they supply now seem to lead to some odd situations in which it's not at all clear how to give the user a pleasant experience. That's why I'd like to see a white paper or some samples from them of how to use these things in reasonably complex scenarios, in order to clear up misconceptions about how to use them. For example, I've noticed that ErrorProviders and TabControls (and TabPages) don't work together very well: you can have an ErrorProvider flashing on some control that currently isn't visible because it's on a tab page that isn't showing at the moment, but there's no way to highlight the tab page to indicate that the user should go there to find a problem. Obviously ErrorProviders were intended to be able to signal a number of errors on a number of fields at once: they're vast overkill for doing nothing more than indicating that field the user is currently in has a problem. However, the concept doesn't seem to have been carried through nicely to work in concert with other Windows Forms controls. (For my part, when the user clicks the OK button and the form fails to validate, I pop a message box saying something like, "You haven't filled the form in properly. Mouse over the little red circles for further details on what is wrong." This at least tells the user that they should go hunting for little red icons, and would lead most people to try clicking on tab pages to see if they could find them. Hokey, but I didn't see much other choice. |
|||||||||||||||||||||||