6

I have the following code:

private void btnOK_Click(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(tbVendorName.Text))
        {
            VendorName = tbVendorName.Text;
            if (!string.IsNullOrEmpty(rtbVendorAddress.Text))
            {
                VendorAddress = rtbVendorAddress.Text;
                if (!string.IsNullOrEmpty(tbVendorEmail.Text))
                {
                    VendorEmail = tbVendorEmail.Text;
                    if (!string.IsNullOrEmpty(tbVendorWebsite.Text))
                    {
                        VendorWebsite = tbVendorWebsite.Text;
                        this.Close();
                    }
                    else
                    {
                        MessageBox.Show("Vendor Website Required");
                    }
                }
                else
                {
                    MessageBox.Show("Vendor email is required");
                }
            }
            else
            {
                MessageBox.Show("Vendor address is required");
            }
        }
        else
        {
            MessageBox.Show("Vendor name is required");
        }
    }

But it just looks horrible. Is there a better way? Or even an alternative methodology which makes the code more readable?

3

14 Answers 14

8

The better way is to master the MVVM pattern. There you could create a ViewModel and define all input data there:

class VendorViewModel
{
    [Required]
    public string Name { get; set; }

    public string Website { get; set; }

    [Regex("regex for email")]
    public string Email { get; set; }

    [MaxLength(160)]
    public string Address { get; set; }
}

Then the framework will show input errors (if any) after each textbox or any other input element. Enable property of the button will be automatically set according of all fields validated correctly.

Sign up to request clarification or add additional context in comments.

3 Comments

Looks like this can be done easily in WPF, but what if the OP uses Winforms?
By the way, I wouldn't say attribute-based validation should be something tied to MVVM. There're various validation frameworks working this way and they can be used in any layer and technology...
@KingKing then it is time for a wind of change.
3

Put it in a method and return from the method a boolean as to valid or not.

 private void btnOK_Click(object sender, EventArgs e)
{
    if(!Validate())
    {
        //Invalid
    }
    //Valid so set details
}

private bool Validate()
{
     if (!string.IsNullOrEmpty(tbVendorName.Text))
     {
          MessageBox.Show(...);
        return false;
     }

    return true;
}

1 Comment

Instead of returning the bool,we can use an Enum that lists all the error.Based on the enum returned we can display the error message.
2

I'd build a dedicated method like this for this task:

