“Repeated” data displayed in datatable - c#

I want to display my List<string> in a datatable, from another form. The data from the List<string> is from textbox and combobox. However, the data from textbox is never repeated but the combobox might be similar with previous data displayed. And if this happened, the data displayed in datatable will be “repeated” (I’m unsure on how to describe it). Here is the actual outcome. I would like to see my displayed data to be like this. Below is my code:
Transavestate - class that hold my List
public static List<string> transnumber_list = new List<string>();
public static List<string> combos_list = new List<string>();
Form1 - form that user input values of textbox and combobox
private void button1_Click(object sender, EventArgs e)
{
//Save values in the List
Transavestate.transnumber_list.Add(Textbox1.Text)
Transavestate.combos_list.Add(comboBox2.SelectedItem.ToString());
//Go to Form 2
this.Hide();
Form2 f2 = new Form2 ();
f2.Show();
}
Form2 - form to display values of textbox and combobox
private DataSet ds;
private DataTable dt;
//Method to insert data into dtg1
private void CreateDataSet()
{
ds = new DataSet();
dt = new DataTable("Vehicle Number");
dt.Columns.Add("Column 1", typeof(string));
dt.Columns.Add("Column 2", typeof(string));
foreach (var item in Transavestate.transnumber_list)
{
foreach (var items in Transavestate.combos_list)
{
dt.Rows.Add(item, items);
}
}
ds.Tables.Add(dt);
this.dataGridView1.DataSource = dt;
dataGridView1.AllowUserToAddRows = false;
}
//To run the method
private void dataGridView1_VisibleChanged(object sender, EventArgs e)
{
CreateDataSet();
}
//Go back to Form1
private void button2_Click(object sender, EventArgs e)
{
this.Hide();
Form1 f1 = new Form1();
f1.Show();
}

