Minimize coding for validation in C# - c#

I'm trying to validate a set of Textboxes in a winform application.
if(string.IsNullOrEmpty(txtCarbohydrate.Text){
//todo
}
but there are several Textboxes in my form to be validated for emptiness , not only in the current win form but other forms too. how can i create a method or a class that can validate several Textboxes and
be reusable across application?
EDIT : i wrote somthing like this , any suggestion to make it better ?
class ValidateEmpty
{
bool res = false;
//List<object> txt = new List<object>();
List<string> st = new List<string>();
public List<string> St
{
get { return st; }
set { st = value; }
}
public ValidateEmpty(List<string> _str)
{
this.st = _str;
}
public bool checkEmpty()
{
bool res = false;
for (int i = 0; i < St.Count(); i++ )
{
if(string.IsNullOrEmpty(St[i]))
{
res= true;
}
}
return res;
}
}
}
`

You could put them in a list and then loop through the list.
List<TextBox> TextBoxes=new List<TextBox>() {txtCarbohydrate, txtProtein, txtFat};
foreach(TextBox tb in TextBoxes)
{
if(String.IsNullOrEmpty(tb.Text)
{
//do something
}
}
Based on your edit, you want to return a Boolean (it's really hard to understand your code and what you're trying to accomplish, you need to be clear and concise!) to indicate if a TextBox was empty or not. Here's how you could create a method to do that...
public static bool IsThereAnEmptyTextBox(List<TextBox> textBoxes)
{
bool emptyfound=false;
foreach(TextBox tb in textboxes)
{
if(String.IsNullOrEmpty(tb.Text)
{
emptyfound=true;
}
}
return emptyfound;
}
You can call this function from any class, if you put this function in a Utility class or in a base class etc. If you want to combine it with paqogomez's answer you can call it from a form like this...
bool emptyfound=MyUtilities.IsThereAnEmptyTextBox(myForm.Controls.OfType<TextBox>().ToList());
I think this is a terrible way of going about it, but I'm trying to demonstrate how you could do what you've asked for.

To grab all textboxes in a single form use Controls.OfType<T>
var controls = myForm.Controls.OfType<TextBox>();
foreach(TextBox tb in controls)
{
//do validation
}
Depending on the type of validation you are doing, you can also do as #RandRandom suggests and put the required attribute on your textboxes. This will force the user to put text in before its submitted.

Related

Creating a class that loops through textboxes and labels making them visible in winform

I am very new to c# and visual studio.
I am using c# with Visual studio. I want to create a method that lops through a number of textboxes and labels and set their visible control to "True."
This is the code I have come up with so far, but it does not work.
public static void showFields(params string[] values)
{
foreach (var value in values)
{
value.Visible = true;
}
}
Any help would be greatly appreciated.
You are on the right path, just need to replace string with Control, by the way, string does not have the Visible property.
public static void showFields(params Control[] values)
{
foreach (var value in values)
{
value.Visible = true;
}
}
Code should be similar to this. You may have nested controls. In this case, you create a recursive method
private void MakeThemVisible(Control topControl)
{
foreach (var c in topControl.Controls)
{
if (c is TextBox txt && <your second criteria>) // <-- pattern matching
{
// <---- txt specific code here -------
txt.Visible = true;
continue;
}
else if (c is Label lbl && <your second criteria>) // <-- pattern matching
{
// <---- lbl specific code here -------
lbl.Visible = true;
continue;
}
MakeThemVisible(c);
}
}
Your form is also control
If you already have a list of needed controls in the form of array - Control[] values, you can use LINQ
values.ToList().ForEach(c => {c.Visible = true;});

Retrieve column of Listview items and display in a comboBox on another form c#

I have 2 forms: 1 form contains a listView and another form contains a comboBox.
I would like the first column of the listView to be loaded into the comboBox on the second form.
This is my attempt:
comboBox1.Items.Add(Form2.listView2.columnHeader1);
However, this does not work. (Form2.ListView is inaccessible due to its protection level). Suggestions would be appreciated.
Quick and dirty solution:
Go to the Properties of listView2 in the winforms designer and look for Modifiers. Then select Public like you see in the picture below:
And now it will be accessible
The more elegant solution:
Create a property in your first Form which has only a getter. In this getter you can safely return the columnHeader1:
public ColumnHeader ColumnHeader { get { return this.listView1.Columns["columnHeader1"]; }}
or:
public ColumnHeader ColumnHeader { get { return this.columnHeader1; }}
EDIT:
It seems that you rather would like to have all values from that column. So in this case you would have to return all the values, which can be done like this:
public List<string> AlllValuesFromColumn
{
get
{
int indexOfColumn = listView2.Columns.IndexOf(this.columnHeader1);
return listView2.Items.OfType<ListViewItem>().Select(x => x.SubItems[indexOfColumn].Text).ToList();
}
}
To Add all the values in one blow to the ComboBox you can use AddRange:
comboBox1.Items.AddRange(Form2.AlllValuesFromColumn.ToArray());
EDIT 2:
But the solution I personally would prefer is to hold the data source in an extra variable. This can be passed around. No magic there.
Make a public method in your second form and let the method set the comboboxitems.
Form1:
bool Do = true;
int i = 0;
Form2 F = new Form2();
while (Do)
{
try
{
F.AddItem(listView1.Columns[i].Name);
i++;
}
catch
{
Do = false;
}
}
Form2:
public void AddItem(string ToAdd)
{
comboBox1.Items.Add(ToAdd);
}

C# Windows Form search txt file for passed data and pass to textboxes

I'm relatively new to C# and have spent an inordinate amount of time trying to figure this out myself with no luck. Hoping you guys can help.
I have 2 windows forms. In the first form, the user enters a citation number. I want to take that citation number, search for it in an external text file, and then return all of the data in the row for that citation into separate textboxes.
The text file looks something like this:
S8729936 , 6JXV123 , 10/1/2015 , 10/31/2015 , PAID , 49.5
A7472601 , 2NXP234 , 10/12/2015 , 11/11/2015 , UNPAID , 99
W2041810 , 5JPB345 , 10/19/2015 , 11/18/2015 , UNPAID , 99
And the second form has 6 textboxes. I have it so that the citation number, let's say S8729936 is passed into the first textbox, but I cannot seem to figure out how to then search the text file for S8729936 and give me the rest of the data in the row inside the textboxes.
Here are some examples of things I've tried. I've been copying and pasting and then messing with code all day, so if the details don't seem to match, that's probably the reason.
public Form2(string citation)
{
InitializeComponent();
txtCitation2.Text = citation;
const string FILENAME = #"\Path\ProjectData.txt";
FileStream fsInFile = new FileStream(FILENAME, FileMode.Open, FileAccess.Read);
StreamReader srReader = new StreamReader(fsInFile);
const char CH_DELIM = ',';
string strRecordIn;
string[] strFields;
if (strFields != null)
{
strRecordIn = srReader.ReadLine();
strFields = strRecordIn.Split(CH_DELIM);
txtLicense2.Text = strFields[1];
}
strRecordIn = srReader.ReadLine();
srReader.Close();
fsInFile.Close();
}
No luck there, how about something along the lines of this:
string whole_file = File.ReadAllText(#"Path\ProjectData.txt");
whole_file = whole_file.Replace('\n', '\r');
string[] lines = whole_file.Split(new char[] { '\r' });
int num_rows = lines.Length;
int num_cols = lines[0].Split(',').Length;
string[,] values = new string[num_rows, num_cols];
for (int r = 0; r < num_rows; r++)
{
string[] line_r = lines[r].Split(',');
for (int c = 0; c < num_cols; c++)
{
values[r, c] = line_r[c];
}
}
txtLicense2.Text = lines[1];
Nope. Maybe something along the lines of this:
const string FILENAME = #"C:\Users\rfranklin\Documents\ProjectData.txt";
FileStream fsinfile = new FileStream(FILENAME, FileMode.Open, FileAccess.Read);
StreamReader srReader = new StreamReader(fsinfile);
const string CH_DELIM = " ,";
string strRecordIn;
string[] strFields = new string[10];
string citnum = citation;
bool found = false;
strRecordIn = srReader.ReadLine();
foreach(string x in strFields)
{
if (x == citation)
{
found = true;
break;
}
}
if (found)
{
txtLicense2.Text = strFields[1];
}
Still no luck. And on and on. It seems as though I'm mostly missing how to tell the program what to search for and I am not sure what else to do. Like I said, I've been Googling various ways to do it all day, but I can't seem to make anything work right.
I'm doing this in Visual Studio 2013, if that helps.
Any help would be immensely appreciated. Thanks.
If the number of lines in the CSV file is not too large (I wouldn't know what "too large" is), then you could leverage a few .NET constructs, such as Data Binding and Linq to achieve this.
For starters, I would create a class that implements INotifyPropertyChanged:
namespace Citations
{
public class Citation : INotifyPropertyChanged
{
public static Citation ParseLine(string line)
{
Citation cit = new Citation();
if (string.IsNullOrEmpty(line))
throw new ArgumentException("Invalid argument", nameof(line));
string[] vals = line.Split(',');
if (vals.Length != 6)
throw new ArgumentOutOfRangeException(nameof(line), "Invalid format");
cit.CitationNumber = vals[0].Trim();
cit.PlateNumber = vals[1].Trim();
cit.DateCreated = DateTime.Parse(vals[2].Trim());
cit.DateExpired = DateTime.Parse(vals[3].Trim());
cit.Status = vals[4].Trim();
cit.Amount = Decimal.Parse(vals[5].Trim());
return cit;
}
void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
}
public event PropertyChangedEventHandler PropertyChanged;
string citationNumber;
public string CitationNumber
{
get
{
return citationNumber;
}
set
{
if (citationNumber != value)
{
citationNumber = value;
RaisePropertyChanged(nameof(CitationNumber));
}
}
}
string plateNumber;
public string PlateNumber
{
get
{
return plateNumber;
}
set
{
if (plateNumber != value)
{
plateNumber = value;
RaisePropertyChanged(nameof(PlateNumber));
}
}
}
DateTime dateCreated;
public DateTime DateCreated
{
get
{
return dateCreated;
}
set
{
if (dateCreated != value)
{
dateCreated = value;
RaisePropertyChanged(nameof(DateCreated));
}
}
}
DateTime dateExpired;
public DateTime DateExpired
{
get
{
return dateExpired;
}
set
{
if (dateExpired != value)
{
dateExpired = value;
RaisePropertyChanged(nameof(DateExpired));
}
}
}
string status;
public string Status
{
get
{
return status;
}
set
{
if (status != value)
{
status = value;
RaisePropertyChanged(nameof(Status));
}
}
}
Decimal amount;
public Decimal Amount
{
get
{
return amount;
}
set
{
if (amount != value)
{
amount = value;
RaisePropertyChanged(nameof(Amount));
}
}
}
}
}
This class is responsible for splitting a line from the CSV file, and converting it to an object of type Citation, which will be used to data bind the textboxes in Form2 later on.
Then in the first Form, I would simply read the file, and using some Linq operators, convert the file to a Dictionary of Citation objects, the key of the Dictionary being the Citation number:
private void button1_Click(object sender, EventArgs e)
{
string[] allLines = File.ReadAllLines("Citations.csv");
Dictionary<string, Citation> dict = allLines.Select(l => Citation.ParseLine(l)).ToDictionary(c => c.CitationNumber, k => k);
Citation cit = dict["W2041810"];
Form2 frm2 = new Form2();
frm2.SetCitation(cit);
frm2.ShowDialog();
}
In the code above, we're using the ToDictionary Linq operator to create a Disctionary from your Citation objects, and the Dictionat key is the Citation number.
Here I'm hardcoding one of the citations for lookup and passing to Form2, which would have a SetCitation method like this:
public void SetCitation(Citation citation)
{
this.citationBindingSource.DataSource = citation;
}
The code to Form2 is a bit difficult to show because I have used the Form designer to setup the data binding for each TextBox, and if I wanted to show that, I'd basically have to show the whole Form2.Designer.cs file.
Instead, I propose to guide you through the process of creating a Project DataSource, then drag & drop the TextBoxes onto Form2 from the Data Sources dialog in Visual Studio.
So, after adding the Citation class to your solution, make sure to compile at leat once so that the "Add data source" wizard will pick that class up as a possible data source.
Then, make sure the Data Sources dialog is displayed by going to View > Other Windows > Data Sources (assuming Visual Studio 2015 here).
From the Data Sources dialog, click the "Add New Data Source button" tolaunch the Data SOurce Configuration Wizard. From the list of possible data sources, you will choose "Object":
Then click the Next button. From the next Wizard step, you will select the Citation class:
and then click the Finish button.
In the Data Sources dialog, you should now have something like this:
From this Data Sources dialog, you can now drag & drop the individual fields onto Form2, which should give you something like this:
You will also notice in the Component tray of Form2, a BindingSource object has been added:
Under the hood, Visual Studio will have set all the Data Binding for your Citation object properties to be displayed in the corresponding TextBox.
This is the "glue" that makes it possible to call Form2.SetCitation() with a Citation object, and have all the fields displayed where they should.
I know this is quite a mouthful to chew on, but believe me, once you understand the principles behind this, you will not want to go back to the kind of spagethi code that you started implementing (no offense, we've all been there).
If you would like me to clarify any specific section of my answer, just let me know, and I'll edit accordingly.
Cheers
A simple winForm version:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
List<string> matchedList = new List<string>();
public Form1(string citation)
{
InitializeComponent();
string filePath = #"C:\Users\rfranklin\Documents\ProjectData.txt";
string[] linesArr = File.ReadAllLines(filePath);
//find matches
foreach(string s in linesArr)
{
if(s.Contains(citation))
{
matchedList.Add(s); //matched
}
}
//output
foreach(string s in matchedList)
{
Console.WriteLine(s); //write to console
//or output to wherever you wish, eg.
//richTextBox.Text += s + "\n";
}
}
}
}
Note that Form1 needs to be called from somewhere else and take in citation argument. To test it standalone, change it to
public Form1()
{
InitializeComponent();
string citation = "S8729936";
The suggestion from #interceptwind did it for me. Inside the //output section of the code he provided, I basically just created a second array from the matched line, with the elements separated by the comma. The code looks like this:
//output
foreach (string s in matchedList)
{
string citationLine = s;
string[] lineData = citationLine.Split(',');
txtLicense2.Text = lineData[1];
txtIssued2.Text = lineData[2];
txtDue2.Text = lineData[3];
txtStatus2.Text = lineData[4];
txtAmount2.Text = lineData[5];
}
This allowed me to put the data in the textboxes I needed. Thank you all for the assistance!

Generic way to get the values from multiple controls

I have a Form with multiple different controls like ComboBox, TextBox and CheckBox. I am looking for a generic way to get values from these controls while looping over them.
For example, something like this:
foreach(Control control in controls)
{
values.Add(control.Value);
}
Is it possible or do I need to treat each control separately?
Try this:
Panel myPanel = this.Panel1;
List<string> values = new List<string>();
foreach (Control control in myPanel.Controls)
{
values.Add(control.Text);
}
But make sure you get only the controls you want. You can check the type just like
if(control is ComboBox)
{
// Do something
}
The Text solution is OK if every Control is a TextBox, but if you have some Label you'll end up with the text of the labels among the values, unless you fill your code with if's. A better solution could be to define a set of delegates that for each kind of Control return what is considered the value (e.g. Text for the TextBox and Checked for the CheckBox), put them in a dictionary, and use them to get the value for each control. The code could be something like this:
public delegate object GetControlValue(Control aCtrl);
private static Dictionary<Type, GetControlValue> _valDelegates;
public static Dictionary<Type, GetControlValue> ValDelegates
{
get
{
if (_valDelegates == null)
InitializeValDelegates();
return _valDelegates;
}
}
private static void InitializeValDelegates()
{
_valDelegates = new Dictionary<Type, GetControlValue>();
_valDelegates[typeof(TextBox)] = new GetControlValue(delegate(Control aCtrl)
{
return ((TextBox)aCtrl).Text;
});
_valDelegates[typeof(CheckBox)] = new GetControlValue(delegate(Control aCtrl)
{
return ((CheckBox)aCtrl).Checked;
});
// ... other controls
}
public static object GetValue(Control aCtrl)
{
GetControlValue aDel;
if (ValDelegates.TryGetValue(aCtrl.GetType(), out aDel))
return aDel(aCtrl);
else
return null;
}
Then you can write:
foreach (Control aCtrl in Controls)
{
object aVal = GetValue(aCtrl);
if (aVal != null)
values.Add(aVal);
}

How to reduce this IF-Else ladder in c#

This is the IF -Else ladder which I have created to focus first visible control on my form.According to the requirement any control can be hidden on the form.So i had to find first visible control and focus it.
if (ddlTranscriptionMethod.Visible)
{
ddlTranscriptionMethod.Focus();
}
else if (ddlSpeechRecognition.Visible)
{
ddlSpeechRecognition.Focus();
}
else if (!SliderControl1.SliderDisable)
{
SliderControl1.Focus();
}
else if (ddlESignature.Visible)
{
ddlESignature.Focus();
}
else
{
if (tblDistributionMethods.Visible)
{
if (chkViaFax.Visible)
{
chkViaFax.Focus();
}
else if (chkViaInterface.Visible)
{
chkViaInterface.Focus();
}
else if (chkViaPrint.Visible)
{
chkViaPrint.Focus();
}
else
{
chkViaSelfService.Focus();
}
}
}
Is there any other way of doing this. I thought using LINQ will hog the performance as i have to tranverse the whole page collection. I am deep on page which has masterpages.Please suggest.
I think your tree is good. This certainly looks like a logic tree that can be simplified, and you have a good sense of smell to be suspicious of it. However, it seems to be that the logic tree reflects what you need. The logic really is this convoluted, and this is the conditional framework that C# gives you to handle this situation. I don't think it can be improved.
If you had a simple list of controls that should have the focus, and you wanted to give focus to the first visible control in the list, you could do this:
(From c in ListOfControls
Where c.visible = true
Select c).First.Focus();
But, it appears you have some additional criteria, so that wouldn't work.
Two approaches:
Iterate controls and set focus if visible
Use TabIndex and set focus to first. Then focus should fall to first visible control
If you're just trying to focus the first visible control on the form, then I would replace the entire ladder with a single loop:
foreach (Control c in Controls)
{
if (c.Visible)
{
c.Focus();
break;
}
}
If you need to focus an inner control, use a recursive method:
bool FocusFirst(ControlCollection controls)
{
foreach (Control c in controls)
{
if (c.Visible)
{
c.Focus();
FocusFirst(c.Controls);
break;
}
}
}
You could return after you meet your criteria, for example:
if (ddlTranscriptionMethod.Visible)
{
ddlTranscriptionMethod.Focus();
return;
}
if (ddlSpeechRecognition.Visible)
{
ddlSpeechRecognition.Focus();
return;
}
etc..
You can iterate controls and set focus if visible. But I would suggest you to use state pattern for better code readability.
All your doing is setting the focus which is client-side functionality. I would personally do this in javascript (using jQuery). ASP.NET controls that aren't set to visible aren't rendered in the HTML, so you could look for the existence of those elements or there might be an easier way if you're just looking for the first visible, enabled control.
What about a jumpto seems like you could use that here.
When the list of items to evaluate is large (and sometimes it's very large), I try to separate the order of evaluation from the conditional logic, something like this:
List<WebControl> wcBasics = new List<WebControl>();
wcBasics.Add(ddlTranscriptionMethod);
wcBasics.Add(ddlSpeechRecognition);
wcBasics.Add(ddlESignature);
List<CheckBox> checks = new List<CheckBox>();
checks.Add(chkViaFax);
checks.Add(chkViaInterface);
checks.Add(chkViaPrint);
private void Focus()
{
foreach (WebControl c in wcBasics)
if (c.Visible) {
c.Focus();
return;
}
if (!tblDistributionMethods.Visible) return;
foreach (CheckBox chk in checks)
if (chk.Visible) {
chk.Focus();
return;
}
}
chkViaSelfService.Focus();
}
Here is a slightly different take on the problem. First, define an interface to represent controls which may or may not be focused:
public interface IFormControl
{
bool Focus();
}
Then create an implementation which handles the easy cases:
public class FormControl : IFormControl
{
private readonly Control _control;
public FormControl(Control control)
{
_control = control;
}
public bool Focus()
{
if(_control.Visible)
{
_control.Focus();
}
return _control.Visible;
}
}
And create another which handles the more difficult cases:
public class DependentFormControl : IFormControl
{
private readonly Control _control;
private readonly Func<bool> _prerequisite;
public DependentFormControl(Control control, Func<bool> prerequisite)
{
_control = control;
_prerequisite = prerequisite;
}
public bool Focus()
{
var focused = _prerequisite() && _control.Visible;
if(focused)
{
_control.Focus();
}
return focused;
}
}
Then, create an extension method which sets the focus on the first control in a sequence:
public static void FocusFirst(this IEnumerable<IFormControl> formControls)
{
var focused = false;
foreach(var formControl in formControls)
{
if(formControl.Focus())
{
break;
}
}
}
And finally, create the set of controls to be focused:
var controls = new FormControl[]
{
new FormControl(ddlTranscriptionMethod),
new FormControl(ddlSpeechRecognition),
new DependentFormControl(SliderControl1, () => !SliderControl1.SliderDisable),
new FormControl(ddlESignature),
new DependentFormControl(chkViaFax, () => tblDistributionMethods.Visible),
new DependentFormControl(chkViaInterface, () => tblDistributionMethods.Visible),
new DependentFormControl(chkViaPrint, () => tblDistributionMethods.Visible),
new DependentFormControl(chkViaSelfService, () => tblDistributionMethods.Visible)
};
controls.FocusFirst();
You can create the set of controls when your page loads and just call .FocusFirst() whenever necessary.
This is a classic state machine. By setting up a machine it can make the code easier to understand and maintain, though it may add to the total lines.
I won't get into the specifics of your code. By determining what state the user is operating in, you can programmatically change the differing values in an understandable specific state fashion. (Pseudo code below)
enum FormStates
{
Initial_View
Working_View
Edit_View
Shutdown_View
};
{ // Somewhere in code
switch (theCurrentState)
{
case Initial_View :
Control1.Enabled = true;
Control2.Enabled = true;
theCurrentState = Working_View;
break;
case Working_View
if (string.empty(Contro1.Text) == false)
{
Control2.Enabled = false;
Speachcontrol.Focus();
theCurrentState = Edit_view;
}
else // Control 2 is operational
{
Control1.Enabled = false;
SliderControl.Focus();
}
case Edit_View:
...
break;
break;
}
By organizing the code in logical steps, it makes it easier to add more states without jeopardizing an huge if/else.

Categories

Resources