private bool ValidateInput()
{
    bool ret = true;

    // List all the inputs you want, add a description for each one
    List<KeyValuePair<<string,string>) inputs = new Dictionary<string,string>();

    inputs.Add(new KeyValuePair<string,string>(tbVendorName.Text, "Vendor Name"));
    inputs.Add(new KeyValuePair<string,string>(tbVendorAddress.Text, "Vendor Address"));
    // .. and so on and so forth

    // Build a list of the empty ones 
    if(inputs.Any(i => string.IsNullOrEmpty(i.Key))
    {
        var msg = string.Join(", ", inputs.Where(i => string.IsNullOrEmpty(i.Key));
        MessageBox.Show(string.Format("The following inputs are required: {0}", msg);
        ret = false;
    }

    return ret;
}

Easy to read and it's immediately clear what the method does, while being quite compact. Also, the list of the fields might be moved out of the method if needed and become a class field (just pass the list as a parameter and you're all set). Many approaches are sound here.

usage:

private void btnOK_Click(object sender, EventArgs e)
{
    if (ValidateInput())
    {
        // They're all filled!
    }   
    else
    {
        // Something was missing.
    }
}

Comments

2

One pattern I've not seen in the answers here, and which I am fond of, is validation using yield return. It's not as clean as attribute based validation, but it's easy to customize to a lot of scenarios and works fine for WinForms projects.

It also returns all errors at once, not just one at a time.

private void btnOK_Click(object sender, EventArgs e)
{
    var errors = Validate();

    if (errors.Any())
    {
        var sb = new StringBuilder();
        sb.AppendLine("The following errors were found:");
        foreach (var error in errors)
        {
            sb.AppendLine(error);
        }
        MessageBox.Show(sb.ToString());
        return;
    }
}

private IEnumerable<string> Validate()
{
    if (string.IsNullOrEmpty(tbVendorName.Text))
    {
        yield return "Vendor name missing";
    }
    if (string.IsNullOrEmpty(rtbVendorAddress.Text))
    {
        yield return "Vendor address missing";
    }
    if (string.IsNullOrEmpty(tbVendorEmail.Text))
    {
        yield return "Vendor email missing";
    }
}

Comments

1

If everything has to be ok to proceed. You could do it like this:

private void btnOK_Click(object sender, EventArgs e)
{



   if (!string.IsNullOrEmpty(tbVendorEmail.Text))
       {
          VendorEmail = tbVendorEmail.Text;


       }
       else
       {
           MessageBox.Show("Vendor email is required");
       }
    if (!string.IsNullOrEmpty(tbVendorName.Text))
    {
        VendorName = tbVendorName.Text;

    }
    else
    {
        MessageBox.Show("Vendor name is required");
    }
    if (!string.IsNullOrEmpty(rtbVendorAddress.Text))
    {
        VendorAddress = rtbVendorAddress.Text;

    }
    else
    {
        MessageBox.Show("Vendor address is required");
    }

    if (!string.IsNullOrEmpty(tbVendorWebsite.Text))
        {
           VendorWebsite = tbVendorWebsite.Text;
           this.Close();
        }
        else
        {
        MessageBox.Show("Vendor Website Required");
        }


}

Dunno what the this.Close does but you can use a boolean to check if it has to be closed. Like:

If(boolean=true)
{
   this.Close();
}

and then set the boolean true when everything is OK

There must be an easier way, but I have no idea how.

2 Comments

Bigger than what OP wants to optimize.
@user1671639 I know but he wants to make it look better?
1

I believe User Input validation should be done with tooltips appearing next to textbox, or other controls, whenever these loose focus. There are much validation frameworks. If you are using simple WPF here one good example : http://www.codeproject.com/Articles/15239/Validation-in-Windows-Presentation-Foundation for WPF with MVVM : http://www.codeproject.com/Articles/97564/Attributes-based-Validation-in-a-WPF-MVVM-Applicat for Win forms look at this http://www.codeproject.com/Articles/10093/Validators-for-Windows-Forms-ValidationProvider-Co

Comments

1

If you want to do it with regular if, you can make use of LINQ extension methods:

bool valid = new [] { tbVendorName, rtbVendorAddress, tbVendorEmail, tbVendorWebsite }
                    .All(textBox => !string.IsNullOrEmpty(textBox.Text));

if(valid) 
{
    VendorName = tbVendorName.Text;          
    VendorAddress = rtbVendorAddress.Text;
    VendorEmail = tbVendorEmail.Text;
    VendorWebsite = tbVendorWebsite.Text;
}

The .All(...) extension method will determine if the whole boolean expression is true for all items in the IEnumerable<T>.

Also, if you want to have accurate results about what's not valid, you can use the specification pattern:

public interface ISpecification<TObject>
{
     // This holds the required field names
     IList<string> RequiredFields { get; }

     bool IsSatisfiedBy(TObject input);
}


public interface TextBoxSpecification : ISpecification<TextBox>
{
    // This holds a relation between field names and their display name
    private readonly IDictionary<string, string> _fieldMapping = new Dictionary<string, string> 
    {
        { "tbVendorName", "Vendor name" },
        { "rtbVendorAddress", "Vendor address" },
        { "tbVendorEmail", "Vendor email" },
        { "tbVendorWebsite", "Vendor Web site" }
    };

      private readonly IList<string> _requiredFields = new List<string>();

      private IList<string> RequiredFields { get { return _brokenRules; } }
      private IDictionary<string, string> { get { return _fieldMapping; } }

      public bool IsSatisfiedBy(TextBox input)
      {
          bool valid = true;

          // If the condition isn't satisfied, the invalid field's diplay name is
          // added to RequiredFields
          if(!string.IsNullOrEmpty(input)) 
          {
              valid = false;                  
              RequiredFields.Add(FieldMapping[input.Name]);
          }

          return valid;
      }
}

Now your event handler with validation will look like this:

// Instantiating the specification.
ISpecification<TextBox> textBoxSpec = new TextBoxSpecification();

// Now, instead of just checking if it's not null or empty, the .All(...)
// extension method will execute the specification for all text boxes
bool valid = new [] { tbVendorName, rtbVendorAddress, tbVendorEmail, tbVendorWebsite }
                    .All(textBox => textBoxSpec.IsSatisfiedBy(textBox));

// If all specification were satisfied, you can assign the whole properties
if(valid) 
{
    VendorName = tbVendorName.Text;          
    VendorAddress = rtbVendorAddress.Text;
    VendorEmail = tbVendorEmail.Text;
    VendorWebsite = tbVendorWebsite.Text;
}
else
{
     // If not, generates a message box with a comma-separated 
     // list of required fields!
     MessageBox.Show
     (
            string.Format
            (
                  "The following fields are required: {0}",
                  textBoxSpec.RequiredFields.ToArray().Join(", ")
            )
     );  
}

Learn more about specification pattern on Wikipedia.

4 Comments

How do you match the messages to show accordingly? That's part of the problem.
@KingKing What do you mean? The MessageBox will show which fields didn't pass the whole spec.
My comment wasn't against the code you updated, the code you wrote before didn't care about specific message to show.
@KingKing Ah! Ok, no problem :) Now you know we had the same concern, that's why I tried to show the specification pattern approach to the OP.
1
private void btnOK_Click(object sender, EventArgs e)
        {
            var box = Controls.OfType<TextBox>().Any(x => !Validate(x));
            if (!box)
            {
                VendorName = tbVendorName.Text;
                VendorAddress = rtbVendorAddress.Text;
                VendorEmail = tbVendorEmail.Text;
                VendorWebsite = tbVendorWebsite.Text;
            }
        }

         private bool Validate(TextBox box)
         {
             if (!String.IsNullOrEmpty(box.Text)) return true;
                 MessageBox.Show(@"vender " + new List<string> {"name","email","website","address"}
                .Single(x => box.Name.ToLower().Contains(x)) + @" is required!...");
           return false;
         }