It seems to me that you have two sequences of strings: a sequence of transNumbers and a sequence of comboItems (in fact they are your transnumber_list and your combos_list, but I don't want to limit myself to the fact that they are Lists, and the names are inconsistent (the first is non-plural, the 2nd is plural?)
Anyway, you say that your sequence of transNumbers contains only unique items, but that your comboItems might contain duplicates (based on some string equality comparer)
Something like this:
IEnumerable<string> transNumbers = new string[] {"1", "2"};
IEnumerable<string> comboItems = new string[] {"A", "A", "A", "B", "A", "B"};
As a result you want something like:
TransNumber ComboItem
"1" "A"
"1" "B"
"2" "A"
"2" "B"
So you want to combine every TransNumber, with every unique comboItem.The order seems not to be important.
With LINQ This is fairly easy. We'll use Enumerable.SelectMany to combine the two sequences and we'll use Enumerable.Distinct to get rid of your duplicates
// SelectMany:
// parameter source: transNumbers
// parameter collectionSelector: comboItems without Duplicates
var transNumberCombiItemCombinations = transNumbers.SelectMany(
// collectionSelector: comboItems without duplicates
combiItems.Distinct(),
// ResultSelector, take a transNumber from the source,
// and a comboItem from collectionSelector to make a new object
(transNumber, comboItem => new
{
Column1 = transNumber,
Column2 = comboItem,
});
If you remove all comments, you'll see that it is really a small piece of code.
Distinct will remove the duplicates. If you consider "myname" and "MYNAME" to be equal, you'll need to provide an IEqualityComparer<string>, for instance StringComparer.OrdinalIgnoreCase.
SelectMany" will do your foreach inside foreach.
The resultSelector will take one transNumber and one comboItem as input to create the output you want. In this case, one object that has two properties: Column1 and Column2.
The result is an IEnumerable. All you have to do is enumerate it into a list or something and add the result to your rows collection:
dt.Rows.AddRange(transNumberCombiItemCombinations.ToList());

Related

Why does dataRow always contains the first row

In my project I have to monitor the status of a decentralized periphery. For this I created a class that mirrors a single data point with name, address etc. Now I want to insert all my single objects of the class DataPoint into a SortedList . The DataPoint objects should be initialized via a DataTable. My problem is that in my Foreach loop I always have the first two of the DataTable. So that I create a SortedList with 39 times the same entry in this example. If the classes DataPoint and DpPeriphyData Class contain the SortedList, the program runs without problems.
private void Cmd_Fbd_Data_List_Click(object sender, EventArgs e)
{
ImportExportExcelToDataGridClass excel = new ImportExportExcelToDataGridClass();
DataTable dataPointList = excel.ImportExceltoDataGrid();
Dgv_Data_List.DataSource = dataPointList;
Dgv_Data_List.ReadOnly = true;
DpPeripheryData InputData = new DpPeripheryData();
foreach (DataRow dataRow in dataPointList.Rows)
{
DataPoint dataPoint = new DataPoint(dataRow); // making a new objekt
InputData.AddDataPoint(dataPoint); // add this class to the Sorted List
}
}

Populate Combo Box with only results that start with selected items first character from another combo box

I have two combo boxes comboBox1 & comboBox2. comboBox1 is populated like so:
BindingSource comboBox1Bs = new BindingSource();
comboBox1Bs.DataSource = new List<string> { "Apple","Amber","Book","Bean","Cat","Cook"};
comboBox1.DataSource = comboBox1Bs;
Combo Box Two is like so:
BindingSource comboBox2Bs = new BindingSource();
comboBox2Bs.DataSource = new List<string> { "Aresult","Bresult","Cresult"};
comboBox2.DataSource = comboBox2Bs;
When a selection from combo box 1 is made I only want results starting with the same letter as the selected to show in combo box 2. For example if Apple is selected only Aresult will show (but if there were more results being with A they would also show).
To get the starting letter from comboBox1 I have the following:
var prefix = comboBox1.SelectedItem.ToString().Substring(0, 1);
But what can I add so combox2 will only show results that start with the same as the contents of prefix
make the collection in the list so that you can filter it:
List<string> colComboBox_2 = new List<string> { "Aresult","Bresult","Cresult"};
then you can filter it when you have your prefix:
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
var prefix = comboBox1.SelectedItem.ToString().Substring(0, 1);
comboBox2.DataSource = colComboBox_2.Where(x => x.StartsWith(prefix)).ToList();
}
You can use LINQ to achieve that.
List<string> foo = ((List<string>)comboBox2Bs.DataSource).Where(x => x.().Substring(0, 1).StartsWith(prefix));
comboBox2.DataSource = foo;
The following example should be even better
List<string> foo = ((List<string>)comboBox2Bs.DataSource).Where(x => x.ToString().StartsWith(prefix));
comboBox2.DataSource = foo;

How to use partial search through an array and output the results?

I have an array with country names in a listBox. When I enter the textBox, I want any country that starts with what's in the textBox to display.
So if I enter : B => Brazil
Not like this: A => Argentina, England
Only if it starts with what's in the textBox. Full words would also work.
The arraylist contains more than just names, but the code below extracts just the names. List2 is the arraylist I want to use for the search.
private void textBox7_TextChanged(object sender, EventArgs e)
{
listBox1.ClearSelected();
listBox1.DataSource = null;
foreach (Country name2 in Mytree.List)
{
List2.Add(name2.name);
Console.WriteLine(List2);
}
}
If your objective is to avoid typing the full country name then there is no need to reinvent a new kind of user interface. The TextBox has already all the plumbing available to do what you are trying to reproduce with your code. All you need is a source of your data and the settings a pair of properties
// Create the list to use as the custom source.
var source = new AutoCompleteStringCollection();
source.AddRange(new string[]
{
"Argentina",
"England",
"Brazil",
"Italy",
"..."
});
textBox1.AutoCompleteCustomSource = source;
textBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
Not quite sure but you will want to look at setting the datasource of the listbox to something like
listBox1.DataSource = Mytree.List.Where(a=>a.name.StartsWith(textBox7.Text)
Your example of using a foreach loop to add each item is a little redundant.
private void textBox7_TextChanged(object sender, EventArgs e)
{
listBox1.ClearSelected();
listBox1.DataSource = null;
var matchingCountries = Mytree.List.Where(l=>l.name.StartsWith(textBox7.Text));
foreach (Country name2 in matchingCountries)
{
listBox1.Items.Add(name2.name);
}
}

Append clipboard text to a listbox

So I created a variable to hold my clipboard text and I have no idea on how to append it to a listbox.
This is as far as I got..
private void clipboardBtn_Click(object sender, EventArgs e)
{
string items = Clipboard.GetText();
List<string> _items = new List<string>();
_items.AddRange(items);
}
but that throws me this error..
Argument 1: cannot convert from 'string' to
'System.Collections.Generic.IEnumerable'
What's causing this and how do I fix it? Is this even the correct way of appending text to the listbox?
-UPDATE-
I got this now but everytime I click the button it overwrites the old one instead of appending a new item to the listbox
string items = Clipboard.GetText();
List<string> _items = new List<string>();
_items.Add(items);
listBox1.DataSource =_items;
How do i append a new item?
Clipboard.GetText has the signature
public static string GetText()
but List<T>.AddRange has the signature
public void AddRange( IEnumerable<T> collection )
So essentially you're trying to add a string as an IEnumerable<T> which gives you the above error.
Better use List<T>.Add for that purpose like that:
_items.Add(items);
your question is about List object and not about ListBox control.
the AddRange() method requires a collection, you can transform your string to a collection (Array) by using Split.
private void clipboardBtn_Click(object sender, EventArgs e)
{
string YourGetClipBoardTextString = "aaa;bbb;ccc;ddd";
List<string> _items = new List<string>();
_items.AddRange(YourGetClipBoardTextString.Split(';').ToArray()); // you can split the string by any char seperator ";" " ", "," etc...
}
if you dont need to split the string just use the Add() method:
_items.Add(YourGetClipBoardTextString);
After your update, you can append new items to a listbox in that manner:
foreach (string itm in _items)
{
listBox1.Items.Add(itm);
}
since you're creating new "_items" on every click, you cannot see the old items. try like this,
List<string> _items = new List<string>();
private void clipboardBtn_Click(object sender, EventArgs e)
{
string items = Clipboard.GetText();
_items.Add(items);
listBox1.DataSource =_items;
}
_items declared outside of method scope.
Your problem is that you are initializing a new list each time:
string items = Clipboard.GetText();
List<string> _items = new List<string>();//<New list here results in removal of existing item
_items.Add(items);
listBox1.DataSource =_items;
Try something like this:
string items = Clipboard.GetText();
List<string> _items = listBox1.DataSource as List<string>;// You may have type casting issues here -
_items.Add(items);
listBox1.DataSource =_items;
First you need to split the clipboard contents into strings for each line, then you need to add them to the list box:
string[] items = Clipboard.GetText().Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
listBox1.Items.AddRange(items);
If you don't want to have a separate listBox item for each line, just do this:
listBox1.Items.Add(Clipboard.GetText());

Problems with Reflection on winforms

I have a winforms application.
I have a Populate method that creates a bunch of controls on each page on my tabcontrol. The Populate method takes two arguments - the tab page and a List with the strings for a bunch of labels. There is a separate List for each tabpage and the names of the Lists are the same as the names of the tabpages. I want to iterate through the pages and pass the appropriate List to the Populate method by name, i.e. to pass the List by a string that is its name. As far as I know I need Reflection for that.
Code:
namespace Test
{
public partial class Form1 : Form
{
List<string> Hongdoe = new List<string>(new string[] { "Chin", "Foa", "Hu", "Dan" });
List<string> Donfu = new List<string>(new string[] { "Faa", "Su", "Pi", "Mou" });
//TabPage1.Name = Hongdoe
//TabPage2.Name = Donfu
foreach (TabPage tp in Tab_Control.TabPages)
{
//I want to tell the program "Find the variable/list that is named as 'tp.Name'
var ListName = typeof(Form1).GetField(tp.Name)
Populate(tp, ListName);
}
}
void Populate (TabPage tp, List<string> list)
{
for (int i = 0; i < list.Count; i++)
{
//Create labels
Label lab = new Label();
lab.Text = list[i];
lab.Location = new Point(i * 10, i * 10));
tp.Controls.Add(lab);
}
}
}
But sofar it returns null. I also tried using "GetProperty", "GetValue" but no success.
(before I edited this question I used a variable to demonstrate my problem simply)
You don't need to use reflection. You can use a Dictionary<string, List<string>> and use list names (tab page names) as keys and list of strings as values. Then you can get a list using the key from dictionary.
Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>();
private void ProductList_Load(object sender, EventArgs e)
{
//Initialize dictionary with keys and values
dictionary["page1"] = new List<string> { "string 1", "string 2" };
dictionary["page2"] = new List<string> { "string 3", "string 4" };
//...
}
Then you can call your Populate method this way:
Populate(tp, dictionary[tp.Name]);
Note
You don't need to pass list to the method and it's enough to pass TabPage to the method and you can get the list using dictionary[tabPage.Name]
You can use a TableLayoutPanel or a FlowLayoutPanel in tab pages to add labels to. This way they will be arranged automatically.
Just for learning purpose if you want to use reflection:
var list = (List<string>)this.GetType().GetField("Hongdoe",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance).GetValue(this);

Categories