How can I Validate Textboxes Values To Be Unique? - c#

I have 12 text boxes, and I am trying to find a strategy to not permit duplicate entries in the TextBoxes by the user at run-time.
List<string> lstTextBoxes = new List<string>();
private void Textbox1(object sender, EventArgs e)
{
lstTextBoxes.Add(Textbox1.Text);
}
public bool lstCheck(List<string> lstTextBoxes,string input)
{
if(lstTextBoxes.Contains(input))
{
return true;
}
else
{
return false;
}
}
private void Textbox2(object sender, EventArgs e)
{
lstTextBoxes.Add(Textbox2.Text);
if (lstCheck(lstTextBoxes, Textbox2.Text))
{
MessageBox.Show("test");
}
}

public bool CheckForDuplicates()
{
//Collect all your TextBox objects in a new list...
List<TextBox> textBoxes = new List<TextBox>
{
textBox1, textBox2, textBox3
};
//Use LINQ to count duplicates in the list...
int dupes = textBoxes.GroupBy(x => x.Text)
.Where(g => g.Count() > 1)
.Count();
//true if duplicates found, otherwise false
return dupes > 0;
}

There are many ways to accomplish this. I've chosen to present a solution as an extension method but you can achieve the same result by simply placing the method within the class containing the list of textboxes or passing the list as a parameter. Which ever you prefer.
Cut and paste following into your project. Make sure you put it within the same namespace, otherwise, add the containing namespace as a reference.
public static class TextBoxCollectionExtensions
{
/// <summary>
/// Extension method that determines whether the list of textboxes contains a text value.
/// (Optionally, you can pass in a list of textboxes or keep the method in the class that contains the local list of textboxes.
/// There is no right or wrong way to do this. Just preference and utility.)
/// </summary>
/// <param name="str">String to look for within the list.</param>
/// <returns>Returns true if found.</returns>
public static bool IsDuplicateText(this List<TextBox> textBoxes, string str)
{
//Just a note, the query has been spread out for readability and better understanding.
//It can be written Inline as well. ( ex. var result = someCollection.Where(someItem => someItem.Name == "some string").ToList(); )
//Using Lambda, query against the list of textboxes to see if any of them already contain the same string. If so, return true.
return textBoxes.AsQueryable() //Convert the IEnumerable collection to an IQueryable
.Any( //Returns true if the collection contains an element that meets the following condition.
textBoxRef => textBoxRef //The => operator separates the parameters to the method from it's statements in the method's body.
// (Word for word - See http://www.dotnetperls.com/lambda for a better explanation)
.Text.ToLower() == str.ToLower() //Check to see if the textBox.Text property matches the string parameter
// (We convert both to lowercase because the ASCII character 'A' is not the same as the ASCII character 'a')
); //Closes the ANY() statement
}
}
To use it, you do something like this:
//Initialize list of textboxes with test data for demonstration
List<TextBox> textBoxes = new List<TextBox>();
for (int i = 0; i < 12; i++)
{
//Initialize a textbox with a unique name and text data.
textBoxes.Add(new TextBox() { Name = "tbField" + i, Text = "Some Text " + i });
}
string newValue = "some value";
if (textBoxes.IsDuplicateText(newValue) == true) //String already exists
{
//Do something
}
else
{
//Do something else
}

Related

Custom Sorting Based on strings