Comments

1

You can control collection and check for it's type then apply the validation such as

foreach(Control c in this.Controls)
 {
      //Check if it's input control    
      if(c is TextBox)
      {
             if (string.IsNullOrEmpty(c.Text))
              {
                 MessageBox.Show("Value Required");
               }
      }
  }

4 Comments

c is a TextBox so it cant be null or empty. Nice approach though, it just depends if user has any other values that dont need checking this.Controls.OfType<TextBox>().Any(x => string.IsNullOrEmpty(x.Text))
Why is the message just Value Required?
@KingKing The message is irrelevant, "Not all values have been entered" may be enough for OP otherwise Tag maybe
@Sayse No, the OP wants specific message, and Yes, using Tag is a way, but we still have to initialize the Tag (It requires the same lines of code).
0

You can avoid writing a whole lot of javascript by just using Required Field validator and Regular expression validators. It will make ur life much simpler just google required field validator

Comments

0

You can write the general function which verifies values with IsNullOrEmpty and sets VendorName, VendorWebsite etc. or shows the error message.

Comments

0

While I would prefer a method which simply disallowed clicking "OK" until the necessary fields are entered, you could check the conditions up front if this is just a request to "pretty up" the code, i.e., reduce nesting.

if (string.IsNullOrEmpty(tbVendorName.Text)) 
{
    MessageBox.Show("Vendor name is required");
    return;
}
if (string.IsNullOrEmpty(rtbVendorAddress.Text)) 
{
    MessageBox.Show("Vendor address is required");
    return;
}
if (string.IsNullOrEmpty(tbVendorEmail.Text)) 
{
    MessageBox.Show("Vendor email is required");
    return;
}
if (string.IsNullOrEmpty(tbVendorWebsite.Text)) 
{
    MessageBox.Show("Vendor Website Required");
    return;
}

VendorName = tbVendorName.Text;          
VendorAddress = rtbVendorAddress.Text;
VendorEmail = tbVendorEmail.Text;
VendorWebsite = tbVendorWebsite.Text;

On a side note, you currently allow only white space (you don't do any sort of validation on format either), so you're better off using String.IsNullOrWhitespace()

2 Comments

you should add a return in your case for each if to display error at once and skip rest
@Alyafey: Derp, so I should have.
0

first thing that u have to do is to make a group of Validation types like one method for all type of Email validation which return type should be boolean. Whenever you want to validate any email field. just call to method. And if u want to check validation of more than one fields. just use following syntax.

if(Control1IsValid && Control2IsValid) {

}

Comments

0

Assuming that you would apply TabOrder on your (rich) text boxes and you would keep the error messages in their tag properties, you could do something like this:

private void btnOk_Click(object sender, EventArgs e)
{
    var emptyTb = this.Controls.OfType<TextBox>()
        .Cast<Control>()
        .Union(this.Controls.OfType<RichTextBox>()
        .Cast<Control>())
        .OrderBy(tb => tb.TabIndex)
        .FirstOrDefault(tb => string.IsNullOrEmpty(tb.Text));
    if (emptyTb != null)
    {
        MessageBox.Show(emptyTb.Tag.ToString());
    }
}

Note: There's some cost in performance, but the method's code would be easier to read and maintain.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.