I want to sort the rows in my DataGrid view based on the "Product Name" column value. So, for example, the possible values for the above mentioned column are : "Module","RX5000", "RM5000". If my gridview has 10 rows, and the "Product Name" column has the values in the following order:
RX5000
RM5000
RM5000
Module
RX5000
RX5000
RM5000
Module
RX5000
RM5000
After I click a button, I want them in the following order (displayed in the same gridview):
Module
Module
RX5000
RX5000
RX5000
RX5000
RM5000
RM5000
RM5000
RM5000
How do I obtain this in C#?
I thought of creating a list of each type of product and then going through each row and filling up the lists based on the product name and then somehow merging the 3 lists into a 4th one and then assigning the 4th list to my mergeview. But I have 12 products (I'm only showing 3 here) so I don't want to have 12 lists. I want it more dynamic.
private void dataGridView1_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
{
string cell1, cell2;
if (e.Column == "Your_Column")
{
if (e.CellValue1 == null) cell1 = "";
else cell1 = e.CellValue1.ToString();
if (e.CellValue2 == null) cell2 = "";
else cell2 = e.CellValue2.ToString();
if (cell1 == "Account Manager") { e.SortResult = -1; e.Handled = true; }
else
{
if (cell2 == "Account Manager") { e.SortResult = 1; e.Handled = true; }
else
{
if (cell1 == "Assistant Account Manager") { e.SortResult = -1; e.Handled = true; }
else
{
if (cell2 == "Assistant Account Manager") { e.SortResult = 1; e.Handled = true; }
}
}
}
}
}
I found the above code in a different Post but I don't know how to apply this to my problem or if it's even possible to apply it to mine.
The answer you found has the biggest part of the puzzle: you need to subscribe to and handle the DataGridView.SortCompare event. Unfortunately, the code you found there is very specific to that one particular scenario. It assumes that there is at most one of the "special" names, and it doesn't impose any particular ordering on the remaining ones.
In your example, it's not clear why you want "RX5000" be ordered such that it comes before "RM5000". A normal alphabetic sort would have those reversed. But, taking as an assumption that that deviation from a "normal alphabetic sort" is exactly your question, then all you need to do is define your desired order, and then refer to that in your SortCompare event handler.
For example, you might do something like this:
private readonly Dictionary<string, int> _sortOrder = new[] { "Module", "RX5000", "RM5000" }
.Select((s, i) => new KeyValuePair<string, int>(s, i))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
private void dataGridView1_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
{
e.SortResult = _sortOrder[e.CellValue1.ToString()].CompareTo(_sortOrder[e.CellValue2.ToString()]);
e.Handled = true;
}
The _sortOrder_ dictionary is constructed by enumerating the elements of an array, which you define as the strings in the order you want them to be sorted, and using the string values along with their index in the array to create the key/value pairs used to initialize the dictionary. This has the effect of assigning specific numeric values to the strings, in the same order you want the strings to be sorted.
Then the dataGridView1_SortCompare() method is declared, with the implementation to simply delegate the comparison to the comparison of the numeric values assigned to the strings. I.e. the numeric value of the string representation of the first cell is retrieved from _sortOrder, and this value is compared to the numeric value, also retrieved from _sortOrder, of the string representation of the second cell.

How to access a group of Text Boxes based on the Index Id in their Name

I have 16 text boxes in my Form whose names are suffixed sequentially from 1 to 16 respectively.
i.e. The 16 test boxes are names TextBox1, 'TextBox2, .... all the way until the 16th one, which is namedTextBox16`.
I would like to read the contents of these 16 text boxes in a loop and modify the ith TextBox's contents or properties based on a certain condition.
How do I do this?
If you use WinForms, easiest way is to store text boxes references in array, in constructor of window:
TextBox[] data = new TextBox[16]{textBox1,textBox2, [...],textBox16};
then you can use for loop to access them.
You can try something like this:
Dictionary<string, string> yourValues = new Dictionary<string, string>();
foreach (Control x in this.Controls)
{
if (x is TextBox)
{
yourValues.Add(((TextBox)x).Name, ((TextBox)x).Text);
}
}
NOTE: On your future question please provide more information and make your question more clear.
i would try to find and modify using Linq:
using System.Linq;
//...
int x = 5; //number of textbox to search
var textbox = this.Controls.OfType<TextBox>().Single(tb => tb.Name.EndsWith(x.ToString()));
textbox.Text = "abc";
In case you have to loop thru all the texboxes in form, you can do something like this:
List<TextBox> textboxes = this.Controls.OfType<TextBox>().ToList();
foreach (TextBox box in textboxes)
{
box.Text = "something";
}
Easiest way according to what you specified is to use Linq.
Let's assume you have 3 TextBoxes :
// 1st -> which is named meTextBox1
// 2nd -> which is named meTextBox2
// 3rd -> which is named meTextBox3
As you can see from above every line differs only by the number ( index .. call it whatever you want ).
Now you can make your base "query" which would look like :
const string TB_NAME = "meTextBox{0}";
And as you can presume right now this will be used inside of string.Format method. Now to retrieve desired TextBox all you have to do is to make Linq statement :
string boxName = string.Format(TB_NAME, 7); // retrieve 7th text box
TextBox tBox = Controls.OfType<TextBox>().FirstOrDefault(tb => tb.Name == boxName);
This example does not consider nested Controls but you can make do this recursively which will retrieve nested Controls:
TextBox ByIndex(int idx, Control parent)
{
TextBox result = null;
string searchFor = string.Format(TB_NAME, idx);
foreach(Control ctrl in parent.Controls)
{
if(!(ctrl is TextBox) && ctrl.HasChildren)
{
result = ByIndex(idx, ctrl);
if( result != null)
break;
}
else if(ctrl is TextBox)
{
if(ctrl.Name = searchFor)
{
result = ctrl as TextBox;
break;
}
}
}
return result;
}
To use above method you can just call it like such :
public class MeForm : Form
{
//.. your code here ..
void meImaginaryMethodToRetrieveTextBox()
{
int meRandomIndex = 7;
TextBox tb = ByIndex(meRandomIndex, this);
// rest of the code...
}
}

Generate unique list variable

I have a C# program where I have a list (List<string>) of unique strings. These strings represent the name of different cases. It is not important what is is. But they have to be unique.
cases = new List<string> { "case1", "case3", "case4" }
Sometimes I read some cases saved in a text format into my program. Sometime the a case stored on file have the same name as a case in my program.I have to rename this new case. Lets say that the name of the case I load from a file is case1.
But the trouble is. How to rename this without adding a large random string. In my case it should ideally be called case2, I do not find any good algorithm which can do that. I want to find the smalles number I can add which make it unique.
i would use a HashSet that only accepts unique values.
List<string> cases = new List<string>() { "case1", "case3", "case4" };
HashSet<string> hcases = new HashSet<string>(cases);
string Result = Enumerable.Range(1, 100).Select(x => "case" + x).First(x => hcases.Add(x));
// Result is "case2"
in this sample i try to add elements between 1 and 100 to the hashset and determine the first sucessfully Add()
If you have a list of unique strings consider to use a HashSet<string> instead. Since you want incrementing numbers that sounds as if you actually should use a custom class instead of a string. One that contains a name and a number property. Then you can increment the number and if you want the full name (or override ToString) use Name + Number.
Lets say that class is Case you could fill a HashSet<Case>. HashSet.Add returns false on duplicates. Then use a loop which increments the number until it could be added.
Something like this:
var cases = new HashSet<Case>();
// fill it ...
// later you want to add one from file:
while(!cases.Add(caseFromFile))
{
// you will get here if the set already contained one with this name+number
caseFromFile.Number++;
}
A possible implementation:
public class Case
{
public string Name { get; set; }
public int Number { get; set; }
// other properties
public override string ToString()
{
return Name + Number;
}
public override bool Equals(object obj)
{
Case other = obj as Case;
if (other == null) return false;
return other.ToString() == this.ToString();
}
public override int GetHashCode()
{
return (ToString() ?? "").GetHashCode();
}
// other methods
}
The solution is quite simple. Get the max number of case currently stored in the list, increment by one and add the new value:
var max = myList.Max(x => Convert.ToInt32(x.Substring("case".Length))) + 1;
myList.Add("case" + max);
Working fiddle.
EDIT: For filling any "holes" within your collection you may use this:
var tmp = myList;
var firstIndex = Convert.ToInt32(myList[0].Substring("case".Length));
for(int i = firstIndex; i < tmp.Count; i++) {
var curIndex = Convert.ToInt32(myList[i].Substring("case".Length));
if (curIndex != i)
{
myList.Add("case" + (curIndex + 1));
break;
}
}
It checks for every element in your list if its number behind the case is equal to its index in the list. The loop is stopped at the very first element where the condition is broken and therefor you have a hole in the list.

Concatenate variable in variable name

I have a list of variable in ASP.NET (C#) that I'm trying to loop through without writing dozens of If statements. What I was hoping to do is just reference each of them with a variable and append it to the variable name. So something like the following:
string _id1 = Id1CheckBox.Checke.ToString();
string _id2 = Id2CheckBox.Checke.ToString();
string _id3 = Id3CheckBox.Checke.ToString();
int x = 1;
while (x < 20)
{
if ("_id&" + x == "True")
{
_id + x = x.ToString();
}
else
{
_id + x = 0;
}
x++;
}
The logic behind what I'm trying to do is I have several checkboxes on a form, Id1CheckBox, Id2CheckBox, Id3CheckBox, etc. I need get these values as strings so I can pass them as parameters in a SQL query. So basically If Id1CheckBox = true then _id1 = 1. If Id2CheckBox = true then _id2 = 2. If any are false then I would want something like If Id3CheckBox = false then _id3 = 0. Is this possible or will I just need to write several If statements instead of using a loop?
You can put the checkboxes into an array like this:
private bool[] _checked;
public Form1() // Constructor
{
InitializeComponent();
_checked = Controls
.OfType<CheckBox>()
.Where(cb => cb.Name.StartsWith("Id"))
.OrderBy(cb => cb.Name)
.Select(cb => cb.Checked)
.ToArray();
}
Then loop the array like this:
for (int i = 0; i < _checked.Length; i++) {
if (_checked[i]) {
...
} else {
...
}
}
Note: An array is a list of numbered variables (so to say). We say that the array is indexed. The index range goes from 0 to the length of the array - 1.
If you have 20 checkboxes add leading zeroes to the names up to number 9. E.g. "Id05CheckBox". Otherwise the checkboxes won't be sorted right.
Note also that the form's Controls collection contains only the controls that are directly on the form. If the ckeckboxes are on a panel or tab-page or the like, then loop through the Controls collection of this container control: panel1.Controls or page1.Controls - you get it.
you could use the loop and do something like this inside of your loop
CheckBox checkbox = (CheckBox )this.FindControl("_id" + x.toString());
if(checkbox.isChecked)
//do something here
Then you can just read the value from there.
Answer came from here
I ended up going with a Dictionary and adding parameters in a foreach loop instead of attempting to combine variables. Heres the code:
Dictionary<string, string> dictionary = new Dictionary<string, string>();
dictionary.Add("ID1", _id1);
dictionary.Add("ID2", _id2);
dictionary.Add("ID3", _id3);
dictionary.Add("ID4", _id4);
int i = 1;
foreach (var pair in dictionary)
{
if (pair.Value == "True")
{
cmd.Parameters.AddWithValue(pair.Key, i.ToString());
}
else
{
cmd.Parameters.AddWithValue(pair.Key, "0");
}
i++;
}

ListViewGroups not working correctly

I'm working with a ListView control in Windows Forms using C# and .NET 3.5. I have several items that I'm adding to this ListView control, and I have functionality in place that is supposed to allow the user to select different ways to group the items in the list. However, sometimes items get dumped into the automatically generated "Default" group for no explicable reason.
The code is too large to just put here, but let me explain what I'm doing algorithmically. Suppose we're starting with a ListView that contains items and may or may not already contain groups.
Cycle through every item and set
it's Group property to null.
Empty the ListView's Groups
collection.
Add new Groups to the ListView's
Group collection.
Cycle through every item and set the
Group property using a value
obtained from the ListView's Group
collection via index.
I've stepped through the code and observed everything as it should be. With each item it obtains the appropriate group from the ListView's Group collection and sets the Group property of the item, yet sometimes they end up listed under the "Default" group.
Has anyone else ever observed this or have any theories as to why it might be happening?
It doesn't sound familiar, and I can't reproduce (see below, which follows your approach). Can you post anything related to your "update the groups" code?
using System;
using System.Windows.Forms;
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
ListView lv;
Button btn;
Form form = new Form {
Controls = {
(lv = new ListView { Dock = DockStyle.Fill,
ShowGroups = true}),
(btn = new Button { Dock = DockStyle.Bottom,
Text = "Scramblle" })
}
};
Random rand = new Random();
for (int i = 0; i < 40; i++) {
lv.Items.Add("Item " + i);
}
btn.Click += delegate {
// 1: Cycle through every item and set it's Group
// property to null.
foreach (ListViewItem item in lv.Items) {
item.Group = null;
}
// 2: Empty the ListView's Groups collection.
lv.Groups.Clear();
// 3: Add new Groups to the ListView's Group collection.
int groupCount = rand.Next(lv.Items.Count) + 1;
for (int i = 0; i < groupCount; i++) {
lv.Groups.Add("grp" + i, "Group " + i);
}
// 4: Cycle through every item and set the Group property
// using a value obtained from the ListView's Group collection
// via index.
foreach (ListViewItem item in lv.Items) {
item.Group = lv.Groups[rand.Next(groupCount)];
}
};
Application.Run(form);
}
}
Is this happening on multiple threads?
It sounds like you might be adding some ListViewItems with Groups taken before the the groups were cleared.
Yes... It happened to me too. Try to set the Group before you add the item. I mean when you initialize the ListViewItem you add to the constructor the group it takes part of. That way is going to work.
Yes, I have seen similar behavior. The solution I followed is based on the code here.
public partial class ListViewExtended : ListView
{
private Collection<Dictionary<string, ListViewGroup>> groupTables = new Collection<Dictionary<string,ListViewGroup>>();
public ListViewExtended()
{
InitializeComponent();
}
/// <summary>
/// Create groups for each column of the list view.
/// </summary>
public void CreateGroups()
{
CreateGroups(false);
}
/// <summary>
/// Create groups for each column of the list view.
/// </summary>
public void CreateGroups(bool reset)
{
if (OSFeature.Feature.IsPresent(OSFeature.Themes))
{
if (reset)
{
this.groupTables.Clear();
}
for (int column = 0; column < this.Columns.Count; column++)
{
Dictionary<string, ListViewGroup> groups = new Dictionary<string, ListViewGroup>();
foreach (ListViewItem item in this.Items)
{
string subItemText = item.SubItems[column].Text;
// Use the initial letter instead if it is the first column.
if (column == 0)
{
subItemText = subItemText.Substring(0, 1).ToUpperInvariant();
}
if (!groups.ContainsKey(subItemText))
{
groups.Add(subItemText, new ListViewGroup(subItemText) { Name = subItemText });
}
}
this.groupTables.Add(groups);
}
}
}
/// <summary>
/// Sets the list view to the groups created for the specified column.
/// </summary>
/// <param name="column"></param>
public void SetGroups(int column)
{
if (OSFeature.Feature.IsPresent(OSFeature.Themes))
{
try
{
this.BeginUpdate();
this.Groups.Clear();
if (column == -1)
{
this.ShowGroups = false;
}
else
{
this.ShowGroups = true;
Dictionary<string, ListViewGroup> groups = groupTables[column];
this.Groups.AddRange(groups.Values.OrderBy(g => g.Name).ToArray());
foreach (ListViewItem item in this.Items)
{
string subItemText = item.SubItems[column].Text;
// For the Title column, use only the first letter.
if (column == 0)
{
subItemText = subItemText.Substring(0, 1).ToUpperInvariant();
}
item.Group = groups[subItemText];
}
groups.Values.ForEach<ListViewGroup>(g => g.Header = String.Format(CultureInfo.CurrentUICulture, "{0} ({1})", g.Name, g.Items.Count));
}
}
finally
{
this.EndUpdate();
}
}
}
}
In the code that actually adds the items to the listview, you want to do something like this:
this.itemsListView.Items.AddRange(items.ToArray());
this.itemsListView.CreateGroups(true);
this.itemsListView.SetGroups(0); // Group by the first column by default.

Categories